diff --git a/app/build.gradle b/app/build.gradle index 3dc122a3..8881e28b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -39,7 +39,7 @@ protobuf { android { namespace 'com.futo.platformplayer' - compileSdk 33 + compileSdk 34 flavorDimensions "buildType" productFlavors { stable { diff --git a/app/src/main/java/com/futo/platformplayer/states/StateApp.kt b/app/src/main/java/com/futo/platformplayer/states/StateApp.kt index c30a4311..8a098137 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StateApp.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StateApp.kt @@ -1,24 +1,18 @@ package com.futo.platformplayer.states -import android.Manifest import android.annotation.SuppressLint import android.app.Activity import android.content.Context import android.content.Intent import android.content.IntentFilter -import android.content.pm.PackageManager import android.media.AudioManager import android.net.ConnectivityManager import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkRequest import android.net.Uri -import android.os.Environment import android.provider.DocumentsContract 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.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope @@ -28,10 +22,9 @@ import com.futo.platformplayer.R import com.futo.platformplayer.activities.CaptchaActivity import com.futo.platformplayer.activities.IWithResultLauncher 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.JSClient -import com.futo.platformplayer.api.media.platforms.js.internal.JSHttpClient import com.futo.platformplayer.background.BackgroundWorker import com.futo.platformplayer.cache.ChannelContentCache 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.LogLevel import com.futo.platformplayer.logging.Logger +import com.futo.platformplayer.models.HistoryVideo import com.futo.platformplayer.receivers.AudioNoisyReceiver import com.futo.platformplayer.services.DownloadService import com.futo.platformplayer.stores.FragmentedStorage +import com.futo.platformplayer.stores.db.types.DBHistory import com.futo.platformplayer.stores.v2.ManagedStore -import com.stripe.android.core.utils.encodeToJson import kotlinx.coroutines.* -import kotlinx.serialization.encodeToString +import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import java.io.File import java.time.OffsetDateTime import java.util.* import java.util.concurrent.TimeUnit import kotlin.system.measureTimeMillis -import kotlin.time.measureTime /*** * This class contains global context for unconventional cases where obtaining context is hard. @@ -545,7 +538,73 @@ class StateApp { StateAnnouncement.instance.registerDidYouKnow(); 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(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? = 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? = 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) { if(!Settings.instance.didFirstStart) { if(StateBackup.hasAutomaticBackup()) { diff --git a/app/src/main/java/com/futo/platformplayer/states/StatePlaylists.kt b/app/src/main/java/com/futo/platformplayer/states/StatePlaylists.kt index b5086399..6a309ad7 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StatePlaylists.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StatePlaylists.kt @@ -3,6 +3,7 @@ package com.futo.platformplayer.states import android.content.Context import android.net.Uri import androidx.core.content.FileProvider +import androidx.sqlite.db.SimpleSQLiteQuery import com.futo.platformplayer.R import com.futo.platformplayer.api.media.PlatformID 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.Playlist 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.ReconstructStore import kotlinx.serialization.encodeToString @@ -26,6 +29,8 @@ import kotlinx.serialization.json.Json import java.io.File import java.time.OffsetDateTime import java.time.temporal.ChronoUnit +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ConcurrentMap /*** * Used to maintain playlists @@ -50,6 +55,11 @@ class StatePlaylists { .withRestore(PlaylistBackup()) .load(); + val historyIndex: ConcurrentMap = ConcurrentHashMap(); + val _historyDBStore = ManagedDBStore.create("history", DBHistory.Descriptor()) + .withIndex({ it.url }, historyIndex) + .load(); + val playlistShareDir = FragmentedStorage.getOrCreateDirectory("shares"); var onHistoricVideoChanged = Event2(); diff --git a/app/src/main/java/com/futo/platformplayer/stores/FragmentedStorage.kt b/app/src/main/java/com/futo/platformplayer/stores/FragmentedStorage.kt index a996a9c8..d439594e 100644 --- a/app/src/main/java/com/futo/platformplayer/stores/FragmentedStorage.kt +++ b/app/src/main/java/com/futo/platformplayer/stores/FragmentedStorage.kt @@ -2,6 +2,8 @@ package com.futo.platformplayer.stores import com.futo.platformplayer.Settings 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.ManagedStore import com.futo.platformplayer.stores.v2.StoreSerializer diff --git a/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBContext.kt b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBContext.kt new file mode 100644 index 00000000..bcbf9cfa --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBContext.kt @@ -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> { + + fun get(id: Int): I; + fun gets(vararg id: Int): List; + fun getAll(): List; + + @Insert + fun insert(index: I); + @Insert + fun insertAll(vararg indexes: I) + + @Update + fun update(index: I); + + @Delete + fun delete(index: I); +}*/ \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBContextPaged.kt b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBContextPaged.kt new file mode 100644 index 00000000..3449d3c7 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBContextPaged.kt @@ -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> { + fun getPaged(page: Int, pageSize: Int): List; +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBDAOBase.kt b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBDAOBase.kt new file mode 100644 index 00000000..a679bb16 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBDAOBase.kt @@ -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> { + + @RawQuery + fun get(query: SupportSQLiteQuery): I; + @RawQuery + fun getMultiple(query: SupportSQLiteQuery): List; + + @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); +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBDatabase.kt b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBDatabase.kt new file mode 100644 index 00000000..b1b73513 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBDatabase.kt @@ -0,0 +1,7 @@ +package com.futo.platformplayer.stores.db + +import androidx.room.RoomDatabase + +abstract class ManagedDBDatabase, D: ManagedDBDAOBase>: RoomDatabase() { + abstract fun base(): D; +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBDescriptor.kt b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBDescriptor.kt new file mode 100644 index 00000000..7800d649 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBDescriptor.kt @@ -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, D: ManagedDBDatabase, DA: ManagedDBDAOBase> { + abstract fun dbClass(): Class; + 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; +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBIndex.kt b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBIndex.kt index 6a06104c..78a30abf 100644 --- a/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBIndex.kt +++ b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBIndex.kt @@ -1,8 +1,14 @@ package com.futo.platformplayer.stores.db +import androidx.room.ColumnInfo +import androidx.room.Ignore import androidx.room.PrimaryKey +import com.futo.platformplayer.api.media.Serializer -open class ManagedDBIndex( - @PrimaryKey(true) - val id: Int? = null -) \ No newline at end of file +interface ManagedDBIndex { + var id: Long? + var serialized: ByteArray? + + @get:Ignore + var obj: T?; +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBIndexOnly.kt b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBIndexOnly.kt new file mode 100644 index 00000000..0795555d --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBIndexOnly.kt @@ -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> { + fun getIndex(): List; +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBStore.kt b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBStore.kt index 7fb8e56c..98eda24f 100644 --- a/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBStore.kt +++ b/app/src/main/java/com/futo/platformplayer/stores/db/ManagedDBStore.kt @@ -1,28 +1,40 @@ package com.futo.platformplayer.stores.db +import androidx.room.Room +import androidx.sqlite.db.SimpleSQLiteQuery import com.futo.platformplayer.assume -import com.futo.platformplayer.stores.v2.ManagedStore -import com.futo.platformplayer.stores.v2.ReconstructStore +import com.futo.platformplayer.states.StateApp +import com.futo.platformplayer.stores.v2.JsonStoreSerializer 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.KType -class ManagedDBStore { +class ManagedDBStore, T, D: ManagedDBDatabase, DA: ManagedDBDAOBase> { private val _class: KType; private val _name: String; private val _serializer: StoreSerializer; + private var _db: ManagedDBDatabase? = null; + private var _dbDaoBase: ManagedDBDAOBase? = null; + val dbDaoBase: ManagedDBDAOBase get() = _dbDaoBase ?: throw IllegalStateException("Not initialized db [${name}]"); - private var _isLoaded = false; + private var _dbDescriptor: ManagedDBDescriptor; - 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>()?.simpleName; val name: String; - constructor(name: String, clazz: KType, serializer: StoreSerializer, niceName: String? = null) { + private val _indexes: ArrayListAny, ConcurrentMap>> = arrayListOf(); + + + constructor(name: String, descriptor: ManagedDBDescriptor, clazz: KType, serializer: StoreSerializer, niceName: String? = null) { + _dbDescriptor = descriptor; _name = name; this.name = niceName ?: name.let { if(it.isNotEmpty()) @@ -31,11 +43,119 @@ class ManagedDBStore { }; _serializer = serializer; _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() { - throw NotImplementedError(); - _isLoaded = true; + fun withIndex(keySelector: (I)->Any, indexContainer: ConcurrentMap): ManagedDBStore { + if(_sqlIndexed == null) + throw IllegalStateException("Can only create indexes if sqlIndexOnly is implemented"); + _indexes.add(Pair(keySelector, indexContainer)); + + return this; } + + fun load(): ManagedDBStore { + _db = Room.databaseBuilder(StateApp.instance.context, _dbDescriptor.dbClass(), _name) + .fallbackToDestructiveMigration() + .allowMainThreadQueries() + .build() + _dbDaoBase = _db!!.base() as ManagedDBDAOBase; + 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 { + if(_sqlIndexed == null) + throw IllegalStateException("Can only create indexes if sqlIndexOnly is implemented"); + return dbDaoBase.getMultiple(_sqlIndexed!!); + } + + fun getAllObjects(): List = convertObjects(getAll()); + fun getAll(): List { + 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 = convertObjects(getAll(*id)); + fun getAll(vararg id: Long): List { + return dbDaoBase.getMultiple(SimpleSQLiteQuery("SELECT * FROM $_name WHERE id IN (?)", arrayOf(id))); + } + + fun getPageObjects(page: Int, length: Int): List = convertObjects(getPage(page, length)); + fun getPage(page: Int, length: Int): List { + 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? { + return index.serialized?.let { + _serializer.deserialize(_class, it); + }; + } + fun convertObjects(indexes: List>): List { + return indexes.mapNotNull { convertObject(it) }; + } + + fun serialize(obj: T): ByteArray { + return _serializer.serialize(_class, obj); + } + + companion object { + inline fun , D: ManagedDBDatabase, DA: ManagedDBDAOBase> create(name: String, descriptor: ManagedDBDescriptor, serializer: KSerializer? = null) + = ManagedDBStore(name, descriptor, kotlin.reflect.typeOf(), JsonStoreSerializer.create(serializer)); + } } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/stores/db/types/DBChannelCache.kt b/app/src/main/java/com/futo/platformplayer/stores/db/types/DBChannelCache.kt new file mode 100644 index 00000000..d6463ec2 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/stores/db/types/DBChannelCache.kt @@ -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 { + @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; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/stores/db/types/DBHistory.kt b/app/src/main/java/com/futo/platformplayer/stores/db/types/DBHistory.kt new file mode 100644 index 00000000..2b0d3301 --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/stores/db/types/DBHistory.kt @@ -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 {} + @Database(entities = [Index::class], version = 2) + abstract class DB: ManagedDBDatabase() { + abstract override fun base(): DBDAO; + } + + class Descriptor: ManagedDBDescriptor() { + override fun create(obj: HistoryVideo): Index = Index(obj); + override fun dbClass(): Class = 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 { + @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; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/views/behavior/GestureControlView.kt b/app/src/main/java/com/futo/platformplayer/views/behavior/GestureControlView.kt index c4990dce..35e65329 100644 --- a/app/src/main/java/com/futo/platformplayer/views/behavior/GestureControlView.kt +++ b/app/src/main/java/com/futo/platformplayer/views/behavior/GestureControlView.kt @@ -92,7 +92,11 @@ class GestureControlView : LinearLayout { override fun onDown(p0: MotionEvent): Boolean { return false; } override fun onShowPress(p0: MotionEvent) = Unit; 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) { val adjustAmount = (distanceY * 2) / height; _brightnessFactor = (_brightnessFactor + adjustAmount).coerceAtLeast(0.0f).coerceAtMost(1.0f); @@ -132,8 +136,7 @@ class GestureControlView : LinearLayout { return true; } - override fun onLongPress(p0: MotionEvent) = Unit; - override fun onFling(p0: MotionEvent, p1: MotionEvent, p2: Float, p3: Float): Boolean { return false; } + override fun onLongPress(p0: MotionEvent) = Unit }); gestureController.setOnDoubleTapListener(object : GestureDetector.OnDoubleTapListener {