Merge branch 'master' of gitlab.futo.org:videostreaming/grayjay

This commit is contained in:
Kelvin 2024-01-19 17:28:45 +01:00
commit 4ff72dcdbb
13 changed files with 133 additions and 37 deletions

View file

@ -13,6 +13,7 @@
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>
<uses-permission android:name="android.permission.WRITE_SETTINGS" tools:ignore="ProtectedPermissions"/>
<application <application
android:allowBackup="true" android:allowBackup="true"

View file

@ -823,7 +823,7 @@ class Settings : FragmentedStorageFileJson() {
var toggleFullscreen: Boolean = true; var toggleFullscreen: Boolean = true;
@FormField(R.string.system_brightness, FieldForm.TOGGLE, R.string.system_brightness_descr, 4) @FormField(R.string.system_brightness, FieldForm.TOGGLE, R.string.system_brightness_descr, 4)
var useSystemBrightness: Boolean = true; var useSystemBrightness: Boolean = false;
@FormField(R.string.system_volume, FieldForm.TOGGLE, R.string.system_volume_descr, 5) @FormField(R.string.system_volume, FieldForm.TOGGLE, R.string.system_volume_descr, 5)
var useSystemVolume: Boolean = true; var useSystemVolume: Boolean = true;

View file

@ -304,12 +304,16 @@ class UIDialogs {
showDialog(context, R.drawable.ic_error, text, null, null, 0, cancelButtonAction, confirmButtonAction) showDialog(context, R.drawable.ic_error, text, null, null, 0, cancelButtonAction, confirmButtonAction)
} }
fun showUpdateAvailableDialog(context: Context, lastVersion: Int) { fun showUpdateAvailableDialog(context: Context, lastVersion: Int, hideExceptionButtons: Boolean = false) {
val dialog = AutoUpdateDialog(context); val dialog = AutoUpdateDialog(context);
registerDialogOpened(dialog); registerDialogOpened(dialog);
dialog.setOnDismissListener { registerDialogClosed(dialog) }; dialog.setOnDismissListener { registerDialogClosed(dialog) };
dialog.show(); dialog.show();
dialog.setMaxVersion(lastVersion); dialog.setMaxVersion(lastVersion);
if (hideExceptionButtons) {
dialog.hideExceptionButtons()
}
} }
fun showChangelogDialog(context: Context, lastVersion: Int) { fun showChangelogDialog(context: Context, lastVersion: Int) {

View file

@ -4,6 +4,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.View
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
@ -15,6 +16,7 @@ import com.futo.platformplayer.logging.LogLevel
import com.futo.platformplayer.logging.Logging import com.futo.platformplayer.logging.Logging
import com.futo.platformplayer.setNavigationBarColorAndIcons import com.futo.platformplayer.setNavigationBarColorAndIcons
import com.futo.platformplayer.states.StateApp import com.futo.platformplayer.states.StateApp
import com.futo.platformplayer.states.StateUpdate
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -28,6 +30,7 @@ class ExceptionActivity : AppCompatActivity() {
private lateinit var _buttonSubmit: LinearLayout; private lateinit var _buttonSubmit: LinearLayout;
private lateinit var _buttonRestart: LinearLayout; private lateinit var _buttonRestart: LinearLayout;
private lateinit var _buttonClose: LinearLayout; private lateinit var _buttonClose: LinearLayout;
private lateinit var _buttonCheckForUpdates: LinearLayout;
private var _file: File? = null; private var _file: File? = null;
private var _submitted = false; private var _submitted = false;
@ -45,6 +48,7 @@ class ExceptionActivity : AppCompatActivity() {
_buttonSubmit = findViewById(R.id.button_submit); _buttonSubmit = findViewById(R.id.button_submit);
_buttonRestart = findViewById(R.id.button_restart); _buttonRestart = findViewById(R.id.button_restart);
_buttonClose = findViewById(R.id.button_close); _buttonClose = findViewById(R.id.button_close);
_buttonCheckForUpdates = findViewById(R.id.button_check_for_updates);
val context = intent.getStringExtra(EXTRA_CONTEXT) ?: getString(R.string.unknown_context); val context = intent.getStringExtra(EXTRA_CONTEXT) ?: getString(R.string.unknown_context);
val stack = intent.getStringExtra(EXTRA_STACK) ?: getString(R.string.something_went_wrong_missing_stack_trace); val stack = intent.getStringExtra(EXTRA_STACK) ?: getString(R.string.something_went_wrong_missing_stack_trace);
@ -83,6 +87,17 @@ class ExceptionActivity : AppCompatActivity() {
_buttonClose.setOnClickListener { _buttonClose.setOnClickListener {
finish(); finish();
}; };
if (!BuildConfig.IS_PLAYSTORE_BUILD) {
_buttonCheckForUpdates.visibility = View.VISIBLE
_buttonCheckForUpdates.setOnClickListener {
lifecycleScope.launch(Dispatchers.IO) {
StateUpdate.instance.checkForUpdates(this@ExceptionActivity, true, true)
}
}
} else {
_buttonCheckForUpdates.visibility = View.GONE
}
} }
private fun submitFile() { private fun submitFile() {

View file

@ -96,6 +96,11 @@ class AutoUpdateDialog(context: Context?) : AlertDialog(context) {
Logger.i(TAG, "Cleared InstallReceiver.onReceiveResult handler.") Logger.i(TAG, "Cleared InstallReceiver.onReceiveResult handler.")
} }
fun hideExceptionButtons() {
_buttonNever.visibility = View.GONE
_buttonShowChangelog.visibility = View.GONE
}
private fun update() { private fun update() {
_buttonShowChangelog.visibility = Button.GONE; _buttonShowChangelog.visibility = Button.GONE;
_buttonNever.visibility = Button.GONE; _buttonNever.visibility = Button.GONE;

View file

@ -246,12 +246,15 @@ class MenuBottomBarFragment : MainActivityFragment() {
fun updateAllButtonVisibility() { fun updateAllButtonVisibility() {
val defs = currentButtonDefinitions?.toMutableList() ?: return val defs = currentButtonDefinitions?.toMutableList() ?: return
val metrics = StateApp.instance.displayMetrics ?: resources.displayMetrics; val metrics = StateApp.instance.displayMetrics ?: resources.displayMetrics;
_buttonsVisible = floor(metrics.widthPixels.toDouble() / 65.dp(resources).toDouble()).roundToInt(); _buttonsVisible = floor(metrics.widthPixels.toDouble() / 65.dp(resources).toDouble()).roundToInt() - 1;
if (_buttonsVisible - 1 >= defs.size) { if (_buttonsVisible >= defs.size) {
updateBottomMenuButtons(defs.toMutableList(), false); updateBottomMenuButtons(defs.toMutableList(), false);
} else if (_buttonsVisible > 0) {
updateBottomMenuButtons(defs.take(_buttonsVisible - 1).toMutableList(), true);
updateMoreButtons(defs.drop(_buttonsVisible - 1).toMutableList());
} else { } else {
updateBottomMenuButtons(defs.slice(IntRange(0, _buttonsVisible - 2)).toMutableList(), true); updateBottomMenuButtons(mutableListOf(), false)
updateMoreButtons(defs.slice(IntRange(_buttonsVisible - 1, defs.size - 1)).toMutableList()); updateMoreButtons(defs.toMutableList())
} }
} }

View file

@ -4,14 +4,19 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.logging.Logger
import com.futo.platformplayer.states.StateApp
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class AudioNoisyReceiver : BroadcastReceiver() { class AudioNoisyReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) { override fun onReceive(context: Context?, intent: Intent?) {
StateApp.instance.scopeOrNull?.launch(Dispatchers.Main) {
Logger.i(TAG, "Audio Noisy received"); Logger.i(TAG, "Audio Noisy received");
MediaControlReceiver.onPauseReceived.emit(); MediaControlReceiver.onPauseReceived.emit();
} }
}
companion object { companion object {
private val TAG = "AudioNoisyReceiver" private val TAG = "AudioNoisyReceiver"

View file

@ -13,6 +13,7 @@ import android.net.NetworkRequest
import android.net.Uri import android.net.Uri
import android.provider.DocumentsContract import android.provider.DocumentsContract
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.util.Log
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
@ -473,7 +474,11 @@ class StateApp {
Logger.i(TAG, "MainApp Started: Initialize [Noisy]"); Logger.i(TAG, "MainApp Started: Initialize [Noisy]");
_receiverBecomingNoisy?.let { _receiverBecomingNoisy?.let {
_receiverBecomingNoisy = null; _receiverBecomingNoisy = null;
try {
context.unregisterReceiver(it); context.unregisterReceiver(it);
} catch (e: Throwable) {
Log.e(TAG, "Failed to unregister receiver.", e)
}
} }
_receiverBecomingNoisy = AudioNoisyReceiver(); _receiverBecomingNoisy = AudioNoisyReceiver();
context.registerReceiver(_receiverBecomingNoisy, IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)); context.registerReceiver(_receiverBecomingNoisy, IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
@ -639,7 +644,11 @@ class StateApp {
Logger.i(TAG, "App ended"); Logger.i(TAG, "App ended");
_receiverBecomingNoisy?.let { _receiverBecomingNoisy?.let {
_receiverBecomingNoisy = null; _receiverBecomingNoisy = null;
try {
context.unregisterReceiver(it); context.unregisterReceiver(it);
} catch (e: Throwable) {
Log.e(TAG, "Failed to unregister receiver.", e)
}
} }
Logger.i(TAG, "Unregistered network callback on connectivityManager.") Logger.i(TAG, "Unregistered network callback on connectivityManager.")

View file

@ -155,7 +155,7 @@ class StateUpdate {
} }
} }
suspend fun checkForUpdates(context: Context, showUpToDateToast: Boolean) = withContext(Dispatchers.IO) { suspend fun checkForUpdates(context: Context, showUpToDateToast: Boolean, hideExceptionButtons: Boolean = false) = withContext(Dispatchers.IO) {
try { try {
val client = ManagedHttpClient(); val client = ManagedHttpClient();
val latestVersion = downloadVersionCode(client); val latestVersion = downloadVersionCode(client);
@ -167,7 +167,7 @@ class StateUpdate {
if (latestVersion > currentVersion) { if (latestVersion > currentVersion) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
try { try {
UIDialogs.showUpdateAvailableDialog(context, latestVersion); UIDialogs.showUpdateAvailableDialog(context, latestVersion, hideExceptionButtons);
} catch (e: Throwable) { } catch (e: Throwable) {
UIDialogs.toast(context, "Failed to show update dialog"); UIDialogs.toast(context, "Failed to show update dialog");
Logger.w(TAG, "Error occurred in update dialog."); Logger.w(TAG, "Error occurred in update dialog.");

View file

@ -3,12 +3,12 @@ package com.futo.platformplayer.views.behavior
import android.animation.Animator import android.animation.Animator
import android.animation.AnimatorSet import android.animation.AnimatorSet
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.app.Activity
import android.content.Context import android.content.Context
import android.graphics.Matrix import android.graphics.Matrix
import android.graphics.drawable.Animatable import android.graphics.drawable.Animatable
import android.media.AudioManager import android.media.AudioManager
import android.util.AttributeSet import android.util.AttributeSet
import android.util.Log
import android.view.GestureDetector import android.view.GestureDetector
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent import android.view.MotionEvent
@ -24,6 +24,7 @@ import androidx.core.animation.doOnStart
import androidx.core.view.GestureDetectorCompat import androidx.core.view.GestureDetectorCompat
import com.futo.platformplayer.R import com.futo.platformplayer.R
import com.futo.platformplayer.Settings import com.futo.platformplayer.Settings
import com.futo.platformplayer.UIDialogs
import com.futo.platformplayer.constructs.Event0 import com.futo.platformplayer.constructs.Event0
import com.futo.platformplayer.constructs.Event1 import com.futo.platformplayer.constructs.Event1
import com.futo.platformplayer.constructs.Event2 import com.futo.platformplayer.constructs.Event2
@ -67,6 +68,7 @@ class GestureControlView : LinearLayout {
private var _animatorSound: ObjectAnimator? = null; private var _animatorSound: ObjectAnimator? = null;
private var _brightnessFactor = 1.0f; private var _brightnessFactor = 1.0f;
private var _originalBrightnessFactor = 1.0f; private var _originalBrightnessFactor = 1.0f;
private var _originalBrightnessMode: Int = 0;
private var _adjustingBrightness: Boolean = false; private var _adjustingBrightness: Boolean = false;
private val _layoutControlsBrightness: FrameLayout; private val _layoutControlsBrightness: FrameLayout;
private val _progressBrightness: CircularProgressBar; private val _progressBrightness: CircularProgressBar;
@ -168,8 +170,6 @@ class GestureControlView : LinearLayout {
if(p0 == null) if(p0 == null)
return false; return false;
Logger.i(TAG, "p0.pointerCount: " + p0.pointerCount)
if (!_isPanning && p1.pointerCount == 1) { if (!_isPanning && p1.pointerCount == 1) {
val minDistance = Math.min(width, height) val minDistance = Math.min(width, height)
if (_isFullScreen && _adjustingBrightness) { if (_isFullScreen && _adjustingBrightness) {
@ -739,16 +739,25 @@ class GestureControlView : LinearLayout {
resetZoomPan() resetZoomPan()
if (isFullScreen) { if (isFullScreen) {
val c = context if (Settings.instance.gestureControls.useSystemBrightness) {
if (c is Activity && Settings.instance.gestureControls.useSystemBrightness) { try {
_brightnessFactor = c.window.attributes.screenBrightness _originalBrightnessMode = android.provider.Settings.System.getInt(context.contentResolver, android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE)
if (_brightnessFactor == -1.0f) {
_brightnessFactor = android.provider.Settings.System.getInt( val brightness = android.provider.Settings.System.getInt(context.contentResolver, android.provider.Settings.System.SCREEN_BRIGHTNESS)
context.contentResolver, _brightnessFactor = brightness / 255.0f;
android.provider.Settings.System.SCREEN_BRIGHTNESS Log.i(TAG, "Starting brightness brightness: $brightness, _brightnessFactor: $_brightnessFactor, _originalBrightnessMode: $_originalBrightnessMode")
) / 255.0f;
}
_originalBrightnessFactor = _brightnessFactor _originalBrightnessFactor = _brightnessFactor
android.provider.Settings.System.putInt(context.contentResolver, android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE, android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
} catch (e: Throwable) {
Settings.instance.gestureControls.useSystemBrightness = false
Settings.instance.save()
UIDialogs.toast(context, "useSystemBrightness disabled due to an error")
}
}
if (!Settings.instance.gestureControls.useSystemBrightness) {
_brightnessFactor = 1.0f;
} }
if (Settings.instance.gestureControls.useSystemVolume) { if (Settings.instance.gestureControls.useSystemVolume) {
@ -761,10 +770,19 @@ class GestureControlView : LinearLayout {
onBrightnessAdjusted.emit(_brightnessFactor); onBrightnessAdjusted.emit(_brightnessFactor);
onSoundAdjusted.emit(_soundFactor); onSoundAdjusted.emit(_soundFactor);
} else { } else {
val c = context if (Settings.instance.gestureControls.useSystemBrightness) {
if (c is Activity && Settings.instance.gestureControls.useSystemBrightness) {
if (Settings.instance.gestureControls.restoreSystemBrightness) { if (Settings.instance.gestureControls.restoreSystemBrightness) {
onBrightnessAdjusted.emit(_originalBrightnessFactor); onBrightnessAdjusted.emit(_originalBrightnessFactor)
if (android.provider.Settings.System.canWrite(context)) {
Log.i(TAG, "Restoring system brightness mode _originalBrightnessMode: $_originalBrightnessMode")
android.provider.Settings.System.putInt(
context.contentResolver,
android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE,
_originalBrightnessMode
)
}
} }
} else { } else {
onBrightnessAdjusted.emit(1.0f); onBrightnessAdjusted.emit(1.0f);

View file

@ -1,18 +1,18 @@
package com.futo.platformplayer.views.video package com.futo.platformplayer.views.video
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent
import android.content.res.Resources import android.content.res.Resources
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.media.AudioManager import android.media.AudioManager
import android.net.Uri
import android.util.AttributeSet import android.util.AttributeSet
import android.util.Log import android.util.Log
import android.util.TypedValue import android.util.TypedValue
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.WindowManager
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.TextView import android.widget.TextView
@ -122,6 +122,7 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
private var _currentChapterLoopActive = false; private var _currentChapterLoopActive = false;
private var _currentChapterLoopId: Int = 0; private var _currentChapterLoopId: Int = 0;
private var _currentChapter: IChapter? = null; private var _currentChapter: IChapter? = null;
private var _promptedForPermissions: Boolean = false;
//Events //Events
@ -249,11 +250,8 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
}; };
gestureControl.onToggleFullscreen.subscribe { setFullScreen(!isFullScreen) }; gestureControl.onToggleFullscreen.subscribe { setFullScreen(!isFullScreen) };
gestureControl.onBrightnessAdjusted.subscribe { gestureControl.onBrightnessAdjusted.subscribe {
if (context is Activity && Settings.instance.gestureControls.useSystemBrightness) { if (Settings.instance.gestureControls.useSystemBrightness) {
val window = context.window setSystemBrightness(it)
val layout: WindowManager.LayoutParams = window.attributes
layout.screenBrightness = it
window.attributes = layout
} else { } else {
if (it == 1.0f) { if (it == 1.0f) {
_overlay_brightness.visibility = View.GONE; _overlay_brightness.visibility = View.GONE;
@ -433,6 +431,30 @@ class FutoVideoPlayer : FutoVideoPlayerBase {
} }
} }
private fun setSystemBrightness(brightness: Float) {
Log.i(TAG, "setSystemBrightness $brightness")
if (android.provider.Settings.System.canWrite(context)) {
Log.i(TAG, "setSystemBrightness canWrite $brightness")
android.provider.Settings.System.putInt(context.contentResolver, android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE, android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
android.provider.Settings.System.putInt(context.contentResolver, android.provider.Settings.System.SCREEN_BRIGHTNESS, (brightness * 255.0f).toInt().coerceAtLeast(1).coerceAtMost(255));
} else if (!_promptedForPermissions) {
Log.i(TAG, "setSystemBrightness prompt $brightness")
_promptedForPermissions = true
UIDialogs.showConfirmationDialog(context, "System brightness controls require explicit permission", action = {
openAndroidPermissionsMenu()
})
} else {
Log.i(TAG, "setSystemBrightness no permission?")
//No permissions but already prompted, ignore
}
}
private fun openAndroidPermissionsMenu() {
val intent = Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS)
intent.setData(Uri.parse("package:" + context.packageName))
context.startActivity(intent)
}
fun updateNextPrevious() { fun updateNextPrevious() {
val vidPrev = StatePlayer.instance.getPrevQueueItem(true); val vidPrev = StatePlayer.instance.getPrevQueueItem(true);
val vidNext = StatePlayer.instance.getNextQueueItem(true); val vidNext = StatePlayer.instance.getNextQueueItem(true);

View file

@ -37,9 +37,22 @@
android:fontFamily="@font/inter_extra_light" /> android:fontFamily="@font/inter_extra_light" />
</FrameLayout> </FrameLayout>
<Space <LinearLayout
android:layout_width="20dp" android:id="@+id/button_check_for_updates"
android:layout_height="match_parent" /> android:layout_width="wrap_content"
android:layout_height="match_parent"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:gravity="center"
android:background="@drawable/background_button_primary_round_4dp"
android:layout_gravity="center_horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAlignment="center"
android:text="@string/check_for_updates"/>
</LinearLayout>
</LinearLayout> </LinearLayout>

View file

@ -749,6 +749,7 @@
<string name="add_creator">Add Creators</string> <string name="add_creator">Add Creators</string>
<string name="select">Select</string> <string name="select">Select</string>
<string name="zoom">Zoom</string> <string name="zoom">Zoom</string>
<string name="check_to_see_if_an_update_is_available">Check to see if an update is available.</string>
<string-array name="home_screen_array"> <string-array name="home_screen_array">
<item>Recommendations</item> <item>Recommendations</item>
<item>Subscriptions</item> <item>Subscriptions</item>