mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2025-05-08 10:34:38 +02:00
Notification improvements, Polycentric subscription parallelization, Cache load parallelization
This commit is contained in:
parent
f8ee340499
commit
88ca90c13a
@ -2,14 +2,24 @@ package com.futo.platformplayer
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.webkit.CookieManager
|
import android.webkit.CookieManager
|
||||||
|
import androidx.work.Constraints
|
||||||
|
import androidx.work.Data
|
||||||
|
import androidx.work.ExistingPeriodicWorkPolicy
|
||||||
|
import androidx.work.NetworkType
|
||||||
|
import androidx.work.OneTimeWorkRequestBuilder
|
||||||
|
import androidx.work.PeriodicWorkRequest
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
import com.caoccao.javet.values.primitive.V8ValueInteger
|
import com.caoccao.javet.values.primitive.V8ValueInteger
|
||||||
import com.caoccao.javet.values.primitive.V8ValueString
|
import com.caoccao.javet.values.primitive.V8ValueString
|
||||||
|
import com.futo.platformplayer.activities.SettingsActivity
|
||||||
import com.futo.platformplayer.api.http.ManagedHttpClient
|
import com.futo.platformplayer.api.http.ManagedHttpClient
|
||||||
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
|
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
|
||||||
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.SourcePluginConfig
|
import com.futo.platformplayer.api.media.platforms.js.SourcePluginConfig
|
||||||
import com.futo.platformplayer.api.media.platforms.js.SourcePluginDescriptor
|
import com.futo.platformplayer.api.media.platforms.js.SourcePluginDescriptor
|
||||||
import com.futo.platformplayer.api.media.structures.IPager
|
import com.futo.platformplayer.api.media.structures.IPager
|
||||||
|
import com.futo.platformplayer.background.BackgroundWorker
|
||||||
import com.futo.platformplayer.engine.V8Plugin
|
import com.futo.platformplayer.engine.V8Plugin
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
import com.futo.platformplayer.serializers.FlexibleBooleanSerializer
|
import com.futo.platformplayer.serializers.FlexibleBooleanSerializer
|
||||||
@ -28,6 +38,8 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.*
|
import kotlinx.serialization.*
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.json.*
|
||||||
|
import java.util.UUID
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
import java.util.stream.IntStream.range
|
import java.util.stream.IntStream.range
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
@ -87,11 +99,23 @@ class SettingsDev : FragmentedStorageFileJson() {
|
|||||||
val cookieManager: CookieManager = CookieManager.getInstance()
|
val cookieManager: CookieManager = CookieManager.getInstance()
|
||||||
cookieManager.removeAllCookies(null);
|
cookieManager.removeAllCookies(null);
|
||||||
}
|
}
|
||||||
|
@FormField(R.string.test_background_worker, FieldForm.BUTTON,
|
||||||
|
R.string.test_background_worker_description, 3)
|
||||||
|
fun triggerBackgroundUpdate() {
|
||||||
|
val act = SettingsActivity.getActivity()!!;
|
||||||
|
UIDialogs.toast(SettingsActivity.getActivity()!!, "Starting test background worker");
|
||||||
|
|
||||||
|
val wm = WorkManager.getInstance(act);
|
||||||
|
val req = OneTimeWorkRequestBuilder<BackgroundWorker>()
|
||||||
|
.setInputData(Data.Builder().putBoolean("bypassMainCheck", true).build())
|
||||||
|
.build();
|
||||||
|
wm.enqueue(req);
|
||||||
|
}
|
||||||
|
|
||||||
@Contextual
|
@Contextual
|
||||||
@Transient
|
@Transient
|
||||||
@FormField(R.string.v8_benchmarks, FieldForm.GROUP,
|
@FormField(R.string.v8_benchmarks, FieldForm.GROUP,
|
||||||
R.string.various_benchmarks_using_the_integrated_v8_engine, 3)
|
R.string.various_benchmarks_using_the_integrated_v8_engine, 4)
|
||||||
val v8Benchmarks: V8Benchmarks = V8Benchmarks();
|
val v8Benchmarks: V8Benchmarks = V8Benchmarks();
|
||||||
class V8Benchmarks {
|
class V8Benchmarks {
|
||||||
@FormField(
|
@FormField(
|
||||||
@ -139,7 +163,7 @@ class SettingsDev : FragmentedStorageFileJson() {
|
|||||||
|
|
||||||
@FormField(
|
@FormField(
|
||||||
R.string.test_v8_communication_speed, FieldForm.BUTTON,
|
R.string.test_v8_communication_speed, FieldForm.BUTTON,
|
||||||
R.string.tests_v8_communication_speeds, 2
|
R.string.tests_v8_communication_speeds, 4
|
||||||
)
|
)
|
||||||
fun testV8RunSpeeds() {
|
fun testV8RunSpeeds() {
|
||||||
var plugin: V8Plugin? = null;
|
var plugin: V8Plugin? = null;
|
||||||
|
@ -4,6 +4,8 @@ import android.app.NotificationChannel
|
|||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
import android.media.MediaSession2Service.MediaNotification
|
import android.media.MediaSession2Service.MediaNotification
|
||||||
import androidx.concurrent.futures.CallbackToFutureAdapter
|
import androidx.concurrent.futures.CallbackToFutureAdapter
|
||||||
import androidx.concurrent.futures.ResolvableFuture
|
import androidx.concurrent.futures.ResolvableFuture
|
||||||
@ -11,8 +13,12 @@ import androidx.core.app.NotificationCompat
|
|||||||
import androidx.work.CoroutineWorker
|
import androidx.work.CoroutineWorker
|
||||||
import androidx.work.ListenableWorker
|
import androidx.work.ListenableWorker
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
|
import com.bumptech.glide.request.target.CustomTarget
|
||||||
|
import com.bumptech.glide.request.transition.Transition
|
||||||
import com.futo.platformplayer.activities.MainActivity
|
import com.futo.platformplayer.activities.MainActivity
|
||||||
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
|
import com.futo.platformplayer.api.media.models.contents.IPlatformContent
|
||||||
|
import com.futo.platformplayer.api.media.models.video.IPlatformVideo
|
||||||
import com.futo.platformplayer.getNowDiffSeconds
|
import com.futo.platformplayer.getNowDiffSeconds
|
||||||
import com.futo.platformplayer.logging.Logger
|
import com.futo.platformplayer.logging.Logger
|
||||||
import com.futo.platformplayer.models.Subscription
|
import com.futo.platformplayer.models.Subscription
|
||||||
@ -29,10 +35,10 @@ import kotlinx.coroutines.runBlocking
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
|
|
||||||
class BackgroundWorker(private val appContext: Context, workerParams: WorkerParameters) :
|
class BackgroundWorker(private val appContext: Context, private val workerParams: WorkerParameters) :
|
||||||
CoroutineWorker(appContext, workerParams) {
|
CoroutineWorker(appContext, workerParams) {
|
||||||
override suspend fun doWork(): Result {
|
override suspend fun doWork(): Result {
|
||||||
if(StateApp.instance.isMainActive) {
|
if(StateApp.instance.isMainActive && !inputData.getBoolean("bypassMainCheck", false)) {
|
||||||
Logger.i("BackgroundWorker", "CANCELLED");
|
Logger.i("BackgroundWorker", "CANCELLED");
|
||||||
return Result.success();
|
return Result.success();
|
||||||
}
|
}
|
||||||
@ -86,9 +92,10 @@ class BackgroundWorker(private val appContext: Context, workerParams: WorkerPara
|
|||||||
val newSubChanges = hashSetOf<Subscription>();
|
val newSubChanges = hashSetOf<Subscription>();
|
||||||
val newItems = mutableListOf<IPlatformContent>();
|
val newItems = mutableListOf<IPlatformContent>();
|
||||||
|
|
||||||
|
val now = OffsetDateTime.now();
|
||||||
val contentNotifs = mutableListOf<Pair<Subscription, IPlatformContent>>();
|
val contentNotifs = mutableListOf<Pair<Subscription, IPlatformContent>>();
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
StateSubscriptions.instance.getSubscriptionsFeedWithExceptions(true, false,this, { progress, total ->
|
val results = StateSubscriptions.instance.getSubscriptionsFeedWithExceptions(true, false,this, { progress, total ->
|
||||||
Logger.i("BackgroundWorker", "SUBSCRIPTION PROGRESS: ${progress}/${total}");
|
Logger.i("BackgroundWorker", "SUBSCRIPTION PROGRESS: ${progress}/${total}");
|
||||||
|
|
||||||
synchronized(manager) {
|
synchronized(manager) {
|
||||||
@ -103,29 +110,46 @@ class BackgroundWorker(private val appContext: Context, workerParams: WorkerPara
|
|||||||
synchronized(newSubChanges) {
|
synchronized(newSubChanges) {
|
||||||
if(!newSubChanges.contains(sub)) {
|
if(!newSubChanges.contains(sub)) {
|
||||||
newSubChanges.add(sub);
|
newSubChanges.add(sub);
|
||||||
if(sub.doNotifications)
|
if(sub.doNotifications && content.datetime?.let { it < now } == true)
|
||||||
contentNotifs.add(Pair(sub, content));
|
contentNotifs.add(Pair(sub, content));
|
||||||
}
|
}
|
||||||
newItems.add(content);
|
newItems.add(content);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Only for testing notifications
|
||||||
|
val testNotifs = 0;
|
||||||
|
if(contentNotifs.size == 0 && testNotifs > 0) {
|
||||||
|
results.first.getResults().filter { it is IPlatformVideo && it.datetime?.let { it < now } == true }
|
||||||
|
.take(testNotifs).forEach {
|
||||||
|
contentNotifs.add(Pair(StateSubscriptions.instance.getSubscriptions().first(), it));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
manager.cancel(12);
|
manager.cancel(12);
|
||||||
|
|
||||||
if(newItems.size > 0) {
|
if(contentNotifs.size > 0) {
|
||||||
try {
|
try {
|
||||||
val items = contentNotifs.take(5).toList()
|
val items = contentNotifs.take(5).toList()
|
||||||
for(i in items.indices) {
|
for(i in items.indices) {
|
||||||
val contentNotif = items.get(i);
|
val contentNotif = items.get(i);
|
||||||
manager.notify(13 + i, NotificationCompat.Builder(appContext, notificationChannel.id)
|
val thumbnail = if(contentNotif.second is IPlatformVideo) (contentNotif.second as IPlatformVideo).thumbnails.getHQThumbnail()
|
||||||
.setSmallIcon(com.futo.platformplayer.R.drawable.foreground)
|
else null;
|
||||||
.setContentTitle("New video by [${contentNotif.first.channel.name}]")
|
if(thumbnail != null)
|
||||||
.setContentText("${contentNotif.second.name}")
|
Glide.with(appContext).asBitmap()
|
||||||
.setSilent(true)
|
.load(thumbnail)
|
||||||
.setContentIntent(PendingIntent.getActivity(this.appContext, 0, MainActivity.getVideoIntent(this.appContext, contentNotif.second.url),
|
.into(object: CustomTarget<Bitmap>() {
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE))
|
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
|
||||||
.setChannelId(notificationChannel.id).build());
|
notifyNewContent(manager, notificationChannel, 13 + i, contentNotif.first, contentNotif.second, resource);
|
||||||
|
}
|
||||||
|
override fun onLoadCleared(placeholder: Drawable?) {}
|
||||||
|
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||||
|
notifyNewContent(manager, notificationChannel, 13 + i, contentNotif.first, contentNotif.second, null);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
else
|
||||||
|
notifyNewContent(manager, notificationChannel, 13 + i, contentNotif.first, contentNotif.second, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(ex: Throwable) {
|
catch(ex: Throwable) {
|
||||||
@ -140,4 +164,20 @@ class BackgroundWorker(private val appContext: Context, workerParams: WorkerPara
|
|||||||
.setSilent(true)
|
.setSilent(true)
|
||||||
.setChannelId(notificationChannel.id).build());*/
|
.setChannelId(notificationChannel.id).build());*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun notifyNewContent(manager: NotificationManager, notificationChannel: NotificationChannel, id: Int, sub: Subscription, content: IPlatformContent, thumbnail: Bitmap? = null) {
|
||||||
|
val notifBuilder = NotificationCompat.Builder(appContext, notificationChannel.id)
|
||||||
|
.setSmallIcon(com.futo.platformplayer.R.drawable.foreground)
|
||||||
|
.setContentTitle("New by [${sub.channel.name}]")
|
||||||
|
.setContentText("${content.name}")
|
||||||
|
.setSilent(true)
|
||||||
|
.setContentIntent(PendingIntent.getActivity(this.appContext, 0, MainActivity.getVideoIntent(this.appContext, content.url),
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE))
|
||||||
|
.setChannelId(notificationChannel.id);
|
||||||
|
if(thumbnail != null) {
|
||||||
|
//notifBuilder.setLargeIcon(thumbnail);
|
||||||
|
notifBuilder.setStyle(NotificationCompat.BigPictureStyle().bigPicture(thumbnail).bigLargeIcon(null as Bitmap?));
|
||||||
|
}
|
||||||
|
manager.notify(id, notifBuilder.build());
|
||||||
|
}
|
||||||
}
|
}
|
@ -18,10 +18,11 @@ import kotlinx.coroutines.CoroutineScope
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
|
import kotlin.streams.toList
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
class ChannelContentCache {
|
class ChannelContentCache {
|
||||||
private val _targetCacheSize = 2000;
|
private val _targetCacheSize = 3000;
|
||||||
val _channelCacheDir = FragmentedStorage.getOrCreateDirectory("channelCache");
|
val _channelCacheDir = FragmentedStorage.getOrCreateDirectory("channelCache");
|
||||||
val _channelContents: HashMap<String, ManagedStore<SerializedPlatformContent>>;
|
val _channelContents: HashMap<String, ManagedStore<SerializedPlatformContent>>;
|
||||||
init {
|
init {
|
||||||
@ -29,11 +30,11 @@ class ChannelContentCache {
|
|||||||
val initializeTime = measureTimeMillis {
|
val initializeTime = measureTimeMillis {
|
||||||
_channelContents = HashMap(allFiles
|
_channelContents = HashMap(allFiles
|
||||||
.filter { it.isDirectory }
|
.filter { it.isDirectory }
|
||||||
.associate {
|
.parallelStream().map {
|
||||||
Pair(it.name, FragmentedStorage.storeJson(_channelCacheDir, it.name, PlatformContentSerializer())
|
Pair(it.name, FragmentedStorage.storeJson(_channelCacheDir, it.name, PlatformContentSerializer())
|
||||||
.withoutBackup()
|
.withoutBackup()
|
||||||
.load())
|
.load())
|
||||||
});
|
}.toList().associate { it })
|
||||||
}
|
}
|
||||||
val minDays = OffsetDateTime.now().minusDays(10);
|
val minDays = OffsetDateTime.now().minusDays(10);
|
||||||
val totalItems = _channelContents.map { it.value.count() }.sum();
|
val totalItems = _channelContents.map { it.value.count() }.sum();
|
||||||
|
@ -122,6 +122,7 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
|
|||||||
|
|
||||||
_toolbarContentView = findViewById(R.id.container_toolbar_content);
|
_toolbarContentView = findViewById(R.id.container_toolbar_content);
|
||||||
|
|
||||||
|
var filteredNextPageCounter = 0;
|
||||||
_nextPageHandler = TaskHandler<TPager, List<TResult>>({fragment.lifecycleScope}, {
|
_nextPageHandler = TaskHandler<TPager, List<TResult>>({fragment.lifecycleScope}, {
|
||||||
if (it is IAsyncPager<*>)
|
if (it is IAsyncPager<*>)
|
||||||
it.nextPageAsync();
|
it.nextPageAsync();
|
||||||
@ -141,10 +142,15 @@ abstract class FeedView<TFragment, TResult, TConverted, TPager, TViewHolder> : L
|
|||||||
val filteredResults = filterResults(it);
|
val filteredResults = filterResults(it);
|
||||||
recyclerData.results.addAll(filteredResults);
|
recyclerData.results.addAll(filteredResults);
|
||||||
recyclerData.resultsUnfiltered.addAll(it);
|
recyclerData.resultsUnfiltered.addAll(it);
|
||||||
if(filteredResults.isEmpty())
|
if(filteredResults.isEmpty()) {
|
||||||
loadNextPage()
|
filteredNextPageCounter++
|
||||||
else
|
if(filteredNextPageCounter <= 4)
|
||||||
|
loadNextPage()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
filteredNextPageCounter = 0;
|
||||||
recyclerData.adapter.notifyItemRangeInserted(recyclerData.adapter.childToParentPosition(posBefore), filteredResults.size);
|
recyclerData.adapter.notifyItemRangeInserted(recyclerData.adapter.childToParentPosition(posBefore), filteredResults.size);
|
||||||
|
}
|
||||||
}.exception<Throwable> {
|
}.exception<Throwable> {
|
||||||
Logger.w(TAG, "Failed to load next page.", it);
|
Logger.w(TAG, "Failed to load next page.", it);
|
||||||
UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_next_page), it, {
|
UIDialogs.showGeneralRetryErrorDialog(context, context.getString(R.string.failed_to_load_next_page), it, {
|
||||||
|
@ -39,6 +39,7 @@ import java.util.concurrent.ForkJoinTask
|
|||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.coroutines.resumeWithException
|
import kotlin.coroutines.resumeWithException
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
import kotlin.streams.toList
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
/***
|
/***
|
||||||
@ -250,12 +251,12 @@ class StateSubscriptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val usePolycentric = true;
|
val usePolycentric = true;
|
||||||
val subUrls = getSubscriptions().associateWith {
|
val subUrls = getSubscriptions().parallelStream().map {
|
||||||
if(usePolycentric)
|
if(usePolycentric)
|
||||||
StatePolycentric.instance.getChannelUrls(it.channel.url, it.channel.id);
|
Pair(it, StatePolycentric.instance.getChannelUrls(it.channel.url, it.channel.id));
|
||||||
else
|
else
|
||||||
listOf(it.channel.url);
|
Pair(it, listOf(it.channel.url));
|
||||||
};
|
}.toList().associate { it };
|
||||||
|
|
||||||
val result = algo.getSubscriptions(subUrls);
|
val result = algo.getSubscriptions(subUrls);
|
||||||
return Pair(result.pager, result.exceptions);
|
return Pair(result.pager, result.exceptions);
|
||||||
|
@ -276,6 +276,8 @@
|
|||||||
<string name="change_the_external_storage_for_download_files">Change the external storage for download files</string>
|
<string name="change_the_external_storage_for_download_files">Change the external storage for download files</string>
|
||||||
<string name="clear_cookies">Clear Cookies</string>
|
<string name="clear_cookies">Clear Cookies</string>
|
||||||
<string name="clear_cookies_on_logout">Clear Cookies on Logout</string>
|
<string name="clear_cookies_on_logout">Clear Cookies on Logout</string>
|
||||||
|
<string name="test_background_worker">Test Background Worker</string>
|
||||||
|
<string name="test_background_worker_description"></string>
|
||||||
<string name="clear_payment">Clear Payment</string>
|
<string name="clear_payment">Clear Payment</string>
|
||||||
<string name="clears_cookies_when_you_log_out">Clears cookies when you log out</string>
|
<string name="clears_cookies_when_you_log_out">Clears cookies when you log out</string>
|
||||||
<string name="clears_in_app_browser_cookies">Clears in-app browser cookies</string>
|
<string name="clears_in_app_browser_cookies">Clears in-app browser cookies</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user