mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-08-04 07:09:53 +00:00
WIP Store/testing
This commit is contained in:
parent
10e3d2122f
commit
99c06c516f
15 changed files with 439 additions and 29 deletions
|
@ -39,7 +39,7 @@ protobuf {
|
||||||
|
|
||||||
android {
|
android {
|
||||||
namespace 'com.futo.platformplayer'
|
namespace 'com.futo.platformplayer'
|
||||||
compileSdk 33
|
compileSdk 34
|
||||||
flavorDimensions "buildType"
|
flavorDimensions "buildType"
|
||||||
productFlavors {
|
productFlavors {
|
||||||
stable {
|
stable {
|
||||||
|
|
|
@ -1,24 +1,18 @@
|
||||||
package com.futo.platformplayer.states
|
package com.futo.platformplayer.states
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.media.AudioManager
|
import android.media.AudioManager
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.Network
|
import android.net.Network
|
||||||
import android.net.NetworkCapabilities
|
import android.net.NetworkCapabilities
|
||||||
import android.net.NetworkRequest
|
import android.net.NetworkRequest
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Environment
|
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
import androidx.activity.ComponentActivity
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.core.net.toUri
|
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
@ -28,10 +22,9 @@ import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.activities.CaptchaActivity
|
import com.futo.platformplayer.activities.CaptchaActivity
|
||||||
import com.futo.platformplayer.activities.IWithResultLauncher
|
import com.futo.platformplayer.activities.IWithResultLauncher
|
||||||
import com.futo.platformplayer.activities.MainActivity
|
import com.futo.platformplayer.activities.MainActivity
|
||||||
import com.futo.platformplayer.api.media.Serializer
|
import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo
|
||||||
import com.futo.platformplayer.api.media.platforms.js.DevJSClient
|
import com.futo.platformplayer.api.media.platforms.js.DevJSClient
|
||||||
import com.futo.platformplayer.api.media.platforms.js.JSClient
|
import com.futo.platformplayer.api.media.platforms.js.JSClient
|
||||||
import com.futo.platformplayer.api.media.platforms.js.internal.JSHttpClient
|
|
||||||
import com.futo.platformplayer.background.BackgroundWorker
|
import com.futo.platformplayer.background.BackgroundWorker
|
||||||
import com.futo.platformplayer.cache.ChannelContentCache
|
import com.futo.platformplayer.cache.ChannelContentCache
|
||||||
import com.futo.platformplayer.casting.StateCasting
|
import com.futo.platformplayer.casting.StateCasting
|
||||||
|
@ -43,20 +36,20 @@ import com.futo.platformplayer.logging.AndroidLogConsumer
|
||||||
import com.futo.platformplayer.logging.FileLogConsumer
|
import com.futo.platformplayer.logging.FileLogConsumer
|
||||||
import com.futo.platformplayer.logging.LogLevel
|
import com.futo.platformplayer.logging.LogLevel
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
|
import com.futo.platformplayer.models.HistoryVideo
|
||||||
import com.futo.platformplayer.receivers.AudioNoisyReceiver
|
import com.futo.platformplayer.receivers.AudioNoisyReceiver
|
||||||
import com.futo.platformplayer.services.DownloadService
|
import com.futo.platformplayer.services.DownloadService
|
||||||
import com.futo.platformplayer.stores.FragmentedStorage
|
import com.futo.platformplayer.stores.FragmentedStorage
|
||||||
|
import com.futo.platformplayer.stores.db.types.DBHistory
|
||||||
import com.futo.platformplayer.stores.v2.ManagedStore
|
import com.futo.platformplayer.stores.v2.ManagedStore
|
||||||
import com.stripe.android.core.utils.encodeToJson
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.decodeFromString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
import kotlin.time.measureTime
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* This class contains global context for unconventional cases where obtaining context is hard.
|
* This class contains global context for unconventional cases where obtaining context is hard.
|
||||||
|
@ -545,7 +538,73 @@ class StateApp {
|
||||||
|
|
||||||
StateAnnouncement.instance.registerDidYouKnow();
|
StateAnnouncement.instance.registerDidYouKnow();
|
||||||
Logger.i(TAG, "MainApp Started: Finished");
|
Logger.i(TAG, "MainApp Started: Finished");
|
||||||
|
|
||||||
|
|
||||||
|
if(true) {
|
||||||
|
Logger.i(TAG, "TEST:--------(200)---------");
|
||||||
|
testHistoryDB(200);
|
||||||
|
Logger.i(TAG, "TEST:--------(1000)---------");
|
||||||
|
testHistoryDB(1000);
|
||||||
|
Logger.i(TAG, "TEST:--------(2000)---------");
|
||||||
|
testHistoryDB(2000);
|
||||||
|
Logger.i(TAG, "TEST:--------(4000)---------");
|
||||||
|
testHistoryDB(4000);
|
||||||
|
Logger.i(TAG, "TEST:--------(6000)---------");
|
||||||
|
testHistoryDB(6000);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
fun testHistoryDB(count: Int) {
|
||||||
|
Logger.i(TAG, "TEST: Starting tests");
|
||||||
|
StatePlaylists.instance._historyDBStore.deleteAll();
|
||||||
|
|
||||||
|
val testHistoryItem = StatePlaylists.instance.getHistory().first();
|
||||||
|
val testItemJson = StatePlaylists.instance.getHistory().first().video.toJson();
|
||||||
|
val now = OffsetDateTime.now();
|
||||||
|
|
||||||
|
val testSet = (0..count).map { HistoryVideo(Json.decodeFromString<SerializedPlatformVideo>(testItemJson.replace(testHistoryItem.video.url, UUID.randomUUID().toString())), it.toLong(), now.minusHours(it.toLong())) }
|
||||||
|
|
||||||
|
|
||||||
|
Logger.i(TAG, "TEST: Inserting (${testSet.size})");
|
||||||
|
val insertMS = measureTimeMillis {
|
||||||
|
for(item in testSet)
|
||||||
|
StatePlaylists.instance._historyDBStore.insert(item);
|
||||||
|
};
|
||||||
|
Logger.i(TAG, "TEST: Inserting in ${insertMS}ms");
|
||||||
|
|
||||||
|
var fetched: List<DBHistory.Index>? = null;
|
||||||
|
val fetchMS = measureTimeMillis {
|
||||||
|
fetched = StatePlaylists.instance._historyDBStore.getAll();
|
||||||
|
Logger.i(TAG, "TEST: Fetched: ${fetched?.size}");
|
||||||
|
};
|
||||||
|
Logger.i(TAG, "TEST: Fetch speed ${fetchMS}MS");
|
||||||
|
val deserializeMS = measureTimeMillis {
|
||||||
|
val deserialized = StatePlaylists.instance._historyDBStore.convertObjects(fetched!!);
|
||||||
|
Logger.i(TAG, "TEST: Deserialized: ${deserialized.size}");
|
||||||
|
};
|
||||||
|
Logger.i(TAG, "TEST: Deserialize speed ${deserializeMS}MS");
|
||||||
|
|
||||||
|
var fetchedIndex: List<DBHistory.Index>? = null;
|
||||||
|
val fetchIndexMS = measureTimeMillis {
|
||||||
|
fetchedIndex = StatePlaylists.instance._historyDBStore.getAllIndexes();
|
||||||
|
Logger.i(TAG, "TEST: Fetched Index: ${fetchedIndex!!.size}");
|
||||||
|
};
|
||||||
|
Logger.i(TAG, "TEST: Fetched Index speed ${fetchIndexMS}ms");
|
||||||
|
val fetchFromIndex = measureTimeMillis {
|
||||||
|
for(preItem in testSet) {
|
||||||
|
val item = StatePlaylists.instance.historyIndex[preItem.video.url];
|
||||||
|
if(item == null)
|
||||||
|
throw IllegalStateException("Missing item [${preItem.video.url}]");
|
||||||
|
if(item.url != preItem.video.url)
|
||||||
|
throw IllegalStateException("Mismatch item [${preItem.video.url}]");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Logger.i(TAG, "TEST: Index Lookup speed ${fetchFromIndex}ms");
|
||||||
|
|
||||||
|
val page1 = StatePlaylists.instance._historyDBStore.getPage(0, 20);
|
||||||
|
val page2 = StatePlaylists.instance._historyDBStore.getPage(1, 20);
|
||||||
|
val page3 = StatePlaylists.instance._historyDBStore.getPage(2, 20);
|
||||||
|
}
|
||||||
|
|
||||||
fun mainAppStartedWithExternalFiles(context: Context) {
|
fun mainAppStartedWithExternalFiles(context: Context) {
|
||||||
if(!Settings.instance.didFirstStart) {
|
if(!Settings.instance.didFirstStart) {
|
||||||
if(StateBackup.hasAutomaticBackup()) {
|
if(StateBackup.hasAutomaticBackup()) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.futo.platformplayer.states
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
|
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||||
import com.futo.platformplayer.R
|
import com.futo.platformplayer.R
|
||||||
import com.futo.platformplayer.api.media.PlatformID
|
import com.futo.platformplayer.api.media.PlatformID
|
||||||
import com.futo.platformplayer.api.media.exceptions.NoPlatformClientException
|
import com.futo.platformplayer.api.media.exceptions.NoPlatformClientException
|
||||||
|
@ -19,6 +20,8 @@ import com.futo.platformplayer.logging.Logger
|
||||||
import com.futo.platformplayer.models.HistoryVideo
|
import com.futo.platformplayer.models.HistoryVideo
|
||||||
import com.futo.platformplayer.models.Playlist
|
import com.futo.platformplayer.models.Playlist
|
||||||
import com.futo.platformplayer.stores.FragmentedStorage
|
import com.futo.platformplayer.stores.FragmentedStorage
|
||||||
|
import com.futo.platformplayer.stores.db.ManagedDBStore
|
||||||
|
import com.futo.platformplayer.stores.db.types.DBHistory
|
||||||
import com.futo.platformplayer.stores.v2.ManagedStore
|
import com.futo.platformplayer.stores.v2.ManagedStore
|
||||||
import com.futo.platformplayer.stores.v2.ReconstructStore
|
import com.futo.platformplayer.stores.v2.ReconstructStore
|
||||||
import kotlinx.serialization.encodeToString
|
import kotlinx.serialization.encodeToString
|
||||||
|
@ -26,6 +29,8 @@ import kotlinx.serialization.json.Json
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
import java.time.temporal.ChronoUnit
|
import java.time.temporal.ChronoUnit
|
||||||
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.ConcurrentMap
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Used to maintain playlists
|
* Used to maintain playlists
|
||||||
|
@ -50,6 +55,11 @@ class StatePlaylists {
|
||||||
.withRestore(PlaylistBackup())
|
.withRestore(PlaylistBackup())
|
||||||
.load();
|
.load();
|
||||||
|
|
||||||
|
val historyIndex: ConcurrentMap<Any, DBHistory.Index> = ConcurrentHashMap();
|
||||||
|
val _historyDBStore = ManagedDBStore.create("history", DBHistory.Descriptor())
|
||||||
|
.withIndex({ it.url }, historyIndex)
|
||||||
|
.load();
|
||||||
|
|
||||||
val playlistShareDir = FragmentedStorage.getOrCreateDirectory("shares");
|
val playlistShareDir = FragmentedStorage.getOrCreateDirectory("shares");
|
||||||
|
|
||||||
var onHistoricVideoChanged = Event2<IPlatformVideo, Long>();
|
var onHistoricVideoChanged = Event2<IPlatformVideo, Long>();
|
||||||
|
|
|
@ -2,6 +2,8 @@ package com.futo.platformplayer.stores
|
||||||
|
|
||||||
import com.futo.platformplayer.Settings
|
import com.futo.platformplayer.Settings
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
|
import com.futo.platformplayer.stores.db.ManagedDBIndex
|
||||||
|
import com.futo.platformplayer.stores.db.ManagedDBStore
|
||||||
import com.futo.platformplayer.stores.v2.JsonStoreSerializer
|
import com.futo.platformplayer.stores.v2.JsonStoreSerializer
|
||||||
import com.futo.platformplayer.stores.v2.ManagedStore
|
import com.futo.platformplayer.stores.v2.ManagedStore
|
||||||
import com.futo.platformplayer.stores.v2.StoreSerializer
|
import com.futo.platformplayer.stores.v2.StoreSerializer
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.futo.platformplayer.stores.db
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.Query
|
||||||
|
import androidx.room.Update
|
||||||
|
|
||||||
|
/*
|
||||||
|
@Dao
|
||||||
|
class ManagedDBContext<T, I: ManagedDBIndex<T>> {
|
||||||
|
|
||||||
|
fun get(id: Int): I;
|
||||||
|
fun gets(vararg id: Int): List<I>;
|
||||||
|
fun getAll(): List<I>;
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insert(index: I);
|
||||||
|
@Insert
|
||||||
|
fun insertAll(vararg indexes: I)
|
||||||
|
|
||||||
|
@Update
|
||||||
|
fun update(index: I);
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(index: I);
|
||||||
|
}*/
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.futo.platformplayer.stores.db
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.Update
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface ManagedDBContextPaged<T, I: ManagedDBIndex<T>> {
|
||||||
|
fun getPaged(page: Int, pageSize: Int): List<I>;
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.futo.platformplayer.stores.db
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.Query
|
||||||
|
import androidx.room.RawQuery
|
||||||
|
import androidx.room.Update
|
||||||
|
import androidx.sqlite.db.SupportSQLiteQuery
|
||||||
|
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface ManagedDBDAOBase<T, I: ManagedDBIndex<T>> {
|
||||||
|
|
||||||
|
@RawQuery
|
||||||
|
fun get(query: SupportSQLiteQuery): I;
|
||||||
|
@RawQuery
|
||||||
|
fun getMultiple(query: SupportSQLiteQuery): List<I>;
|
||||||
|
|
||||||
|
@RawQuery
|
||||||
|
fun action(query: SupportSQLiteQuery): Int
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun insert(index: I): Long;
|
||||||
|
@Insert
|
||||||
|
fun insertAll(vararg indexes: I)
|
||||||
|
|
||||||
|
@Update
|
||||||
|
fun update(index: I);
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(index: I);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.futo.platformplayer.stores.db
|
||||||
|
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
|
||||||
|
abstract class ManagedDBDatabase<T, I: ManagedDBIndex<T>, D: ManagedDBDAOBase<T, I>>: RoomDatabase() {
|
||||||
|
abstract fun base(): D;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.futo.platformplayer.stores.db
|
||||||
|
|
||||||
|
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||||
|
import com.futo.platformplayer.models.HistoryVideo
|
||||||
|
import com.futo.platformplayer.stores.db.types.DBHistory
|
||||||
|
|
||||||
|
|
||||||
|
abstract class ManagedDBDescriptor<T, I: ManagedDBIndex<T>, D: ManagedDBDatabase<T, I, DA>, DA: ManagedDBDAOBase<T, I>> {
|
||||||
|
abstract fun dbClass(): Class<D>;
|
||||||
|
abstract fun create(obj: T): I;
|
||||||
|
|
||||||
|
open val ordered: String? = null;
|
||||||
|
|
||||||
|
open fun sqlIndexOnly(tableName: String): SimpleSQLiteQuery? = null;
|
||||||
|
open fun sqlPage(tableName: String, page: Int, length: Int): SimpleSQLiteQuery? = null;
|
||||||
|
}
|
|
@ -1,8 +1,14 @@
|
||||||
package com.futo.platformplayer.stores.db
|
package com.futo.platformplayer.stores.db
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Ignore
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
|
import com.futo.platformplayer.api.media.Serializer
|
||||||
|
|
||||||
open class ManagedDBIndex(
|
interface ManagedDBIndex<T> {
|
||||||
@PrimaryKey(true)
|
var id: Long?
|
||||||
val id: Int? = null
|
var serialized: ByteArray?
|
||||||
)
|
|
||||||
|
@get:Ignore
|
||||||
|
var obj: T?;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.futo.platformplayer.stores.db
|
||||||
|
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Delete
|
||||||
|
import androidx.room.Insert
|
||||||
|
import androidx.room.Update
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface ManagedDBIndexOnly<T, I: ManagedDBIndex<T>> {
|
||||||
|
fun getIndex(): List<I>;
|
||||||
|
}
|
|
@ -1,28 +1,40 @@
|
||||||
package com.futo.platformplayer.stores.db
|
package com.futo.platformplayer.stores.db
|
||||||
|
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||||
import com.futo.platformplayer.assume
|
import com.futo.platformplayer.assume
|
||||||
import com.futo.platformplayer.stores.v2.ManagedStore
|
import com.futo.platformplayer.states.StateApp
|
||||||
import com.futo.platformplayer.stores.v2.ReconstructStore
|
import com.futo.platformplayer.stores.v2.JsonStoreSerializer
|
||||||
import com.futo.platformplayer.stores.v2.StoreSerializer
|
import com.futo.platformplayer.stores.v2.StoreSerializer
|
||||||
import java.io.File
|
import kotlinx.serialization.KSerializer
|
||||||
|
import java.util.concurrent.ConcurrentMap
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
|
|
||||||
class ManagedDBStore<I, T> {
|
class ManagedDBStore<I: ManagedDBIndex<T>, T, D: ManagedDBDatabase<T, I, DA>, DA: ManagedDBDAOBase<T, I>> {
|
||||||
private val _class: KType;
|
private val _class: KType;
|
||||||
private val _name: String;
|
private val _name: String;
|
||||||
private val _serializer: StoreSerializer<T>;
|
private val _serializer: StoreSerializer<T>;
|
||||||
|
|
||||||
|
private var _db: ManagedDBDatabase<T, I, *>? = null;
|
||||||
|
private var _dbDaoBase: ManagedDBDAOBase<T, I>? = null;
|
||||||
|
val dbDaoBase: ManagedDBDAOBase<T, I> get() = _dbDaoBase ?: throw IllegalStateException("Not initialized db [${name}]");
|
||||||
|
|
||||||
private var _isLoaded = false;
|
private var _dbDescriptor: ManagedDBDescriptor<T, I, D, DA>;
|
||||||
|
|
||||||
private var _withUnique: ((I) -> Any)? = null;
|
private val _sqlAll: SimpleSQLiteQuery;
|
||||||
|
private val _sqlDeleteAll: SimpleSQLiteQuery;
|
||||||
|
private var _sqlIndexed: SimpleSQLiteQuery? = null;
|
||||||
|
|
||||||
val className: String? get() = _class.classifier?.assume<KClass<*>>()?.simpleName;
|
val className: String? get() = _class.classifier?.assume<KClass<*>>()?.simpleName;
|
||||||
|
|
||||||
val name: String;
|
val name: String;
|
||||||
|
|
||||||
constructor(name: String, clazz: KType, serializer: StoreSerializer<T>, niceName: String? = null) {
|
private val _indexes: ArrayList<Pair<(I)->Any, ConcurrentMap<Any, I>>> = arrayListOf();
|
||||||
|
|
||||||
|
|
||||||
|
constructor(name: String, descriptor: ManagedDBDescriptor<T, I, D, DA>, clazz: KType, serializer: StoreSerializer<T>, niceName: String? = null) {
|
||||||
|
_dbDescriptor = descriptor;
|
||||||
_name = name;
|
_name = name;
|
||||||
this.name = niceName ?: name.let {
|
this.name = niceName ?: name.let {
|
||||||
if(it.isNotEmpty())
|
if(it.isNotEmpty())
|
||||||
|
@ -31,11 +43,119 @@ class ManagedDBStore<I, T> {
|
||||||
};
|
};
|
||||||
_serializer = serializer;
|
_serializer = serializer;
|
||||||
_class = clazz;
|
_class = clazz;
|
||||||
|
|
||||||
|
_sqlAll = SimpleSQLiteQuery("SELECT * FROM $_name" + if(descriptor.ordered.isNullOrEmpty()) "" else " ${descriptor.ordered}");
|
||||||
|
_sqlDeleteAll = SimpleSQLiteQuery("DELETE FROM ${_name}");
|
||||||
|
_sqlIndexed = descriptor.sqlIndexOnly(_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
fun load() {
|
fun withIndex(keySelector: (I)->Any, indexContainer: ConcurrentMap<Any, I>): ManagedDBStore<I, T, D, DA> {
|
||||||
throw NotImplementedError();
|
if(_sqlIndexed == null)
|
||||||
_isLoaded = true;
|
throw IllegalStateException("Can only create indexes if sqlIndexOnly is implemented");
|
||||||
|
_indexes.add(Pair(keySelector, indexContainer));
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun load(): ManagedDBStore<I, T, D, DA> {
|
||||||
|
_db = Room.databaseBuilder(StateApp.instance.context, _dbDescriptor.dbClass(), _name)
|
||||||
|
.fallbackToDestructiveMigration()
|
||||||
|
.allowMainThreadQueries()
|
||||||
|
.build()
|
||||||
|
_dbDaoBase = _db!!.base() as ManagedDBDAOBase<T, I>;
|
||||||
|
if(_indexes.any()) {
|
||||||
|
val allItems = _dbDaoBase!!.getMultiple(_sqlIndexed!!);
|
||||||
|
for(index in _indexes)
|
||||||
|
index.second.putAll(allItems.associateBy(index.first));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
fun insert(obj: T) {
|
||||||
|
val newIndex = _dbDescriptor.create(obj);
|
||||||
|
newIndex.serialized = serialize(obj);
|
||||||
|
newIndex.id = dbDaoBase.insert(newIndex);
|
||||||
|
newIndex.serialized = null;
|
||||||
|
|
||||||
|
if(!_indexes.isEmpty()) {
|
||||||
|
for (index in _indexes) {
|
||||||
|
val key = index.first(newIndex);
|
||||||
|
index.second.put(key, newIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun update(id: Long, obj: T) {
|
||||||
|
val newIndex = _dbDescriptor.create(obj);
|
||||||
|
newIndex.id = id;
|
||||||
|
newIndex.serialized = serialize(obj);
|
||||||
|
dbDaoBase.update(newIndex);
|
||||||
|
newIndex.serialized = null;
|
||||||
|
|
||||||
|
if(!_indexes.isEmpty()) {
|
||||||
|
for (index in _indexes) {
|
||||||
|
val key = index.first(newIndex);
|
||||||
|
index.second.put(key, newIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAllIndexes(): List<I> {
|
||||||
|
if(_sqlIndexed == null)
|
||||||
|
throw IllegalStateException("Can only create indexes if sqlIndexOnly is implemented");
|
||||||
|
return dbDaoBase.getMultiple(_sqlIndexed!!);
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAllObjects(): List<T> = convertObjects(getAll());
|
||||||
|
fun getAll(): List<I> {
|
||||||
|
return dbDaoBase.getMultiple(_sqlAll);
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getObject(id: Long) = convertObject(get(id));
|
||||||
|
fun get(id: Long): I {
|
||||||
|
return dbDaoBase.get(SimpleSQLiteQuery("SELECT * FROM $_name WHERE id = ?", arrayOf(id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAllObjects(vararg id: Long): List<T> = convertObjects(getAll(*id));
|
||||||
|
fun getAll(vararg id: Long): List<I> {
|
||||||
|
return dbDaoBase.getMultiple(SimpleSQLiteQuery("SELECT * FROM $_name WHERE id IN (?)", arrayOf(id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPageObjects(page: Int, length: Int): List<T> = convertObjects(getPage(page, length));
|
||||||
|
fun getPage(page: Int, length: Int): List<I> {
|
||||||
|
val query = _dbDescriptor.sqlPage(_name, page, length) ?: throw IllegalStateException("Paged db not setup for ${_name}");
|
||||||
|
return dbDaoBase.getMultiple(query);
|
||||||
|
}
|
||||||
|
fun delete(item: I) {
|
||||||
|
dbDaoBase.delete(item);
|
||||||
|
|
||||||
|
for(index in _indexes)
|
||||||
|
index.second.remove(index.first(item));
|
||||||
|
}
|
||||||
|
fun deleteAll() {
|
||||||
|
dbDaoBase.action(_sqlDeleteAll);
|
||||||
|
|
||||||
|
for(index in _indexes)
|
||||||
|
index.second.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun convertObject(index: ManagedDBIndex<T>): T? {
|
||||||
|
return index.serialized?.let {
|
||||||
|
_serializer.deserialize(_class, it);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fun convertObjects(indexes: List<ManagedDBIndex<T>>): List<T> {
|
||||||
|
return indexes.mapNotNull { convertObject(it) };
|
||||||
|
}
|
||||||
|
|
||||||
|
fun serialize(obj: T): ByteArray {
|
||||||
|
return _serializer.serialize(_class, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
inline fun <reified T, I: ManagedDBIndex<T>, D: ManagedDBDatabase<T, I, DA>, DA: ManagedDBDAOBase<T, I>> create(name: String, descriptor: ManagedDBDescriptor<T, I, D, DA>, serializer: KSerializer<T>? = null)
|
||||||
|
= ManagedDBStore(name, descriptor, kotlin.reflect.typeOf<T>(), JsonStoreSerializer.create(serializer));
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.futo.platformplayer.stores.db.types
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Ignore
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import com.futo.platformplayer.api.media.models.video.SerializedPlatformContent
|
||||||
|
import com.futo.platformplayer.models.HistoryVideo
|
||||||
|
import com.futo.platformplayer.stores.db.ManagedDBIndex
|
||||||
|
|
||||||
|
class DBChannelCache {
|
||||||
|
companion object {
|
||||||
|
const val TABLE_NAME = "channelCache";
|
||||||
|
}
|
||||||
|
|
||||||
|
class Index: ManagedDBIndex<SerializedPlatformContent> {
|
||||||
|
@PrimaryKey(true)
|
||||||
|
override var id: Long? = null;
|
||||||
|
@ColumnInfo(typeAffinity = ColumnInfo.BLOB)
|
||||||
|
override var serialized: ByteArray? = null;
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
override var obj: SerializedPlatformContent? = null;
|
||||||
|
|
||||||
|
var feedType: String? = null;
|
||||||
|
var channelUrl: String? = null;
|
||||||
|
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
constructor(sCache: SerializedPlatformContent) {
|
||||||
|
id = null;
|
||||||
|
serialized = null;
|
||||||
|
obj = sCache;
|
||||||
|
channelUrl = sCache.author.url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package com.futo.platformplayer.stores.db.types
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Dao
|
||||||
|
import androidx.room.Database
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.Ignore
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
import androidx.room.Query
|
||||||
|
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||||
|
import com.futo.platformplayer.api.media.models.video.SerializedPlatformContent
|
||||||
|
import com.futo.platformplayer.models.HistoryVideo
|
||||||
|
import com.futo.platformplayer.stores.db.ManagedDBDAOBase
|
||||||
|
import com.futo.platformplayer.stores.db.ManagedDBDatabase
|
||||||
|
import com.futo.platformplayer.stores.db.ManagedDBDescriptor
|
||||||
|
import com.futo.platformplayer.stores.db.ManagedDBIndex
|
||||||
|
import com.futo.platformplayer.stores.db.ManagedDBStore
|
||||||
|
import kotlin.reflect.KType
|
||||||
|
|
||||||
|
class DBHistory {
|
||||||
|
companion object {
|
||||||
|
const val TABLE_NAME = "history";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface DBDAO: ManagedDBDAOBase<HistoryVideo, Index> {}
|
||||||
|
@Database(entities = [Index::class], version = 2)
|
||||||
|
abstract class DB: ManagedDBDatabase<HistoryVideo, Index, DBDAO>() {
|
||||||
|
abstract override fun base(): DBDAO;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Descriptor: ManagedDBDescriptor<HistoryVideo, Index, DB, DBDAO>() {
|
||||||
|
override fun create(obj: HistoryVideo): Index = Index(obj);
|
||||||
|
override fun dbClass(): Class<DB> = DB::class.java;
|
||||||
|
|
||||||
|
//Optional
|
||||||
|
override fun sqlIndexOnly(tableName: String): SimpleSQLiteQuery = SimpleSQLiteQuery("SELECT id, url, position, date FROM $TABLE_NAME");
|
||||||
|
override fun sqlPage(tableName: String, page: Int, length: Int): SimpleSQLiteQuery = SimpleSQLiteQuery("SELECT * FROM $TABLE_NAME ORDER BY date DESC, id DESC LIMIT ? OFFSET ?", arrayOf(length, page * length));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(TABLE_NAME)
|
||||||
|
class Index: ManagedDBIndex<HistoryVideo> {
|
||||||
|
@PrimaryKey(true)
|
||||||
|
override var id: Long? = null;
|
||||||
|
@ColumnInfo(typeAffinity = ColumnInfo.BLOB)
|
||||||
|
override var serialized: ByteArray? = null;
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
override var obj: HistoryVideo? = null;
|
||||||
|
|
||||||
|
var url: String;
|
||||||
|
var position: Long;
|
||||||
|
var date: Long;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
url = "";
|
||||||
|
position = 0;
|
||||||
|
date = 0;
|
||||||
|
}
|
||||||
|
constructor(historyVideo: HistoryVideo) {
|
||||||
|
id = null;
|
||||||
|
serialized = null;
|
||||||
|
url = historyVideo.video.url;
|
||||||
|
position = historyVideo.position;
|
||||||
|
date = historyVideo.date.toEpochSecond();
|
||||||
|
obj = historyVideo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -92,7 +92,11 @@ class GestureControlView : LinearLayout {
|
||||||
override fun onDown(p0: MotionEvent): Boolean { return false; }
|
override fun onDown(p0: MotionEvent): Boolean { return false; }
|
||||||
override fun onShowPress(p0: MotionEvent) = Unit;
|
override fun onShowPress(p0: MotionEvent) = Unit;
|
||||||
override fun onSingleTapUp(p0: MotionEvent): Boolean { return false; }
|
override fun onSingleTapUp(p0: MotionEvent): Boolean { return false; }
|
||||||
override fun onScroll(p0: MotionEvent, p1: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
|
override fun onFling(p0: MotionEvent?, p1: MotionEvent, p2: Float, p3: Float): Boolean { return false; }
|
||||||
|
override fun onScroll(p0: MotionEvent?, p1: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
|
||||||
|
if(p0 == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (_isFullScreen && _adjustingBrightness) {
|
if (_isFullScreen && _adjustingBrightness) {
|
||||||
val adjustAmount = (distanceY * 2) / height;
|
val adjustAmount = (distanceY * 2) / height;
|
||||||
_brightnessFactor = (_brightnessFactor + adjustAmount).coerceAtLeast(0.0f).coerceAtMost(1.0f);
|
_brightnessFactor = (_brightnessFactor + adjustAmount).coerceAtLeast(0.0f).coerceAtMost(1.0f);
|
||||||
|
@ -132,8 +136,7 @@ class GestureControlView : LinearLayout {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
override fun onLongPress(p0: MotionEvent) = Unit;
|
override fun onLongPress(p0: MotionEvent) = Unit
|
||||||
override fun onFling(p0: MotionEvent, p1: MotionEvent, p2: Float, p3: Float): Boolean { return false; }
|
|
||||||
});
|
});
|
||||||
|
|
||||||
gestureController.setOnDoubleTapListener(object : GestureDetector.OnDoubleTapListener {
|
gestureController.setOnDoubleTapListener(object : GestureDetector.OnDoubleTapListener {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue