mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-01-06 01:20:29 +05:30
Merge pull request #1055 from Bnyro/master
Migrate all stored data to Room database
This commit is contained in:
commit
ed51cf91a2
@ -4,6 +4,7 @@ plugins {
|
|||||||
id 'com.android.application'
|
id 'com.android.application'
|
||||||
id 'kotlin-android'
|
id 'kotlin-android'
|
||||||
id 'kotlin-android-extensions'
|
id 'kotlin-android-extensions'
|
||||||
|
id 'kotlin-kapt'
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@ -74,6 +75,8 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
//debugImplementation libs.square.leakcanary
|
//debugImplementation libs.square.leakcanary
|
||||||
|
|
||||||
|
kapt libs.room.compiler
|
||||||
|
|
||||||
implementation libs.androidx.appcompat
|
implementation libs.androidx.appcompat
|
||||||
implementation libs.androidx.constraintlayout
|
implementation libs.androidx.constraintlayout
|
||||||
implementation libs.androidx.legacySupport
|
implementation libs.androidx.legacySupport
|
||||||
@ -106,6 +109,8 @@ dependencies {
|
|||||||
implementation libs.lifecycle.viewmodel
|
implementation libs.lifecycle.viewmodel
|
||||||
implementation libs.lifecycle.runtime
|
implementation libs.lifecycle.runtime
|
||||||
implementation libs.lifecycle.livedata
|
implementation libs.lifecycle.livedata
|
||||||
|
|
||||||
|
implementation libs.room
|
||||||
}
|
}
|
||||||
|
|
||||||
static def getUnixTime() {
|
static def getUnixTime() {
|
||||||
|
@ -49,3 +49,8 @@ const val DOWNLOAD_SUCCESS_NOTIFICATION_ID = 5
|
|||||||
const val DOWNLOAD_CHANNEL_ID = "download_service"
|
const val DOWNLOAD_CHANNEL_ID = "download_service"
|
||||||
const val BACKGROUND_CHANNEL_ID = "background_mode"
|
const val BACKGROUND_CHANNEL_ID = "background_mode"
|
||||||
const val PUSH_CHANNEL_ID = "notification_worker"
|
const val PUSH_CHANNEL_ID = "notification_worker"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database
|
||||||
|
*/
|
||||||
|
const val DATABASE_NAME = "LibreTubeDatabase"
|
||||||
|
@ -8,6 +8,7 @@ import android.os.Build
|
|||||||
import android.os.StrictMode
|
import android.os.StrictMode
|
||||||
import android.os.StrictMode.VmPolicy
|
import android.os.StrictMode.VmPolicy
|
||||||
import androidx.work.ExistingPeriodicWorkPolicy
|
import androidx.work.ExistingPeriodicWorkPolicy
|
||||||
|
import com.github.libretube.database.DatabaseHolder
|
||||||
import com.github.libretube.preferences.PreferenceHelper
|
import com.github.libretube.preferences.PreferenceHelper
|
||||||
import com.github.libretube.preferences.PreferenceKeys
|
import com.github.libretube.preferences.PreferenceKeys
|
||||||
import com.github.libretube.util.ExceptionHandler
|
import com.github.libretube.util.ExceptionHandler
|
||||||
@ -19,15 +20,20 @@ class MyApp : Application() {
|
|||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* initialize the needed [NotificationChannel]s for DownloadService and BackgroundMode
|
* Initialize the needed [NotificationChannel]s for DownloadService and BackgroundMode
|
||||||
*/
|
*/
|
||||||
initializeNotificationChannels()
|
initializeNotificationChannels()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set the applicationContext as context for the [PreferenceHelper]
|
* Set the applicationContext as context for the [PreferenceHelper]
|
||||||
*/
|
*/
|
||||||
PreferenceHelper.setContext(applicationContext)
|
PreferenceHelper.setContext(applicationContext)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the [DatabaseHolder]
|
||||||
|
*/
|
||||||
|
DatabaseHolder.initializeDatabase(this)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bypassing fileUriExposedException, see https://stackoverflow.com/questions/38200282/android-os-fileuriexposedexception-file-storage-emulated-0-test-txt-exposed
|
* bypassing fileUriExposedException, see https://stackoverflow.com/questions/38200282/android-os-fileuriexposedexception-file-storage-emulated-0-test-txt-exposed
|
||||||
*/
|
*/
|
||||||
@ -35,12 +41,12 @@ class MyApp : Application() {
|
|||||||
StrictMode.setVmPolicy(builder.build())
|
StrictMode.setVmPolicy(builder.build())
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set the api and the auth api url
|
* Set the api and the auth api url
|
||||||
*/
|
*/
|
||||||
setRetrofitApiUrls()
|
setRetrofitApiUrls()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* initialize the notification listener in the background
|
* Initialize the notification listener in the background
|
||||||
*/
|
*/
|
||||||
NotificationHelper.enqueueWork(this, ExistingPeriodicWorkPolicy.KEEP)
|
NotificationHelper.enqueueWork(this, ExistingPeriodicWorkPolicy.KEEP)
|
||||||
|
|
||||||
@ -52,13 +58,13 @@ class MyApp : Application() {
|
|||||||
Thread.setDefaultUncaughtExceptionHandler(exceptionHandler)
|
Thread.setDefaultUncaughtExceptionHandler(exceptionHandler)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* legacy preference file migration
|
* Legacy preference file migration
|
||||||
*/
|
*/
|
||||||
prefFileMigration()
|
prefFileMigration()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set the api urls needed for the [RetrofitInstance]
|
* Set the api urls needed for the [RetrofitInstance]
|
||||||
*/
|
*/
|
||||||
private fun setRetrofitApiUrls() {
|
private fun setRetrofitApiUrls() {
|
||||||
RetrofitInstance.url =
|
RetrofitInstance.url =
|
||||||
|
@ -89,77 +89,79 @@ class MainActivity : BaseActivity() {
|
|||||||
if (!ConnectionHelper.isNetworkAvailable(this)) {
|
if (!ConnectionHelper.isNetworkAvailable(this)) {
|
||||||
val noInternetIntent = Intent(this, NoInternetActivity::class.java)
|
val noInternetIntent = Intent(this, NoInternetActivity::class.java)
|
||||||
startActivity(noInternetIntent)
|
startActivity(noInternetIntent)
|
||||||
} else {
|
finish()
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
return
|
||||||
setContentView(binding.root)
|
|
||||||
|
|
||||||
// set the action bar for the activity
|
|
||||||
setSupportActionBar(binding.toolbar)
|
|
||||||
|
|
||||||
navController = findNavController(R.id.fragment)
|
|
||||||
binding.bottomNav.setupWithNavController(navController)
|
|
||||||
|
|
||||||
// gets the surface color of the bottom navigation view
|
|
||||||
val color = SurfaceColors.getColorForElevation(this, 10F)
|
|
||||||
|
|
||||||
// sets the navigation bar color to the previously calculated color
|
|
||||||
window.navigationBarColor = color
|
|
||||||
|
|
||||||
// hide the trending page if enabled
|
|
||||||
val hideTrendingPage =
|
|
||||||
PreferenceHelper.getBoolean(PreferenceKeys.HIDE_TRENDING_PAGE, false)
|
|
||||||
if (hideTrendingPage) binding.bottomNav.menu.findItem(R.id.homeFragment).isVisible =
|
|
||||||
false
|
|
||||||
|
|
||||||
// save start tab fragment id
|
|
||||||
startFragmentId =
|
|
||||||
when (PreferenceHelper.getString(PreferenceKeys.DEFAULT_TAB, "home")) {
|
|
||||||
"home" -> R.id.homeFragment
|
|
||||||
"subscriptions" -> R.id.subscriptionsFragment
|
|
||||||
"library" -> R.id.libraryFragment
|
|
||||||
else -> R.id.homeFragment
|
|
||||||
}
|
|
||||||
|
|
||||||
// set default tab as start fragment
|
|
||||||
navController.graph.setStartDestination(startFragmentId)
|
|
||||||
|
|
||||||
// navigate to the default fragment
|
|
||||||
navController.navigate(startFragmentId)
|
|
||||||
|
|
||||||
val labelVisibilityMode = when (
|
|
||||||
PreferenceHelper.getString(PreferenceKeys.LABEL_VISIBILITY, "always")
|
|
||||||
) {
|
|
||||||
"always" -> NavigationBarView.LABEL_VISIBILITY_LABELED
|
|
||||||
"selected" -> NavigationBarView.LABEL_VISIBILITY_SELECTED
|
|
||||||
"never" -> NavigationBarView.LABEL_VISIBILITY_UNLABELED
|
|
||||||
else -> NavigationBarView.LABEL_VISIBILITY_AUTO
|
|
||||||
}
|
|
||||||
binding.bottomNav.labelVisibilityMode = labelVisibilityMode
|
|
||||||
|
|
||||||
binding.bottomNav.setOnApplyWindowInsetsListener(null)
|
|
||||||
|
|
||||||
binding.bottomNav.setOnItemSelectedListener {
|
|
||||||
// clear backstack if it's the start fragment
|
|
||||||
if (startFragmentId == it.itemId) navController.backQueue.clear()
|
|
||||||
// set menu item on click listeners
|
|
||||||
removeSearchFocus()
|
|
||||||
when (it.itemId) {
|
|
||||||
R.id.homeFragment -> {
|
|
||||||
navController.navigate(R.id.homeFragment)
|
|
||||||
}
|
|
||||||
R.id.subscriptionsFragment -> {
|
|
||||||
navController.navigate(R.id.subscriptionsFragment)
|
|
||||||
}
|
|
||||||
R.id.libraryFragment -> {
|
|
||||||
navController.navigate(R.id.libraryFragment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.toolbar.title = ThemeHelper.getStyledAppName(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
// set the action bar for the activity
|
||||||
|
setSupportActionBar(binding.toolbar)
|
||||||
|
|
||||||
|
navController = findNavController(R.id.fragment)
|
||||||
|
binding.bottomNav.setupWithNavController(navController)
|
||||||
|
|
||||||
|
// gets the surface color of the bottom navigation view
|
||||||
|
val color = SurfaceColors.getColorForElevation(this, 10F)
|
||||||
|
|
||||||
|
// sets the navigation bar color to the previously calculated color
|
||||||
|
window.navigationBarColor = color
|
||||||
|
|
||||||
|
// hide the trending page if enabled
|
||||||
|
val hideTrendingPage =
|
||||||
|
PreferenceHelper.getBoolean(PreferenceKeys.HIDE_TRENDING_PAGE, false)
|
||||||
|
if (hideTrendingPage) binding.bottomNav.menu.findItem(R.id.homeFragment).isVisible =
|
||||||
|
false
|
||||||
|
|
||||||
|
// save start tab fragment id
|
||||||
|
startFragmentId =
|
||||||
|
when (PreferenceHelper.getString(PreferenceKeys.DEFAULT_TAB, "home")) {
|
||||||
|
"home" -> R.id.homeFragment
|
||||||
|
"subscriptions" -> R.id.subscriptionsFragment
|
||||||
|
"library" -> R.id.libraryFragment
|
||||||
|
else -> R.id.homeFragment
|
||||||
|
}
|
||||||
|
|
||||||
|
// set default tab as start fragment
|
||||||
|
navController.graph.setStartDestination(startFragmentId)
|
||||||
|
|
||||||
|
// navigate to the default fragment
|
||||||
|
navController.navigate(startFragmentId)
|
||||||
|
|
||||||
|
val labelVisibilityMode = when (
|
||||||
|
PreferenceHelper.getString(PreferenceKeys.LABEL_VISIBILITY, "always")
|
||||||
|
) {
|
||||||
|
"always" -> NavigationBarView.LABEL_VISIBILITY_LABELED
|
||||||
|
"selected" -> NavigationBarView.LABEL_VISIBILITY_SELECTED
|
||||||
|
"never" -> NavigationBarView.LABEL_VISIBILITY_UNLABELED
|
||||||
|
else -> NavigationBarView.LABEL_VISIBILITY_AUTO
|
||||||
|
}
|
||||||
|
binding.bottomNav.labelVisibilityMode = labelVisibilityMode
|
||||||
|
|
||||||
|
binding.bottomNav.setOnApplyWindowInsetsListener(null)
|
||||||
|
|
||||||
|
binding.bottomNav.setOnItemSelectedListener {
|
||||||
|
// clear backstack if it's the start fragment
|
||||||
|
if (startFragmentId == it.itemId) navController.backQueue.clear()
|
||||||
|
// set menu item on click listeners
|
||||||
|
removeSearchFocus()
|
||||||
|
when (it.itemId) {
|
||||||
|
R.id.homeFragment -> {
|
||||||
|
navController.navigate(R.id.homeFragment)
|
||||||
|
}
|
||||||
|
R.id.subscriptionsFragment -> {
|
||||||
|
navController.navigate(R.id.subscriptionsFragment)
|
||||||
|
}
|
||||||
|
R.id.libraryFragment -> {
|
||||||
|
navController.navigate(R.id.libraryFragment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.toolbar.title = ThemeHelper.getStyledAppName(this)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* handle error logs
|
* handle error logs
|
||||||
*/
|
*/
|
||||||
|
@ -4,8 +4,9 @@ import android.view.LayoutInflater
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.github.libretube.database.DatabaseHolder
|
||||||
import com.github.libretube.databinding.SearchhistoryRowBinding
|
import com.github.libretube.databinding.SearchhistoryRowBinding
|
||||||
import com.github.libretube.preferences.PreferenceHelper
|
import com.github.libretube.obj.SearchHistoryItem
|
||||||
|
|
||||||
class SearchHistoryAdapter(
|
class SearchHistoryAdapter(
|
||||||
private var historyList: List<String>,
|
private var historyList: List<String>,
|
||||||
@ -29,8 +30,12 @@ class SearchHistoryAdapter(
|
|||||||
historyText.text = historyQuery
|
historyText.text = historyQuery
|
||||||
|
|
||||||
deleteHistory.setOnClickListener {
|
deleteHistory.setOnClickListener {
|
||||||
historyList = historyList - historyQuery
|
historyList -= historyQuery
|
||||||
PreferenceHelper.removeFromSearchHistory(historyQuery)
|
Thread {
|
||||||
|
DatabaseHolder.database.searchHistoryDao().delete(
|
||||||
|
SearchHistoryItem(query = historyQuery)
|
||||||
|
)
|
||||||
|
}.start()
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,11 +4,11 @@ import android.view.LayoutInflater
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.fragment.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.github.libretube.database.DatabaseHelper
|
||||||
import com.github.libretube.databinding.WatchHistoryRowBinding
|
import com.github.libretube.databinding.WatchHistoryRowBinding
|
||||||
import com.github.libretube.dialogs.VideoOptionsDialog
|
import com.github.libretube.dialogs.VideoOptionsDialog
|
||||||
import com.github.libretube.extensions.setFormattedDuration
|
import com.github.libretube.extensions.setFormattedDuration
|
||||||
import com.github.libretube.obj.WatchHistoryItem
|
import com.github.libretube.obj.WatchHistoryItem
|
||||||
import com.github.libretube.preferences.PreferenceHelper
|
|
||||||
import com.github.libretube.util.ConnectionHelper
|
import com.github.libretube.util.ConnectionHelper
|
||||||
import com.github.libretube.util.NavigationHelper
|
import com.github.libretube.util.NavigationHelper
|
||||||
import com.github.libretube.util.setWatchProgressLength
|
import com.github.libretube.util.setWatchProgressLength
|
||||||
@ -21,7 +21,7 @@ class WatchHistoryAdapter(
|
|||||||
private val TAG = "WatchHistoryAdapter"
|
private val TAG = "WatchHistoryAdapter"
|
||||||
|
|
||||||
fun removeFromWatchHistory(position: Int) {
|
fun removeFromWatchHistory(position: Int) {
|
||||||
PreferenceHelper.removeFromWatchHistory(position)
|
DatabaseHelper.removeFromWatchHistory(position)
|
||||||
watchHistory.removeAt(position)
|
watchHistory.removeAt(position)
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
@ -54,12 +54,12 @@ class WatchHistoryAdapter(
|
|||||||
NavigationHelper.navigateVideo(root.context, video.videoId)
|
NavigationHelper.navigateVideo(root.context, video.videoId)
|
||||||
}
|
}
|
||||||
root.setOnLongClickListener {
|
root.setOnLongClickListener {
|
||||||
VideoOptionsDialog(video.videoId!!)
|
VideoOptionsDialog(video.videoId)
|
||||||
.show(childFragmentManager, VideoOptionsDialog::class.java.name)
|
.show(childFragmentManager, VideoOptionsDialog::class.java.name)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
watchProgress.setWatchProgressLength(video.videoId!!, video.duration)
|
watchProgress.setWatchProgressLength(video.videoId, video.duration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.github.libretube.database
|
||||||
|
|
||||||
|
import androidx.room.Database
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
import com.github.libretube.obj.CustomInstance
|
||||||
|
import com.github.libretube.obj.SearchHistoryItem
|
||||||
|
import com.github.libretube.obj.WatchHistoryItem
|
||||||
|
import com.github.libretube.obj.WatchPosition
|
||||||
|
|
||||||
|
@Database(
|
||||||
|
entities = [
|
||||||
|
WatchHistoryItem::class,
|
||||||
|
WatchPosition::class,
|
||||||
|
SearchHistoryItem::class,
|
||||||
|
CustomInstance::class
|
||||||
|
],
|
||||||
|
version = 6
|
||||||
|
)
|
||||||
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
/**
|
||||||
|
* Watch History
|
||||||
|
*/
|
||||||
|
abstract fun watchHistoryDao(): WatchHistoryDao
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Watch Positions
|
||||||
|
*/
|
||||||
|
abstract fun watchPositionDao(): WatchPositionDao
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search History
|
||||||
|
*/
|
||||||
|
abstract fun searchHistoryDao(): SearchHistoryDao
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom Instances
|
||||||
|
*/
|
||||||
|
abstract fun customInstanceDao(): CustomInstanceDao
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.github.libretube.database
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import com.github.libretube.obj.CustomInstance
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface CustomInstanceDao {
|
||||||
|
@Query("SELECT * FROM customInstance")
|
||||||
|
fun getAll(): List<CustomInstance>
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun insertAll(vararg customInstances: CustomInstance)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(customInstance: CustomInstance)
|
||||||
|
|
||||||
|
@Query("DELETE FROM customInstance")
|
||||||
|
fun deleteAll()
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
package com.github.libretube.database
|
||||||
|
|
||||||
|
import com.github.libretube.obj.Streams
|
||||||
|
import com.github.libretube.obj.WatchHistoryItem
|
||||||
|
import com.github.libretube.obj.WatchPosition
|
||||||
|
import com.github.libretube.preferences.PreferenceHelper
|
||||||
|
import com.github.libretube.preferences.PreferenceKeys
|
||||||
|
import com.github.libretube.util.toID
|
||||||
|
|
||||||
|
object DatabaseHelper {
|
||||||
|
fun addToWatchHistory(videoId: String, streams: Streams) {
|
||||||
|
val watchHistoryItem = WatchHistoryItem(
|
||||||
|
videoId,
|
||||||
|
streams.title,
|
||||||
|
streams.uploadDate,
|
||||||
|
streams.uploader,
|
||||||
|
streams.uploaderUrl.toID(),
|
||||||
|
streams.uploaderAvatar,
|
||||||
|
streams.thumbnailUrl,
|
||||||
|
streams.duration
|
||||||
|
)
|
||||||
|
Thread {
|
||||||
|
DatabaseHolder.database.watchHistoryDao().insertAll(watchHistoryItem)
|
||||||
|
val maxHistorySize = PreferenceHelper.getString(PreferenceKeys.WATCH_HISTORY_SIZE, "unlimited")
|
||||||
|
if (maxHistorySize == "unlimited") return@Thread
|
||||||
|
|
||||||
|
// delete the first watch history entry if the limit is reached
|
||||||
|
val watchHistory = DatabaseHolder.database.watchHistoryDao().getAll()
|
||||||
|
if (watchHistory.size > maxHistorySize.toInt()) {
|
||||||
|
DatabaseHolder.database.watchHistoryDao()
|
||||||
|
.delete(watchHistory.first())
|
||||||
|
}
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeFromWatchHistory(index: Int) {
|
||||||
|
Thread {
|
||||||
|
DatabaseHolder.database.watchHistoryDao().delete(
|
||||||
|
DatabaseHolder.database.watchHistoryDao().getAll()[index]
|
||||||
|
)
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveWatchPosition(videoId: String, position: Long) {
|
||||||
|
val watchPosition = WatchPosition(
|
||||||
|
videoId,
|
||||||
|
position
|
||||||
|
)
|
||||||
|
Thread {
|
||||||
|
DatabaseHolder.database.watchPositionDao().insertAll(watchPosition)
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeWatchPosition(videoId: String) {
|
||||||
|
Thread {
|
||||||
|
DatabaseHolder.database.watchPositionDao().delete(
|
||||||
|
DatabaseHolder.database.watchPositionDao().findById(videoId)
|
||||||
|
)
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package com.github.libretube.database
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.room.Room
|
||||||
|
import com.github.libretube.DATABASE_NAME
|
||||||
|
|
||||||
|
object DatabaseHolder {
|
||||||
|
lateinit var database: AppDatabase
|
||||||
|
|
||||||
|
fun initializeDatabase(context: Context) {
|
||||||
|
database = Room.databaseBuilder(
|
||||||
|
context,
|
||||||
|
AppDatabase::class.java,
|
||||||
|
DATABASE_NAME
|
||||||
|
)
|
||||||
|
.fallbackToDestructiveMigration()
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.github.libretube.database
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import com.github.libretube.obj.SearchHistoryItem
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface SearchHistoryDao {
|
||||||
|
@Query("SELECT * FROM searchHistoryItem")
|
||||||
|
fun getAll(): List<SearchHistoryItem>
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun insertAll(vararg searchHistoryItem: SearchHistoryItem)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(searchHistoryItem: SearchHistoryItem)
|
||||||
|
|
||||||
|
@Query("DELETE FROM searchHistoryItem")
|
||||||
|
fun deleteAll()
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.github.libretube.database
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import com.github.libretube.obj.WatchHistoryItem
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface WatchHistoryDao {
|
||||||
|
@Query("SELECT * FROM watchHistoryItem")
|
||||||
|
fun getAll(): List<WatchHistoryItem>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM watchHistoryItem WHERE videoId LIKE :videoId LIMIT 1")
|
||||||
|
fun findById(videoId: String): WatchHistoryItem
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun insertAll(vararg watchHistoryItems: WatchHistoryItem)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(watchHistoryItem: WatchHistoryItem)
|
||||||
|
|
||||||
|
@Query("DELETE FROM watchHistoryItem")
|
||||||
|
fun deleteAll()
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package com.github.libretube.database
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.OnConflictStrategy
|
||||||
|
import androidx.room.Query
|
||||||
|
import com.github.libretube.obj.WatchPosition
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface WatchPositionDao {
|
||||||
|
@Query("SELECT * FROM watchPosition")
|
||||||
|
fun getAll(): List<WatchPosition>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM watchPosition WHERE videoId LIKE :videoId LIMIT 1")
|
||||||
|
fun findById(videoId: String): WatchPosition
|
||||||
|
|
||||||
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
|
fun insertAll(vararg watchPositions: WatchPosition)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(watchPosition: WatchPosition)
|
||||||
|
|
||||||
|
@Query("DELETE FROM watchPosition")
|
||||||
|
fun deleteAll()
|
||||||
|
}
|
@ -5,9 +5,9 @@ import android.os.Bundle
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
|
import com.github.libretube.database.DatabaseHolder
|
||||||
import com.github.libretube.databinding.DialogCustomInstanceBinding
|
import com.github.libretube.databinding.DialogCustomInstanceBinding
|
||||||
import com.github.libretube.obj.CustomInstance
|
import com.github.libretube.obj.CustomInstance
|
||||||
import com.github.libretube.preferences.PreferenceHelper
|
|
||||||
import com.github.libretube.util.ThemeHelper
|
import com.github.libretube.util.ThemeHelper
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
@ -26,10 +26,11 @@ class CustomInstanceDialog : DialogFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.addInstance.setOnClickListener {
|
binding.addInstance.setOnClickListener {
|
||||||
val customInstance = CustomInstance()
|
val customInstance = CustomInstance(
|
||||||
customInstance.name = binding.instanceName.text.toString()
|
name = binding.instanceName.text.toString(),
|
||||||
customInstance.apiUrl = binding.instanceApiUrl.text.toString()
|
apiUrl = binding.instanceApiUrl.text.toString(),
|
||||||
customInstance.frontendUrl = binding.instanceFrontendUrl.text.toString()
|
frontendUrl = binding.instanceFrontendUrl.text.toString()
|
||||||
|
)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
customInstance.name != "" &&
|
customInstance.name != "" &&
|
||||||
@ -41,7 +42,10 @@ class CustomInstanceDialog : DialogFragment() {
|
|||||||
URL(customInstance.apiUrl).toURI()
|
URL(customInstance.apiUrl).toURI()
|
||||||
URL(customInstance.frontendUrl).toURI()
|
URL(customInstance.frontendUrl).toURI()
|
||||||
|
|
||||||
PreferenceHelper.saveCustomInstance(customInstance)
|
Thread {
|
||||||
|
DatabaseHolder.database.customInstanceDao().insertAll(customInstance)
|
||||||
|
}.start()
|
||||||
|
|
||||||
activity?.recreate()
|
activity?.recreate()
|
||||||
dismiss()
|
dismiss()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -7,6 +7,9 @@ import androidx.fragment.app.DialogFragment
|
|||||||
import com.github.libretube.PIPED_FRONTEND_URL
|
import com.github.libretube.PIPED_FRONTEND_URL
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.YOUTUBE_FRONTEND_URL
|
import com.github.libretube.YOUTUBE_FRONTEND_URL
|
||||||
|
import com.github.libretube.database.DatabaseHolder
|
||||||
|
import com.github.libretube.extensions.await
|
||||||
|
import com.github.libretube.obj.CustomInstance
|
||||||
import com.github.libretube.preferences.PreferenceHelper
|
import com.github.libretube.preferences.PreferenceHelper
|
||||||
import com.github.libretube.preferences.PreferenceKeys
|
import com.github.libretube.preferences.PreferenceKeys
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
@ -71,7 +74,10 @@ class ShareDialog(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// get the api urls of the other custom instances
|
// get the api urls of the other custom instances
|
||||||
val customInstances = PreferenceHelper.getCustomInstances()
|
var customInstances = listOf<CustomInstance>()
|
||||||
|
Thread {
|
||||||
|
customInstances = DatabaseHolder.database.customInstanceDao().getAll()
|
||||||
|
}.await()
|
||||||
|
|
||||||
// return the custom instance frontend url if available
|
// return the custom instance frontend url if available
|
||||||
customInstances.forEach { instance ->
|
customInstances.forEach { instance ->
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.github.libretube.extensions
|
||||||
|
|
||||||
|
fun Thread.await() {
|
||||||
|
this.apply {
|
||||||
|
start()
|
||||||
|
join()
|
||||||
|
}
|
||||||
|
}
|
@ -3,37 +3,38 @@ package com.github.libretube.util
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewTreeObserver
|
import android.view.ViewTreeObserver
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import com.github.libretube.preferences.PreferenceHelper
|
import com.github.libretube.database.DatabaseHolder
|
||||||
|
import com.github.libretube.extensions.await
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* shows the already watched time under the video
|
* shows the already watched time under the video
|
||||||
*/
|
*/
|
||||||
fun View?.setWatchProgressLength(videoId: String, duration: Long) {
|
fun View?.setWatchProgressLength(videoId: String, duration: Long) {
|
||||||
val view = this!!
|
val view = this!!
|
||||||
val positions = PreferenceHelper.getWatchPositions()
|
var progress: Long? = null
|
||||||
var newWidth: Long? = null
|
|
||||||
|
Thread {
|
||||||
|
try {
|
||||||
|
progress = DatabaseHolder.database.watchPositionDao().findById(videoId).position
|
||||||
|
} catch (e: Exception) {
|
||||||
|
progress = null
|
||||||
|
}
|
||||||
|
}.await()
|
||||||
|
|
||||||
view.getViewTreeObserver()
|
view.getViewTreeObserver()
|
||||||
.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
|
.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
|
||||||
override fun onGlobalLayout() {
|
override fun onGlobalLayout() {
|
||||||
this@setWatchProgressLength.getViewTreeObserver().removeOnGlobalLayoutListener(this)
|
this@setWatchProgressLength.getViewTreeObserver().removeOnGlobalLayoutListener(this)
|
||||||
positions.forEach {
|
if (progress == null) {
|
||||||
if (it.videoId == videoId) {
|
|
||||||
val fullWidth = (parent as LinearLayout).width
|
|
||||||
if (duration != 0L) newWidth =
|
|
||||||
(fullWidth * (it.position / (duration))) / 1000
|
|
||||||
return@forEach
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (newWidth != null) {
|
|
||||||
val lp = view.layoutParams
|
|
||||||
lp.apply {
|
|
||||||
width = newWidth!!.toInt()
|
|
||||||
}
|
|
||||||
view.layoutParams = lp
|
|
||||||
view.visibility = View.VISIBLE
|
|
||||||
} else {
|
|
||||||
view.visibility = View.GONE
|
view.visibility = View.GONE
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
val fullWidth = (parent as LinearLayout).width
|
||||||
|
val newWidth = (fullWidth * (progress!! / (duration))) / 1000
|
||||||
|
val lp = view.layoutParams
|
||||||
|
lp.width = newWidth.toInt()
|
||||||
|
view.layoutParams = lp
|
||||||
|
view.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,8 @@ import com.github.libretube.activities.MainActivity
|
|||||||
import com.github.libretube.adapters.ChaptersAdapter
|
import com.github.libretube.adapters.ChaptersAdapter
|
||||||
import com.github.libretube.adapters.CommentsAdapter
|
import com.github.libretube.adapters.CommentsAdapter
|
||||||
import com.github.libretube.adapters.TrendingAdapter
|
import com.github.libretube.adapters.TrendingAdapter
|
||||||
|
import com.github.libretube.database.DatabaseHelper
|
||||||
|
import com.github.libretube.database.DatabaseHolder
|
||||||
import com.github.libretube.databinding.DoubleTapOverlayBinding
|
import com.github.libretube.databinding.DoubleTapOverlayBinding
|
||||||
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
import com.github.libretube.databinding.ExoStyledPlayerControlViewBinding
|
||||||
import com.github.libretube.databinding.FragmentPlayerBinding
|
import com.github.libretube.databinding.FragmentPlayerBinding
|
||||||
@ -44,6 +46,7 @@ import com.github.libretube.dialogs.AddToPlaylistDialog
|
|||||||
import com.github.libretube.dialogs.DownloadDialog
|
import com.github.libretube.dialogs.DownloadDialog
|
||||||
import com.github.libretube.dialogs.ShareDialog
|
import com.github.libretube.dialogs.ShareDialog
|
||||||
import com.github.libretube.extensions.BaseFragment
|
import com.github.libretube.extensions.BaseFragment
|
||||||
|
import com.github.libretube.extensions.await
|
||||||
import com.github.libretube.interfaces.DoubleTapInterface
|
import com.github.libretube.interfaces.DoubleTapInterface
|
||||||
import com.github.libretube.interfaces.PlayerOptionsInterface
|
import com.github.libretube.interfaces.PlayerOptionsInterface
|
||||||
import com.github.libretube.obj.ChapterSegment
|
import com.github.libretube.obj.ChapterSegment
|
||||||
@ -88,7 +91,6 @@ import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
|
|||||||
import com.google.android.exoplayer2.util.RepeatModeUtil
|
import com.google.android.exoplayer2.util.RepeatModeUtil
|
||||||
import com.google.android.exoplayer2.video.VideoSize
|
import com.google.android.exoplayer2.video.VideoSize
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import kotlinx.android.synthetic.main.bottom_sheet.repeatMode
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@ -444,8 +446,7 @@ class PlayerFragment : BaseFragment() {
|
|||||||
val newParams = if (index != 0) {
|
val newParams = if (index != 0) {
|
||||||
// caption selected
|
// caption selected
|
||||||
|
|
||||||
// get the caption name and language
|
// get the caption language code
|
||||||
val captionLanguage = subtitlesNamesList[index]
|
|
||||||
val captionLanguageCode = subtitleCodesList[index]
|
val captionLanguageCode = subtitleCodesList[index]
|
||||||
|
|
||||||
// select the new caption preference
|
// select the new caption preference
|
||||||
@ -814,13 +815,13 @@ class PlayerFragment : BaseFragment() {
|
|||||||
// save the watch position if video isn't finished and option enabled
|
// save the watch position if video isn't finished and option enabled
|
||||||
private fun saveWatchPosition() {
|
private fun saveWatchPosition() {
|
||||||
if (watchPositionsEnabled && exoPlayer.currentPosition != exoPlayer.duration) {
|
if (watchPositionsEnabled && exoPlayer.currentPosition != exoPlayer.duration) {
|
||||||
PreferenceHelper.saveWatchPosition(
|
DatabaseHelper.saveWatchPosition(
|
||||||
videoId!!,
|
videoId!!,
|
||||||
exoPlayer.currentPosition
|
exoPlayer.currentPosition
|
||||||
)
|
)
|
||||||
} else if (watchPositionsEnabled) {
|
} else if (watchPositionsEnabled) {
|
||||||
// delete watch position if video has ended
|
// delete watch position if video has ended
|
||||||
PreferenceHelper.removeWatchPosition(videoId!!)
|
DatabaseHelper.removeWatchPosition(videoId!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -878,7 +879,7 @@ class PlayerFragment : BaseFragment() {
|
|||||||
if (!relatedStreamsEnabled) toggleComments()
|
if (!relatedStreamsEnabled) toggleComments()
|
||||||
// prepare for autoplay
|
// prepare for autoplay
|
||||||
if (autoplayEnabled) setNextStream()
|
if (autoplayEnabled) setNextStream()
|
||||||
if (watchHistoryEnabled) PreferenceHelper.addToWatchHistory(videoId!!, streams)
|
if (watchHistoryEnabled) DatabaseHelper.addToWatchHistory(videoId!!, streams)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -934,14 +935,14 @@ class PlayerFragment : BaseFragment() {
|
|||||||
|
|
||||||
private fun seekToWatchPosition() {
|
private fun seekToWatchPosition() {
|
||||||
// seek to saved watch position if available
|
// seek to saved watch position if available
|
||||||
val watchPositions = PreferenceHelper.getWatchPositions()
|
|
||||||
var position: Long? = null
|
var position: Long? = null
|
||||||
watchPositions.forEach {
|
Thread {
|
||||||
if (it.videoId == videoId &&
|
try {
|
||||||
// don't seek to the position if it's the end, autoplay would skip it immediately
|
position = DatabaseHolder.database.watchPositionDao().findById(videoId!!).position
|
||||||
streams.duration!! - it.position / 1000 > 2
|
} catch (e: Exception) {
|
||||||
) position = it.position
|
position = null
|
||||||
}
|
}
|
||||||
|
}.await()
|
||||||
// support for time stamped links
|
// support for time stamped links
|
||||||
val timeStamp: Long? = arguments?.getLong("timeStamp")
|
val timeStamp: Long? = arguments?.getLong("timeStamp")
|
||||||
if (timeStamp != null && timeStamp != 0L) {
|
if (timeStamp != null && timeStamp != 0L) {
|
||||||
|
@ -11,10 +11,11 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||||||
import com.github.libretube.activities.MainActivity
|
import com.github.libretube.activities.MainActivity
|
||||||
import com.github.libretube.adapters.SearchHistoryAdapter
|
import com.github.libretube.adapters.SearchHistoryAdapter
|
||||||
import com.github.libretube.adapters.SearchSuggestionsAdapter
|
import com.github.libretube.adapters.SearchSuggestionsAdapter
|
||||||
|
import com.github.libretube.database.DatabaseHolder
|
||||||
import com.github.libretube.databinding.FragmentSearchBinding
|
import com.github.libretube.databinding.FragmentSearchBinding
|
||||||
import com.github.libretube.extensions.BaseFragment
|
import com.github.libretube.extensions.BaseFragment
|
||||||
|
import com.github.libretube.extensions.await
|
||||||
import com.github.libretube.models.SearchViewModel
|
import com.github.libretube.models.SearchViewModel
|
||||||
import com.github.libretube.preferences.PreferenceHelper
|
|
||||||
import com.github.libretube.util.RetrofitInstance
|
import com.github.libretube.util.RetrofitInstance
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -89,7 +90,11 @@ class SearchFragment() : BaseFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun showHistory() {
|
private fun showHistory() {
|
||||||
val historyList = PreferenceHelper.getSearchHistory()
|
var historyList = listOf<String>()
|
||||||
|
Thread {
|
||||||
|
val history = DatabaseHolder.database.searchHistoryDao().getAll()
|
||||||
|
historyList = history.map { it.query!! }
|
||||||
|
}.await()
|
||||||
if (historyList.isNotEmpty()) {
|
if (historyList.isNotEmpty()) {
|
||||||
binding.suggestionsRecycler.adapter =
|
binding.suggestionsRecycler.adapter =
|
||||||
SearchHistoryAdapter(
|
SearchHistoryAdapter(
|
||||||
|
@ -9,8 +9,11 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.adapters.SearchAdapter
|
import com.github.libretube.adapters.SearchAdapter
|
||||||
|
import com.github.libretube.database.DatabaseHolder
|
||||||
import com.github.libretube.databinding.FragmentSearchResultBinding
|
import com.github.libretube.databinding.FragmentSearchResultBinding
|
||||||
import com.github.libretube.extensions.BaseFragment
|
import com.github.libretube.extensions.BaseFragment
|
||||||
|
import com.github.libretube.extensions.await
|
||||||
|
import com.github.libretube.obj.SearchHistoryItem
|
||||||
import com.github.libretube.preferences.PreferenceHelper
|
import com.github.libretube.preferences.PreferenceHelper
|
||||||
import com.github.libretube.preferences.PreferenceKeys
|
import com.github.libretube.preferences.PreferenceKeys
|
||||||
import com.github.libretube.util.RetrofitInstance
|
import com.github.libretube.util.RetrofitInstance
|
||||||
@ -132,7 +135,13 @@ class SearchResultFragment : BaseFragment() {
|
|||||||
val searchHistoryEnabled =
|
val searchHistoryEnabled =
|
||||||
PreferenceHelper.getBoolean(PreferenceKeys.SEARCH_HISTORY_TOGGLE, true)
|
PreferenceHelper.getBoolean(PreferenceKeys.SEARCH_HISTORY_TOGGLE, true)
|
||||||
if (searchHistoryEnabled && query != "") {
|
if (searchHistoryEnabled && query != "") {
|
||||||
PreferenceHelper.saveToSearchHistory(query)
|
Thread {
|
||||||
|
DatabaseHolder.database.searchHistoryDao().insertAll(
|
||||||
|
SearchHistoryItem(
|
||||||
|
query = query
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}.await()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,11 @@ import androidx.recyclerview.widget.ItemTouchHelper
|
|||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.github.libretube.adapters.WatchHistoryAdapter
|
import com.github.libretube.adapters.WatchHistoryAdapter
|
||||||
|
import com.github.libretube.database.DatabaseHolder
|
||||||
import com.github.libretube.databinding.FragmentWatchHistoryBinding
|
import com.github.libretube.databinding.FragmentWatchHistoryBinding
|
||||||
import com.github.libretube.extensions.BaseFragment
|
import com.github.libretube.extensions.BaseFragment
|
||||||
import com.github.libretube.preferences.PreferenceHelper
|
import com.github.libretube.extensions.await
|
||||||
|
import com.github.libretube.obj.WatchHistoryItem
|
||||||
|
|
||||||
class WatchHistoryFragment : BaseFragment() {
|
class WatchHistoryFragment : BaseFragment() {
|
||||||
private val TAG = "WatchHistoryFragment"
|
private val TAG = "WatchHistoryFragment"
|
||||||
@ -28,7 +30,11 @@ class WatchHistoryFragment : BaseFragment() {
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
val watchHistory = PreferenceHelper.getWatchHistory()
|
var watchHistory = listOf<WatchHistoryItem>()
|
||||||
|
|
||||||
|
Thread {
|
||||||
|
watchHistory = DatabaseHolder.database.watchHistoryDao().getAll()
|
||||||
|
}.await()
|
||||||
|
|
||||||
if (watchHistory.isEmpty()) return
|
if (watchHistory.isEmpty()) return
|
||||||
|
|
||||||
@ -39,7 +45,7 @@ class WatchHistoryFragment : BaseFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val watchHistoryAdapter = WatchHistoryAdapter(
|
val watchHistoryAdapter = WatchHistoryAdapter(
|
||||||
watchHistory,
|
watchHistory.toMutableList(),
|
||||||
childFragmentManager
|
childFragmentManager
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
package com.github.libretube.obj
|
package com.github.libretube.obj
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "customInstance")
|
||||||
class CustomInstance(
|
class CustomInstance(
|
||||||
var name: String = "",
|
@PrimaryKey var name: String = "",
|
||||||
var apiUrl: String = "",
|
@ColumnInfo var apiUrl: String = "",
|
||||||
var frontendUrl: String = ""
|
@ColumnInfo var frontendUrl: String = ""
|
||||||
)
|
)
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.github.libretube.obj
|
||||||
|
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "searchHistoryItem")
|
||||||
|
data class SearchHistoryItem(
|
||||||
|
@PrimaryKey val query: String = ""
|
||||||
|
)
|
@ -1,12 +1,17 @@
|
|||||||
package com.github.libretube.obj
|
package com.github.libretube.obj
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "watchHistoryItem")
|
||||||
data class WatchHistoryItem(
|
data class WatchHistoryItem(
|
||||||
val videoId: String? = null,
|
@PrimaryKey val videoId: String = "",
|
||||||
val title: String? = null,
|
@ColumnInfo val title: String? = null,
|
||||||
val uploadDate: String? = null,
|
@ColumnInfo val uploadDate: String? = null,
|
||||||
val uploader: String? = null,
|
@ColumnInfo val uploader: String? = null,
|
||||||
val uploaderUrl: String? = null,
|
@ColumnInfo val uploaderUrl: String? = null,
|
||||||
val uploaderAvatar: String? = null,
|
@ColumnInfo val uploaderAvatar: String? = null,
|
||||||
val thumbnailUrl: String? = null,
|
@ColumnInfo val thumbnailUrl: String? = null,
|
||||||
val duration: Long? = null
|
@ColumnInfo val duration: Long? = null
|
||||||
)
|
)
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
package com.github.libretube.obj
|
package com.github.libretube.obj
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "watchPosition")
|
||||||
data class WatchPosition(
|
data class WatchPosition(
|
||||||
val videoId: String = "",
|
@PrimaryKey val videoId: String = "",
|
||||||
val position: Long = 0L
|
@ColumnInfo val position: Long = 0L
|
||||||
)
|
)
|
||||||
|
@ -4,6 +4,7 @@ import android.os.Bundle
|
|||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.activities.SettingsActivity
|
import com.github.libretube.activities.SettingsActivity
|
||||||
|
import com.github.libretube.database.DatabaseHolder
|
||||||
import com.github.libretube.views.MaterialPreferenceFragment
|
import com.github.libretube.views.MaterialPreferenceFragment
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
|
||||||
@ -18,33 +19,41 @@ class HistorySettings : MaterialPreferenceFragment() {
|
|||||||
// clear search history
|
// clear search history
|
||||||
val clearHistory = findPreference<Preference>(PreferenceKeys.CLEAR_SEARCH_HISTORY)
|
val clearHistory = findPreference<Preference>(PreferenceKeys.CLEAR_SEARCH_HISTORY)
|
||||||
clearHistory?.setOnPreferenceClickListener {
|
clearHistory?.setOnPreferenceClickListener {
|
||||||
showClearDialog(R.string.clear_history, "search_history")
|
showClearDialog(R.string.clear_history) {
|
||||||
|
DatabaseHolder.database.searchHistoryDao().deleteAll()
|
||||||
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear watch history and positions
|
// clear watch history and positions
|
||||||
val clearWatchHistory = findPreference<Preference>(PreferenceKeys.CLEAR_WATCH_HISTORY)
|
val clearWatchHistory = findPreference<Preference>(PreferenceKeys.CLEAR_WATCH_HISTORY)
|
||||||
clearWatchHistory?.setOnPreferenceClickListener {
|
clearWatchHistory?.setOnPreferenceClickListener {
|
||||||
showClearDialog(R.string.clear_history, "watch_history")
|
showClearDialog(R.string.clear_history) {
|
||||||
|
DatabaseHolder.database.watchHistoryDao().deleteAll()
|
||||||
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
// clear watch positions
|
// clear watch positions
|
||||||
val clearWatchPositions = findPreference<Preference>(PreferenceKeys.CLEAR_WATCH_POSITIONS)
|
val clearWatchPositions = findPreference<Preference>(PreferenceKeys.CLEAR_WATCH_POSITIONS)
|
||||||
clearWatchPositions?.setOnPreferenceClickListener {
|
clearWatchPositions?.setOnPreferenceClickListener {
|
||||||
showClearDialog(R.string.reset_watch_positions, "watch_positions")
|
showClearDialog(R.string.reset_watch_positions) {
|
||||||
|
DatabaseHolder.database.watchPositionDao().deleteAll()
|
||||||
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showClearDialog(title: Int, preferenceKey: String) {
|
private fun showClearDialog(title: Int, action: () -> Unit) {
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
.setTitle(title)
|
.setTitle(title)
|
||||||
.setMessage(R.string.irreversible)
|
.setMessage(R.string.irreversible)
|
||||||
.setNegativeButton(R.string.cancel, null)
|
.setNegativeButton(R.string.cancel, null)
|
||||||
.setPositiveButton(R.string.okay) { _, _ ->
|
.setPositiveButton(R.string.okay) { _, _ ->
|
||||||
// clear the selected preference preferences
|
// clear the selected preference preferences
|
||||||
PreferenceHelper.removePreference(preferenceKey)
|
Thread {
|
||||||
|
action()
|
||||||
|
}.start()
|
||||||
}
|
}
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package com.github.libretube.preferences
|
package com.github.libretube.preferences
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
@ -13,10 +12,13 @@ import androidx.preference.Preference
|
|||||||
import androidx.preference.SwitchPreferenceCompat
|
import androidx.preference.SwitchPreferenceCompat
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.activities.SettingsActivity
|
import com.github.libretube.activities.SettingsActivity
|
||||||
|
import com.github.libretube.database.DatabaseHolder
|
||||||
import com.github.libretube.dialogs.CustomInstanceDialog
|
import com.github.libretube.dialogs.CustomInstanceDialog
|
||||||
import com.github.libretube.dialogs.DeleteAccountDialog
|
import com.github.libretube.dialogs.DeleteAccountDialog
|
||||||
import com.github.libretube.dialogs.LoginDialog
|
import com.github.libretube.dialogs.LoginDialog
|
||||||
import com.github.libretube.dialogs.LogoutDialog
|
import com.github.libretube.dialogs.LogoutDialog
|
||||||
|
import com.github.libretube.extensions.await
|
||||||
|
import com.github.libretube.obj.CustomInstance
|
||||||
import com.github.libretube.util.ImportHelper
|
import com.github.libretube.util.ImportHelper
|
||||||
import com.github.libretube.util.PermissionHelper
|
import com.github.libretube.util.PermissionHelper
|
||||||
import com.github.libretube.util.RetrofitInstance
|
import com.github.libretube.util.RetrofitInstance
|
||||||
@ -104,9 +106,10 @@ class InstanceSettings : MaterialPreferenceFragment() {
|
|||||||
|
|
||||||
val clearCustomInstances = findPreference<Preference>(PreferenceKeys.CLEAR_CUSTOM_INSTANCES)
|
val clearCustomInstances = findPreference<Preference>(PreferenceKeys.CLEAR_CUSTOM_INSTANCES)
|
||||||
clearCustomInstances?.setOnPreferenceClickListener {
|
clearCustomInstances?.setOnPreferenceClickListener {
|
||||||
PreferenceHelper.removePreference("customInstances")
|
Thread {
|
||||||
val intent = Intent(context, SettingsActivity::class.java)
|
DatabaseHolder.database.customInstanceDao().deleteAll()
|
||||||
startActivity(intent)
|
}.await()
|
||||||
|
activity?.recreate()
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +160,10 @@ class InstanceSettings : MaterialPreferenceFragment() {
|
|||||||
|
|
||||||
private fun initCustomInstances(instancePref: ListPreference) {
|
private fun initCustomInstances(instancePref: ListPreference) {
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val customInstances = PreferenceHelper.getCustomInstances()
|
var customInstances = listOf<CustomInstance>()
|
||||||
|
Thread {
|
||||||
|
customInstances = DatabaseHolder.database.customInstanceDao().getAll()
|
||||||
|
}.await()
|
||||||
|
|
||||||
val instanceNames = arrayListOf<String>()
|
val instanceNames = arrayListOf<String>()
|
||||||
val instanceValues = arrayListOf<String>()
|
val instanceValues = arrayListOf<String>()
|
||||||
|
@ -5,11 +5,6 @@ import android.content.SharedPreferences
|
|||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.fasterxml.jackson.core.type.TypeReference
|
import com.fasterxml.jackson.core.type.TypeReference
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import com.github.libretube.obj.CustomInstance
|
|
||||||
import com.github.libretube.obj.Streams
|
|
||||||
import com.github.libretube.obj.WatchHistoryItem
|
|
||||||
import com.github.libretube.obj.WatchPosition
|
|
||||||
import com.github.libretube.util.toID
|
|
||||||
|
|
||||||
object PreferenceHelper {
|
object PreferenceHelper {
|
||||||
private val TAG = "PreferenceHelper"
|
private val TAG = "PreferenceHelper"
|
||||||
@ -75,174 +70,6 @@ object PreferenceHelper {
|
|||||||
authEditor.putString(PreferenceKeys.USERNAME, newValue).apply()
|
authEditor.putString(PreferenceKeys.USERNAME, newValue).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveCustomInstance(customInstance: CustomInstance) {
|
|
||||||
val customInstancesList = getCustomInstances()
|
|
||||||
customInstancesList += customInstance
|
|
||||||
|
|
||||||
val json = mapper.writeValueAsString(customInstancesList)
|
|
||||||
editor.putString("customInstances", json).apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getCustomInstances(): ArrayList<CustomInstance> {
|
|
||||||
val json: String = settings.getString("customInstances", "")!!
|
|
||||||
val type = mapper.typeFactory.constructCollectionType(
|
|
||||||
List::class.java,
|
|
||||||
CustomInstance::class.java
|
|
||||||
)
|
|
||||||
return try {
|
|
||||||
mapper.readValue(json, type)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
arrayListOf()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getSearchHistory(): List<String> {
|
|
||||||
return try {
|
|
||||||
val json = settings.getString("search_history", "")!!
|
|
||||||
val type = object : TypeReference<List<String>>() {}
|
|
||||||
return mapper.readValue(json, type)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun saveToSearchHistory(query: String) {
|
|
||||||
val historyList = getSearchHistory().toMutableList()
|
|
||||||
|
|
||||||
if ((historyList.contains(query))) {
|
|
||||||
// remove from history list if already contained
|
|
||||||
historyList -= query
|
|
||||||
}
|
|
||||||
|
|
||||||
// append new query to history
|
|
||||||
historyList.add(0, query)
|
|
||||||
|
|
||||||
if (historyList.size > 10) {
|
|
||||||
historyList.removeAt(historyList.size - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
updateSearchHistory(historyList)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removeFromSearchHistory(query: String) {
|
|
||||||
val historyList = getSearchHistory().toMutableList()
|
|
||||||
historyList -= query
|
|
||||||
updateSearchHistory(historyList)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateSearchHistory(historyList: List<String>) {
|
|
||||||
val json = mapper.writeValueAsString(historyList)
|
|
||||||
editor.putString("search_history", json).apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addToWatchHistory(videoId: String, streams: Streams) {
|
|
||||||
removeFromWatchHistory(videoId)
|
|
||||||
|
|
||||||
val watchHistoryItem = WatchHistoryItem(
|
|
||||||
videoId,
|
|
||||||
streams.title,
|
|
||||||
streams.uploadDate,
|
|
||||||
streams.uploader,
|
|
||||||
streams.uploaderUrl.toID(),
|
|
||||||
streams.uploaderAvatar,
|
|
||||||
streams.thumbnailUrl,
|
|
||||||
streams.duration
|
|
||||||
)
|
|
||||||
|
|
||||||
val watchHistory = getWatchHistory()
|
|
||||||
|
|
||||||
watchHistory += watchHistoryItem
|
|
||||||
|
|
||||||
// remove oldest item when the watch history is longer than the pref
|
|
||||||
val maxWatchHistorySize = getString(PreferenceKeys.WATCH_HISTORY_SIZE, "unlimited")
|
|
||||||
if (maxWatchHistorySize != "unlimited" && watchHistory.size > maxWatchHistorySize.toInt()) {
|
|
||||||
watchHistory.removeAt(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
val json = mapper.writeValueAsString(watchHistory)
|
|
||||||
editor.putString("watch_history", json).apply()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removeFromWatchHistory(videoId: String) {
|
|
||||||
val watchHistory = getWatchHistory()
|
|
||||||
|
|
||||||
var indexToRemove: Int? = null
|
|
||||||
watchHistory.forEachIndexed { index, item ->
|
|
||||||
if (item.videoId == videoId) indexToRemove = index
|
|
||||||
}
|
|
||||||
if (indexToRemove == null) return
|
|
||||||
watchHistory.removeAt(indexToRemove!!)
|
|
||||||
val json = mapper.writeValueAsString(watchHistory)
|
|
||||||
editor.putString("watch_history", json).commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removeFromWatchHistory(position: Int) {
|
|
||||||
val watchHistory = getWatchHistory()
|
|
||||||
watchHistory.removeAt(position)
|
|
||||||
|
|
||||||
val json = mapper.writeValueAsString(watchHistory)
|
|
||||||
editor.putString("watch_history", json).commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getWatchHistory(): ArrayList<WatchHistoryItem> {
|
|
||||||
val json: String = settings.getString("watch_history", "")!!
|
|
||||||
val type = mapper.typeFactory.constructCollectionType(
|
|
||||||
List::class.java,
|
|
||||||
WatchHistoryItem::class.java
|
|
||||||
)
|
|
||||||
|
|
||||||
return try {
|
|
||||||
mapper.readValue(json, type)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
arrayListOf()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun saveWatchPosition(videoId: String, position: Long) {
|
|
||||||
val watchPositions = getWatchPositions()
|
|
||||||
val watchPositionItem = WatchPosition(videoId, position)
|
|
||||||
|
|
||||||
var indexToRemove: Int? = null
|
|
||||||
watchPositions.forEachIndexed { index, item ->
|
|
||||||
if (item.videoId == videoId) indexToRemove = index
|
|
||||||
}
|
|
||||||
|
|
||||||
if (indexToRemove != null) watchPositions.removeAt(indexToRemove!!)
|
|
||||||
|
|
||||||
watchPositions += watchPositionItem
|
|
||||||
|
|
||||||
val json = mapper.writeValueAsString(watchPositions)
|
|
||||||
editor.putString("watch_positions", json).commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removeWatchPosition(videoId: String) {
|
|
||||||
val watchPositions = getWatchPositions()
|
|
||||||
|
|
||||||
var indexToRemove: Int? = null
|
|
||||||
watchPositions.forEachIndexed { index, item ->
|
|
||||||
if (item.videoId == videoId) indexToRemove = index
|
|
||||||
}
|
|
||||||
|
|
||||||
if (indexToRemove != null) watchPositions.removeAt(indexToRemove!!)
|
|
||||||
|
|
||||||
val json = mapper.writeValueAsString(watchPositions)
|
|
||||||
editor.putString("watch_positions", json).commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getWatchPositions(): ArrayList<WatchPosition> {
|
|
||||||
val json: String = settings.getString("watch_positions", "")!!
|
|
||||||
val type = mapper.typeFactory.constructCollectionType(
|
|
||||||
List::class.java,
|
|
||||||
WatchPosition::class.java
|
|
||||||
)
|
|
||||||
|
|
||||||
return try {
|
|
||||||
mapper.readValue(json, type)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
arrayListOf()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setLatestVideoId(videoId: String) {
|
fun setLatestVideoId(videoId: String) {
|
||||||
editor.putString(PreferenceKeys.LAST_STREAM_VIDEO_ID, videoId)
|
editor.putString(PreferenceKeys.LAST_STREAM_VIDEO_ID, videoId)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package com.github.libretube.update
|
|||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import com.github.libretube.GITHUB_API_URL
|
import com.github.libretube.GITHUB_API_URL
|
||||||
|
import com.github.libretube.extensions.await
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
object UpdateChecker {
|
object UpdateChecker {
|
||||||
@ -15,10 +16,7 @@ object UpdateChecker {
|
|||||||
versionInfo = getUpdateInfo()
|
versionInfo = getUpdateInfo()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
}
|
}
|
||||||
}
|
}.await()
|
||||||
thread.start()
|
|
||||||
// wait for the thread to finish
|
|
||||||
thread.join()
|
|
||||||
|
|
||||||
// return the information about the latest version
|
// return the information about the latest version
|
||||||
return versionInfo
|
return versionInfo
|
||||||
|
@ -10,6 +10,7 @@ import android.support.v4.media.session.MediaSessionCompat
|
|||||||
import com.github.libretube.BACKGROUND_CHANNEL_ID
|
import com.github.libretube.BACKGROUND_CHANNEL_ID
|
||||||
import com.github.libretube.PLAYER_NOTIFICATION_ID
|
import com.github.libretube.PLAYER_NOTIFICATION_ID
|
||||||
import com.github.libretube.activities.MainActivity
|
import com.github.libretube.activities.MainActivity
|
||||||
|
import com.github.libretube.extensions.await
|
||||||
import com.github.libretube.obj.Streams
|
import com.github.libretube.obj.Streams
|
||||||
import com.google.android.exoplayer2.ExoPlayer
|
import com.google.android.exoplayer2.ExoPlayer
|
||||||
import com.google.android.exoplayer2.Player
|
import com.google.android.exoplayer2.Player
|
||||||
@ -87,7 +88,7 @@ class NowPlayingNotification(
|
|||||||
/**
|
/**
|
||||||
* running on a new thread to prevent a NetworkMainThreadException
|
* running on a new thread to prevent a NetworkMainThreadException
|
||||||
*/
|
*/
|
||||||
val thread = Thread {
|
Thread {
|
||||||
try {
|
try {
|
||||||
/**
|
/**
|
||||||
* try to GET the thumbnail from the URL
|
* try to GET the thumbnail from the URL
|
||||||
@ -97,9 +98,7 @@ class NowPlayingNotification(
|
|||||||
} catch (ex: java.lang.Exception) {
|
} catch (ex: java.lang.Exception) {
|
||||||
ex.printStackTrace()
|
ex.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}.await()
|
||||||
thread.start()
|
|
||||||
thread.join()
|
|
||||||
/**
|
/**
|
||||||
* returns the scaled bitmap if it got fetched successfully
|
* returns the scaled bitmap if it got fetched successfully
|
||||||
*/
|
*/
|
||||||
|
@ -20,6 +20,7 @@ cronetEmbedded = "101.4951.41"
|
|||||||
cronetOkHttp = "0.1.0"
|
cronetOkHttp = "0.1.0"
|
||||||
coil = "2.1.0"
|
coil = "2.1.0"
|
||||||
leakcanary = "2.8.1"
|
leakcanary = "2.8.1"
|
||||||
|
room = "2.4.3"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||||
@ -48,4 +49,6 @@ coil = { group = "io.coil-kt", name = "coil", version.ref="coil" }
|
|||||||
square-leakcanary = { group = "com.squareup.leakcanary", name = "leakcanary-android", version.ref = "leakcanary" }
|
square-leakcanary = { group = "com.squareup.leakcanary", name = "leakcanary-android", version.ref = "leakcanary" }
|
||||||
lifecycle-viewmodel = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
|
lifecycle-viewmodel = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version.ref = "lifecycle" }
|
||||||
lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
|
lifecycle-runtime = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
|
||||||
lifecycle-livedata = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycle" }
|
lifecycle-livedata = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version.ref = "lifecycle" }
|
||||||
|
room = { group = "androidx.room", name="room-runtime", version.ref = "room" }
|
||||||
|
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
|
Loading…
Reference in New Issue
Block a user