mirror of
https://gitlab.futo.org/videostreaming/grayjay.git
synced 2026-05-28 10:43:00 +02:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 713d46c781 | |||
| 0429665173 | |||
| ac05edca77 | |||
| ad3dacf68f | |||
| 91a8996c11 | |||
| 40c4a51a2b | |||
| f8e0aaf4d2 | |||
| ad97b5a406 | |||
| b0e0c1b75f | |||
| b1fce443e9 | |||
| 66f8711055 | |||
| b7c123c281 | |||
| 9481bbf3f1 |
@@ -218,10 +218,6 @@
|
||||
android:name=".activities.ManageTabsActivity"
|
||||
android:screenOrientation="sensorPortrait"
|
||||
android:theme="@style/Theme.FutoVideo.NoActionBar" />
|
||||
<activity
|
||||
android:name=".activities.UnhideCreatorsActivity"
|
||||
android:screenOrientation="sensorPortrait"
|
||||
android:theme="@style/Theme.FutoVideo.NoActionBar" />
|
||||
<activity
|
||||
android:name=".activities.QRCaptureActivity"
|
||||
android:screenOrientation="sensorPortrait"
|
||||
|
||||
@@ -1022,15 +1022,35 @@
|
||||
return x.value
|
||||
});
|
||||
|
||||
|
||||
let settingsToUse = __DEV_SETTINGS ?? {};
|
||||
if (true) {
|
||||
for (let setting of this.Plugin?.currentPlugin?.settings) {
|
||||
if (typeof settingsToUse[setting.variable] == "undefined") {
|
||||
switch (setting?.type?.toLowerCase()) {
|
||||
case "boolean":
|
||||
settingsToUse[setting.variable] = setting.default === 'true';
|
||||
break;
|
||||
case "dropdown":
|
||||
let dropDownIndex = parseInt(setting.default);
|
||||
if (dropDownIndex) {
|
||||
settingsToUse[setting.variable] = setting.options[dropDownIndex];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(name == "enable") {
|
||||
if(parameterVals.length > 0)
|
||||
parameterVals[0] = this.Plugin.currentPlugin;
|
||||
else
|
||||
parameterVals.push(this.Plugin.currentPlugin);
|
||||
if(parameterVals.length > 1)
|
||||
parameterVals[1] = __DEV_SETTINGS;
|
||||
parameterVals[1] = settingsToUse;
|
||||
else
|
||||
parameterVals.push(__DEV_SETTINGS);
|
||||
parameterVals.push(settingsToUse);
|
||||
}
|
||||
|
||||
const func = source[name];
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.futo.platformplayer.activities.PolycentricHomeActivity
|
||||
import com.futo.platformplayer.activities.PolycentricProfileActivity
|
||||
import com.futo.platformplayer.activities.SettingsActivity
|
||||
import com.futo.platformplayer.activities.SyncHomeActivity
|
||||
import com.futo.platformplayer.activities.UnhideCreatorsActivity
|
||||
import com.futo.platformplayer.api.http.ManagedHttpClient
|
||||
import com.futo.platformplayer.constructs.Event0
|
||||
import com.futo.platformplayer.fragment.mainactivity.bottombar.MenuBottomBarFragment
|
||||
@@ -26,6 +25,7 @@ import com.futo.platformplayer.states.StateCache
|
||||
import com.futo.platformplayer.states.StateMeta
|
||||
import com.futo.platformplayer.states.StatePayment
|
||||
import com.futo.platformplayer.states.StatePolycentric
|
||||
import com.futo.platformplayer.states.StateSync
|
||||
import com.futo.platformplayer.states.StateUpdate
|
||||
import com.futo.platformplayer.stores.FragmentedStorage
|
||||
import com.futo.platformplayer.stores.FragmentedStorageFileJson
|
||||
@@ -35,6 +35,7 @@ import com.futo.platformplayer.views.fields.DropdownFieldOptionsId
|
||||
import com.futo.platformplayer.views.fields.FieldForm
|
||||
import com.futo.platformplayer.views.fields.FormField
|
||||
import com.futo.platformplayer.views.fields.FormFieldButton
|
||||
import com.futo.platformplayer.views.fields.FormFieldWarning
|
||||
import com.futo.platformplayer.views.overlays.slideup.SlideUpMenuItem
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -139,6 +140,8 @@ class Settings : FragmentedStorageFileJson() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@FormField(R.string.import_data, FieldForm.BUTTON, R.string.import_data_description, -3)
|
||||
@FormFieldButton(R.drawable.ic_move_up)
|
||||
fun import() {
|
||||
@@ -233,17 +236,6 @@ class Settings : FragmentedStorageFileJson() {
|
||||
@FormField(R.string.progress_bar, FieldForm.TOGGLE, R.string.progress_bar_description, 6)
|
||||
var progressBar: Boolean = true;
|
||||
|
||||
@FormField(R.string.unhide_creators, FieldForm.BUTTON, R.string.unhide_creators_description, 6)
|
||||
@FormFieldButton(R.drawable.ic_visibility_off)
|
||||
fun unhideCreators() {
|
||||
try {
|
||||
SettingsActivity.getActivity()?.let {
|
||||
it.startActivity(Intent(it, UnhideCreatorsActivity::class.java));
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
//Ignored
|
||||
}
|
||||
}
|
||||
|
||||
@FormField(R.string.clear_hidden, FieldForm.BUTTON, R.string.clear_hidden_description, 8)
|
||||
@FormFieldButton(R.drawable.ic_visibility_off)
|
||||
@@ -618,6 +610,11 @@ class Settings : FragmentedStorageFileJson() {
|
||||
@AdvancedField
|
||||
@FormField(R.string.shorts_pregenerate, FieldForm.TOGGLE, R.string.shorts_pregenerate_description, 28)
|
||||
var shortsPregenerate: Boolean = false;
|
||||
|
||||
@AdvancedField
|
||||
@FormField(R.string.shorts_fit_video, FieldForm.TOGGLE, R.string.shorts_fit_video_description, 29)
|
||||
@FormFieldWarning(R.string.shorts_fit_video_warning)
|
||||
var shortsFitVideo: Boolean = false;
|
||||
}
|
||||
|
||||
@FormField(R.string.comments, "group", R.string.comments_description, 6)
|
||||
@@ -1102,6 +1099,38 @@ class Settings : FragmentedStorageFileJson() {
|
||||
|
||||
@FormField(R.string.local_connections, FieldForm.TOGGLE, R.string.local_connections_description, 3)
|
||||
var localConnections: Boolean = true;
|
||||
|
||||
|
||||
|
||||
var syncServerUrl: String? = null;
|
||||
@FormField(R.string.relay_server, FieldForm.READONLYTEXT, -1, 6)
|
||||
val syncServer: String get() = if(syncServerUrl?.isBlank() == true) StateSync.RELAY_SERVER else syncServerUrl ?: StateSync.RELAY_SERVER;
|
||||
|
||||
@FormField(R.string.configure_sync_server, FieldForm.BUTTON, R.string.configure_sync_server_description, 7)
|
||||
fun configureSyncServer() {
|
||||
SettingsActivity.getActivity()?.let { context ->
|
||||
UIDialogs.showDialog(context, R.drawable.device_sync, false,
|
||||
"Enter the url to your relay server",
|
||||
"Using your own relay server requires a proper setup with portforwarding.\nUse at your own risk.",
|
||||
null,
|
||||
syncServerUrl ?: "",
|
||||
"YourRelayServerDomain.com", 0,
|
||||
UIDialogs.Action("Cancel", {}),
|
||||
UIDialogs.Action("Reset", {
|
||||
syncServerUrl = null;
|
||||
instance.save();
|
||||
context.reloadSettings();
|
||||
UIDialogs.toast("Sync server changes require a restart");
|
||||
}, UIDialogs.ActionStyle.ACCENT),
|
||||
UIDialogs.Action.withInput("Configure", {
|
||||
syncServerUrl = it?.text
|
||||
instance.save();
|
||||
context.reloadSettings();
|
||||
UIDialogs.toast("Sync server changes require a restart");
|
||||
}, UIDialogs.ActionStyle.PRIMARY),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@FormField(R.string.info, FieldForm.GROUP, -1, 21)
|
||||
|
||||
@@ -113,8 +113,8 @@ class UIDialogs {
|
||||
currentDialog.code,
|
||||
currentDialog.defaultCloseAction,
|
||||
*currentDialog.actions.map {
|
||||
return@map Action(it.text, {
|
||||
it.action();
|
||||
return@map Action.withInput(it.text, { str ->
|
||||
it.invokeAction(str);
|
||||
multiShowDialog(context, dialogDescriptor.drop(1), finally);
|
||||
}, it.style);
|
||||
}.toTypedArray());
|
||||
@@ -203,7 +203,9 @@ class UIDialogs {
|
||||
fun showDialog(context: Context, icon: Int, text: String, textDetails: String? = null, code: String? = null, defaultCloseAction: Int, vararg actions: Action): AlertDialog {
|
||||
return showDialog(context, icon, false, text, textDetails, code, defaultCloseAction, *actions);
|
||||
}
|
||||
fun showDialog(context: Context, icon: Int, animated: Boolean, text: String, textDetails: String? = null, code: String? = null, defaultCloseAction: Int, vararg actions: Action): AlertDialog {
|
||||
fun showDialog(context: Context, icon: Int, animated: Boolean, text: String, textDetails: String? = null, code: String? = null, defaultCloseAction: Int, vararg actions: Action): AlertDialog
|
||||
= showDialog(context, icon, animated, text, textDetails, code, null, null, defaultCloseAction, *actions);
|
||||
fun showDialog(context: Context, icon: Int, animated: Boolean, text: String, textDetails: String? = null, code: String? = null, input: String?, placeholder: String?, defaultCloseAction: Int, vararg actions: Action): AlertDialog {
|
||||
val builder = AlertDialog.Builder(context);
|
||||
val view = LayoutInflater.from(context).inflate(R.layout.dialog_multi_button, null);
|
||||
builder.setView(view);
|
||||
@@ -226,6 +228,16 @@ class UIDialogs {
|
||||
this.text = textDetails;
|
||||
}
|
||||
};
|
||||
var inputView = view.findViewById<TextView>(R.id.dialog_text_input);
|
||||
inputView.apply {
|
||||
if (input == null && placeholder == null) this.visibility = View.GONE;
|
||||
else {
|
||||
this.text = input ?: "";
|
||||
this.hint = placeholder ?: "";
|
||||
this.visibility = View.VISIBLE;
|
||||
this.textAlignment = View.TEXT_ALIGNMENT_VIEW_START
|
||||
}
|
||||
};
|
||||
view.findViewById<TextView>(R.id.dialog_text_code).apply {
|
||||
if (code == null) this.visibility = View.GONE;
|
||||
else {
|
||||
@@ -250,7 +262,7 @@ class UIDialogs {
|
||||
buttonView.textSize = 14f;
|
||||
buttonView.typeface = resources.getFont(R.font.inter_regular);
|
||||
buttonView.text = act.text;
|
||||
buttonView.setOnClickListener { act.action(); dialog.dismiss(); };
|
||||
buttonView.setOnClickListener { act.invokeAction(DialogResult(inputView?.text?.toString())); dialog.dismiss(); };
|
||||
when(act.style) {
|
||||
ActionStyle.PRIMARY -> buttonView.setBackgroundResource(R.drawable.background_button_primary);
|
||||
ActionStyle.ACCENT -> buttonView.setBackgroundResource(R.drawable.background_button_accent);
|
||||
@@ -275,7 +287,7 @@ class UIDialogs {
|
||||
};
|
||||
dialog.setOnCancelListener {
|
||||
if(defaultCloseAction >= 0 && defaultCloseAction < actions.size)
|
||||
actions[defaultCloseAction].action();
|
||||
actions[defaultCloseAction].invokeAction(DialogResult(inputView?.text?.toString()));
|
||||
}
|
||||
dialog.setOnDismissListener {
|
||||
registerDialogClosed(dialog);
|
||||
@@ -535,17 +547,36 @@ class UIDialogs {
|
||||
}
|
||||
class Action {
|
||||
val text: String;
|
||||
val action: ()->Unit;
|
||||
val action: ((DialogResult?)->Unit);
|
||||
val style: ActionStyle;
|
||||
var center: Boolean;
|
||||
|
||||
constructor(text: String, action: ()->Unit, style: ActionStyle = ActionStyle.NONE, center: Boolean = false) {
|
||||
this.text = text;
|
||||
this.action = { action() };
|
||||
this.style = style;
|
||||
this.center = center;
|
||||
}
|
||||
protected constructor(text: String, action: (DialogResult?)->Unit, style: ActionStyle = ActionStyle.NONE, center: Boolean = false) {
|
||||
this.text = text;
|
||||
this.action = action;
|
||||
this.style = style;
|
||||
this.center = center;
|
||||
}
|
||||
|
||||
fun invokeAction(input: DialogResult? = null) {
|
||||
this.action(input);
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun withInput(text: String, action: (DialogResult?)->Unit, style: ActionStyle = ActionStyle.NONE, center: Boolean = false): Action {
|
||||
return Action(text, action, style, center);
|
||||
}
|
||||
}
|
||||
}
|
||||
class DialogResult(
|
||||
val text: String?
|
||||
);
|
||||
enum class ActionStyle {
|
||||
NONE,
|
||||
PRIMARY,
|
||||
|
||||
@@ -79,20 +79,9 @@ class LoginActivity : AppCompatActivity() {
|
||||
val uiMods = authConfig.uiMods?.toMutableList() ?: mutableListOf<SourcePluginAuthConfig.UIMod>();
|
||||
var currentScale = 100;
|
||||
var currentDesktop = false;
|
||||
_webView.setInitialScale(50);
|
||||
webViewClient.onPageLoaded.subscribe { view, url ->
|
||||
_textUrl.setText(url ?: "");
|
||||
|
||||
if(!isFirstLoad)
|
||||
return@subscribe;
|
||||
isFirstLoad = false;
|
||||
|
||||
if(!authConfig.loginButton.isNullOrEmpty() && authConfig.loginButton.matches(REGEX_LOGIN_BUTTON)) {
|
||||
Logger.i(TAG, "Clicking login button [${authConfig.loginButton}]");
|
||||
//TODO: Find most reliable way to wait for page js to finish
|
||||
view?.evaluateJavascript("setTimeout(()=> document.querySelector(\"${authConfig.loginButton}\")?.click(), 1000)", {});
|
||||
}
|
||||
|
||||
if(loginWarnings.size > 0 && url != null) {
|
||||
synchronized(loginWarnings) {
|
||||
val warning = loginWarnings.find { url.matches(it.getRegex()) };
|
||||
@@ -106,6 +95,16 @@ class LoginActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
if(!isFirstLoad)
|
||||
return@subscribe;
|
||||
isFirstLoad = false;
|
||||
|
||||
if(!authConfig.loginButton.isNullOrEmpty() && authConfig.loginButton.matches(REGEX_LOGIN_BUTTON)) {
|
||||
Logger.i(TAG, "Clicking login button [${authConfig.loginButton}]");
|
||||
//TODO: Find most reliable way to wait for page js to finish
|
||||
view?.evaluateJavascript("setTimeout(()=> document.querySelector(\"${authConfig.loginButton}\")?.click(), 1000)", {});
|
||||
}
|
||||
|
||||
/*
|
||||
var specifiedScale = false;
|
||||
var specifiedDesktop = false;
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
package com.futo.platformplayer.activities
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.ImageButton
|
||||
import android.widget.LinearLayout
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.setNavigationBarColorAndIcons
|
||||
import com.futo.platformplayer.states.StateApp
|
||||
import com.futo.platformplayer.states.StateMeta
|
||||
import com.futo.platformplayer.views.AnyAdapterView.Companion.asAny
|
||||
import com.futo.platformplayer.views.adapters.viewholders.HiddenCreatorViewHolder
|
||||
import com.futo.platformplayer.views.adapters.viewholders.HiddenCreatorViewHolderData
|
||||
import com.futo.platformplayer.views.buttons.BigButton
|
||||
|
||||
class UnhideCreatorsActivity : AppCompatActivity() {
|
||||
override fun attachBaseContext(newBase: Context?) {
|
||||
super.attachBaseContext(StateApp.instance.getLocaleContext(newBase))
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_unhide_creators)
|
||||
setNavigationBarColorAndIcons()
|
||||
|
||||
val backButton: ImageButton = findViewById(R.id.button_back)
|
||||
val zeroState: LinearLayout = findViewById(R.id.zero_state)
|
||||
val returnButton: BigButton = findViewById(R.id.return_button)
|
||||
val recyclerHiddenCreators: RecyclerView = findViewById(R.id.recycler_tabs)
|
||||
|
||||
val items = ArrayList(StateMeta.instance.hiddenCreators.values.map {
|
||||
HiddenCreatorViewHolderData(it)
|
||||
})
|
||||
|
||||
if (items.isEmpty()) {
|
||||
zeroState.visibility = View.VISIBLE
|
||||
recyclerHiddenCreators.visibility = View.GONE
|
||||
}
|
||||
|
||||
returnButton.onClick.subscribe {
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
|
||||
recyclerHiddenCreators.asAny<HiddenCreatorViewHolderData, HiddenCreatorViewHolder>(items) {
|
||||
it.onUnhide.subscribe {
|
||||
it.data?.creatorUrl?.let { creatorUrl ->
|
||||
StateMeta.instance.removeHiddenCreator(creatorUrl)
|
||||
|
||||
val position = items.indexOfFirst { item -> item.creatorUrl == creatorUrl }
|
||||
if (position != -1) {
|
||||
items.removeAt(position)
|
||||
recyclerHiddenCreators.adapter!!.notifyItemRemoved(position)
|
||||
}
|
||||
|
||||
if (items.isEmpty()) {
|
||||
zeroState.visibility = View.VISIBLE
|
||||
recyclerHiddenCreators.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
backButton.setOnClickListener {
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Suppress("unused")
|
||||
private const val TAG = "UnhideCreatorsActivity"
|
||||
}
|
||||
}
|
||||
@@ -279,6 +279,14 @@ class HomeFragment : MainFragment() {
|
||||
else {
|
||||
view.setToggle(!active);
|
||||
}
|
||||
}, { view, views, enabled ->
|
||||
val toDisable = views.filter { it != view && it.tag == "plugins" };
|
||||
if(!view.isActive)
|
||||
view.handleClick();
|
||||
for(tag in toDisable) {
|
||||
if(tag.isActive)
|
||||
tag.handleClick();
|
||||
}
|
||||
}).withTag("plugins")
|
||||
})
|
||||
else listOf())
|
||||
|
||||
+4
-3
@@ -1005,8 +1005,7 @@ class VideoDetailView : ConstraintLayout {
|
||||
}
|
||||
}
|
||||
_slideUpOverlay?.hide();
|
||||
} else null,
|
||||
if(video is JSVideoDetails && (video as JSVideoDetails).hasVODEvents())
|
||||
} else if(video is JSVideoDetails && (video as JSVideoDetails).hasVODEvents())
|
||||
RoundButton(context, R.drawable.ic_chat, context.getString(R.string.vod_chat), TAG_VODCHAT) {
|
||||
video?.let {
|
||||
try {
|
||||
@@ -1155,7 +1154,7 @@ class VideoDetailView : ConstraintLayout {
|
||||
//Recover cancelled loads
|
||||
if(video == null) {
|
||||
val t = (lastPositionMilliseconds / 1000.0f).roundToLong();
|
||||
if(_searchVideo != null)
|
||||
if(_searchVideo != null && !wasLoginCall)
|
||||
setVideoOverview(_searchVideo!!, true, t);
|
||||
else if(_url != null && !wasLoginCall)
|
||||
setVideo(_url!!, t, _playWhenReady);
|
||||
@@ -2518,6 +2517,7 @@ class VideoDetailView : ConstraintLayout {
|
||||
if (!StateCasting.instance.resumeVideo()) {
|
||||
_player.play();
|
||||
}
|
||||
onShouldEnterPictureInPictureChanged.emit()
|
||||
|
||||
//TODO: This was needed because handleLowerVolume was done.
|
||||
//_player.setVolume(1.0f);
|
||||
@@ -2534,6 +2534,7 @@ class VideoDetailView : ConstraintLayout {
|
||||
if (!StateCasting.instance.pauseVideo()) {
|
||||
_player.pause();
|
||||
}
|
||||
onShouldEnterPictureInPictureChanged.emit()
|
||||
}
|
||||
private fun handleSeek(ms: Long) {
|
||||
Logger.i(TAG, "handleSeek(ms=$ms)")
|
||||
|
||||
@@ -57,9 +57,12 @@ class StateSync {
|
||||
return
|
||||
}
|
||||
|
||||
var relayServerUrl = Settings.instance.synchronization.syncServer;
|
||||
Logger.i(TAG, "Relay used: ${relayServerUrl}");
|
||||
|
||||
syncService = SyncService(
|
||||
SERVICE_NAME,
|
||||
RELAY_SERVER,
|
||||
relayServerUrl,
|
||||
RELAY_PUBLIC_KEY,
|
||||
APP_ID,
|
||||
StoreBasedSyncDatabaseProvider(),
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.view.children
|
||||
import androidx.lifecycle.findViewTreeLifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@@ -28,6 +29,8 @@ import kotlinx.coroutines.launch
|
||||
class ToggleBar : LinearLayout {
|
||||
private val _tagsContainer: LinearLayout;
|
||||
|
||||
private var allowLongPress: Boolean = false;
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
}
|
||||
@@ -48,12 +51,31 @@ class ToggleBar : LinearLayout {
|
||||
for(button in buttons) {
|
||||
_tagsContainer.addView(ToggleTagView(context).apply {
|
||||
if(button.icon > 0)
|
||||
this.setInfo(button.icon, button.name, button.isActive, button.isButton);
|
||||
this.setInfo(button.icon, button.name, button.isActive, button.isButton, button.tag);
|
||||
else if(button.iconVariable != null)
|
||||
this.setInfo(button.iconVariable, button.name, button.isActive, button.isButton);
|
||||
this.setInfo(button.iconVariable, button.name, button.isActive, button.isButton, button.tag);
|
||||
else
|
||||
this.setInfo(button.name, button.isActive, button.isButton);
|
||||
this.setInfo(button.name, button.isActive, button.isButton, button.tag);
|
||||
this.onClick.subscribe({ view, enabled -> button.action(view, enabled); });
|
||||
if(allowLongPress) {
|
||||
this.onLongClick.subscribe({ view, enabled ->
|
||||
for (tagView in _tagsContainer.children.filter { it is ToggleTagView }) {
|
||||
if (tagView != view && tagView is ToggleTagView && !tagView.isButton) {
|
||||
if (enabled && !tagView.isActive) {
|
||||
tagView.handleClick();
|
||||
} else if (!enabled && tagView.isActive) {
|
||||
tagView.handleClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
else if(button.actionLong != null) {
|
||||
this.onLongClick.subscribe({ view, enabled ->
|
||||
val tags = _tagsContainer.children.filter { it is ToggleTagView }.map { it as ToggleTagView }.toList();
|
||||
button.actionLong!!(view, tags, enabled);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -63,16 +85,18 @@ class ToggleBar : LinearLayout {
|
||||
val icon: Int;
|
||||
val iconVariable: ImageVariable?;
|
||||
val action: (ToggleTagView, Boolean)->Unit;
|
||||
val actionLong: ((ToggleTagView, List<ToggleTagView>, Boolean) -> Unit)?;
|
||||
val isActive: Boolean;
|
||||
var isButton: Boolean = false
|
||||
private set;
|
||||
var tag: String? = null;
|
||||
|
||||
constructor(name: String, icon: ImageVariable?, isActive: Boolean = false, action: (ToggleTagView, Boolean)->Unit) {
|
||||
constructor(name: String, icon: ImageVariable?, isActive: Boolean = false, action: (ToggleTagView, Boolean)->Unit, actionLong: ((ToggleTagView, List<ToggleTagView>, Boolean)->Unit)? = null) {
|
||||
this.name = name;
|
||||
this.icon = 0;
|
||||
this.iconVariable = icon;
|
||||
this.action = action;
|
||||
this.actionLong = actionLong;
|
||||
this.isActive = isActive;
|
||||
}
|
||||
constructor(name: String, icon: Int, isActive: Boolean = false, action: (ToggleTagView, Boolean)->Unit) {
|
||||
@@ -80,6 +104,7 @@ class ToggleBar : LinearLayout {
|
||||
this.icon = icon;
|
||||
this.iconVariable = null;
|
||||
this.action = action;
|
||||
this.actionLong = null;
|
||||
this.isActive = isActive;
|
||||
}
|
||||
constructor(name: String, isActive: Boolean = false, action: (ToggleTagView, Boolean)->Unit) {
|
||||
@@ -87,6 +112,7 @@ class ToggleBar : LinearLayout {
|
||||
this.icon = 0;
|
||||
this.iconVariable = null;
|
||||
this.action = action;
|
||||
this.actionLong = null;
|
||||
this.isActive = isActive;
|
||||
}
|
||||
|
||||
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
package com.futo.platformplayer.views.adapters.viewholders
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageButton
|
||||
import android.widget.TextView
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.constructs.Event0
|
||||
import com.futo.platformplayer.views.adapters.AnyAdapter
|
||||
|
||||
data class HiddenCreatorViewHolderData(val creatorUrl: String)
|
||||
|
||||
class HiddenCreatorViewHolder(viewGroup: ViewGroup) :
|
||||
AnyAdapter.AnyViewHolder<HiddenCreatorViewHolderData>(
|
||||
LayoutInflater.from(viewGroup.context)
|
||||
.inflate(R.layout.list_hidden_creator, viewGroup, false)
|
||||
) {
|
||||
var data: HiddenCreatorViewHolderData? = null
|
||||
private val _creatorLabel: TextView = _view.findViewById(R.id.text_creator_name)
|
||||
|
||||
val onUnhide = Event0()
|
||||
|
||||
init {
|
||||
val unhideButton: ImageButton = _view.findViewById(R.id.button_trash)
|
||||
unhideButton.setOnClickListener {
|
||||
onUnhide.emit()
|
||||
}
|
||||
}
|
||||
|
||||
override fun bind(value: HiddenCreatorViewHolderData) {
|
||||
_creatorLabel.text = value.creatorUrl
|
||||
data = value
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,29 @@ class RadioGroupView : FlexboxLayout {
|
||||
radioView.layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
|
||||
radioView.setInfo(option.first, initiallySelectedOptions.contains(option.second));
|
||||
radioView.setPadding(_padding_px, _padding_px, _padding_px, _padding_px);
|
||||
if(multiSelect)
|
||||
radioView.onLongClick.subscribe {
|
||||
val selected = !radioView.selected;
|
||||
if (selected) {
|
||||
selectedOptions.clear();
|
||||
for(v in radioViews)
|
||||
v.setIsSelected(true);
|
||||
selectedOptions.addAll(options.map { it.second });
|
||||
} else {
|
||||
if(atLeastOne) {
|
||||
for(v in radioViews)
|
||||
v.setIsSelected(false);
|
||||
selectedOptions.clear();
|
||||
selectedOptions.add(option.second);
|
||||
}
|
||||
else {
|
||||
for(v in radioViews)
|
||||
v.setIsSelected(false);
|
||||
selectedOptions.clear();
|
||||
}
|
||||
}
|
||||
onSelectedChange.emit(selectedOptions);
|
||||
}
|
||||
radioView.onClick.subscribe {
|
||||
val selected = !radioView.selected;
|
||||
if (selected) {
|
||||
|
||||
@@ -20,6 +20,7 @@ class RadioView : LinearLayout {
|
||||
|
||||
val selected get() = _selected;
|
||||
var onClick = Event0();
|
||||
var onLongClick = Event0();
|
||||
var onSelectedChange = Event1<Boolean>();
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet? = null) : super(context, attrs) {
|
||||
@@ -32,6 +33,13 @@ class RadioView : LinearLayout {
|
||||
setIsSelected(!_selected)
|
||||
}
|
||||
};
|
||||
_root.setOnLongClickListener {
|
||||
onLongClick.emit();
|
||||
if (_handleClick) {
|
||||
setIsSelected(!_selected)
|
||||
}
|
||||
return@setOnLongClickListener true;
|
||||
}
|
||||
|
||||
_root.setBackgroundResource(R.drawable.background_radio_unselected);
|
||||
_textTag.setTextColor(ContextCompat.getColor(context, R.color.gray_67));
|
||||
|
||||
@@ -23,12 +23,16 @@ class ToggleTagView : LinearLayout {
|
||||
private var _text: String = "";
|
||||
private var _image: ImageView;
|
||||
|
||||
var tag: String? = null
|
||||
private set;
|
||||
|
||||
var isActive: Boolean = false
|
||||
private set;
|
||||
var isButton: Boolean = false
|
||||
private set;
|
||||
|
||||
var onClick = Event2<ToggleTagView, Boolean>();
|
||||
var onLongClick = Event2<ToggleTagView, Boolean>();
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet? = null) : super(context, attrs) {
|
||||
LayoutInflater.from(context).inflate(R.layout.view_toggle_tag, this, true);
|
||||
@@ -36,10 +40,25 @@ class ToggleTagView : LinearLayout {
|
||||
_textTag = findViewById(R.id.text_tag);
|
||||
_image = findViewById(R.id.image_tag);
|
||||
_root.setOnClickListener {
|
||||
if(!isButton)
|
||||
setToggle(!isActive);
|
||||
onClick.emit(this, isActive);
|
||||
handleClick();
|
||||
}
|
||||
_root.setOnLongClickListener {
|
||||
if(onLongClick.hasListeners())
|
||||
onLongClick.emit(this, isActive);
|
||||
else {
|
||||
if(!isButton) {
|
||||
setToggle(!isActive);
|
||||
}
|
||||
onClick.emit(this, isActive);
|
||||
}
|
||||
return@setOnLongClickListener true;
|
||||
}
|
||||
}
|
||||
|
||||
fun handleClick() {
|
||||
if(!isButton)
|
||||
setToggle(!isActive);
|
||||
onClick.emit(this, isActive);
|
||||
}
|
||||
|
||||
fun setToggle(isActive: Boolean) {
|
||||
@@ -70,9 +89,10 @@ class ToggleTagView : LinearLayout {
|
||||
_image.visibility = View.VISIBLE;
|
||||
_textTag.visibility = if(!toggle.name.isNullOrEmpty()) View.VISIBLE else View.GONE;
|
||||
this.isButton = isButton;
|
||||
tag = toggle.tag;
|
||||
}
|
||||
|
||||
fun setInfo(imageResource: Int, text: String, isActive: Boolean, isButton: Boolean = false) {
|
||||
fun setInfo(imageResource: Int, text: String, isActive: Boolean, isButton: Boolean = false, tag: String? = null) {
|
||||
_text = text;
|
||||
_textTag.text = text;
|
||||
setToggle(isActive);
|
||||
@@ -80,8 +100,9 @@ class ToggleTagView : LinearLayout {
|
||||
_image.visibility = View.VISIBLE;
|
||||
_textTag.visibility = if(!text.isNullOrEmpty()) View.VISIBLE else View.GONE;
|
||||
this.isButton = isButton;
|
||||
this.tag = tag;
|
||||
}
|
||||
fun setInfo(image: ImageVariable, text: String, isActive: Boolean, isButton: Boolean = false) {
|
||||
fun setInfo(image: ImageVariable, text: String, isActive: Boolean, isButton: Boolean = false, tag: String? = null) {
|
||||
_text = text;
|
||||
_textTag.text = text;
|
||||
setToggle(isActive);
|
||||
@@ -89,13 +110,15 @@ class ToggleTagView : LinearLayout {
|
||||
_image.visibility = View.VISIBLE;
|
||||
_textTag.visibility = if(!text.isNullOrEmpty()) View.VISIBLE else View.GONE;
|
||||
this.isButton = isButton;
|
||||
this.tag = tag;
|
||||
}
|
||||
fun setInfo(text: String, isActive: Boolean, isButton: Boolean = false) {
|
||||
fun setInfo(text: String, isActive: Boolean, isButton: Boolean = false, tag: String? = null) {
|
||||
_image.visibility = View.GONE;
|
||||
_text = text;
|
||||
_textTag.text = text;
|
||||
_textTag.visibility = if(!text.isNullOrEmpty()) View.VISIBLE else View.GONE;
|
||||
setToggle(isActive);
|
||||
this.isButton = isButton;
|
||||
this.tag = tag;
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,12 @@ import androidx.annotation.OptIn
|
||||
import androidx.media3.common.PlaybackParameters
|
||||
import androidx.media3.common.Player
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.ui.AspectRatioFrameLayout
|
||||
import androidx.media3.ui.DefaultTimeBar
|
||||
import androidx.media3.ui.PlayerView
|
||||
import androidx.media3.ui.TimeBar
|
||||
import com.futo.platformplayer.R
|
||||
import com.futo.platformplayer.Settings
|
||||
import com.futo.platformplayer.constructs.Event1
|
||||
import com.futo.platformplayer.logging.Logger
|
||||
import com.futo.platformplayer.states.StatePlayer
|
||||
@@ -66,6 +68,11 @@ class FutoShortPlayer(context: Context, attrs: AttributeSet? = null) :
|
||||
videoView = findViewById(R.id.short_player_view)
|
||||
progressBar = findViewById(R.id.short_player_progress_bar)
|
||||
|
||||
if(Settings.instance.playback.shortsFitVideo)
|
||||
videoView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT;
|
||||
else
|
||||
videoView.resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM;
|
||||
|
||||
videoView.subtitleView?.setFixedTextSize(Dimension.SP, 18F);
|
||||
|
||||
if (!isInEditMode) {
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingBottom="15dp">
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/button_back"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:contentDescription="@string/back"
|
||||
android:minWidth="48dp"
|
||||
android:minHeight="48dp"
|
||||
app:srcCompat="@drawable/ic_back_thin_white_16dp" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@font/inter_extra_light"
|
||||
android:text="@string/unhide_creators"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="24sp" />
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<!-- zero state -->
|
||||
<LinearLayout
|
||||
android:id="@+id/zero_state"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginHorizontal="24dp"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/icon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:importantForAccessibility="no"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/ic_help" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:fontFamily="@font/inter_bold"
|
||||
android:gravity="center"
|
||||
android:text="@string/no_results"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="22dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_centered"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:fontFamily="@font/inter_regular"
|
||||
android:gravity="center"
|
||||
android:text="@string/no_hidden_creators"
|
||||
android:textColor="@color/gray_ac"
|
||||
android:textSize="13dp" />
|
||||
|
||||
<com.futo.platformplayer.views.buttons.BigButton
|
||||
android:id="@+id/return_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:buttonIcon="@drawable/ic_settings"
|
||||
app:buttonSubText="Go back to settings"
|
||||
app:buttonText="Back" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -63,6 +63,16 @@
|
||||
android:layout_height="wrap_content"
|
||||
tools:ignore="HardcodedText" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/dialog_text_input"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:visibility="gone"
|
||||
/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/dialog_buttons"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_creator_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_weight="1"
|
||||
android:fontFamily="@font/inter_light"
|
||||
android:gravity="center_vertical"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/button_trash"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:contentDescription="@string/cd_button_delete"
|
||||
android:padding="5dp"
|
||||
android:scaleType="fitCenter"
|
||||
app:srcCompat="@drawable/ic_trash" />
|
||||
</LinearLayout>
|
||||
@@ -9,7 +9,7 @@
|
||||
android:layout_above="@+id/short_player_progress_bar"
|
||||
android:background="@color/black"
|
||||
app:default_artwork="@drawable/placeholder_video_thumbnail"
|
||||
app:resize_mode="fill"
|
||||
app:resize_mode="zoom"
|
||||
app:show_buffering="when_playing"
|
||||
app:use_artwork="true"
|
||||
app:use_controller="false" />
|
||||
|
||||
@@ -91,7 +91,6 @@
|
||||
<string name="watch_later">Watch Later</string>
|
||||
<string name="no_results">Nothing to see here</string>
|
||||
<string name="no_results_shorts">The enabled sources do not have any short video results.</string>
|
||||
<string name="no_hidden_creators">There are no hidden creators.</string>
|
||||
<string name="create">Create</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="yes">Yes</string>
|
||||
@@ -339,6 +338,8 @@
|
||||
<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="configure_sync_server">Configure Sync Server</string>
|
||||
<string name="configure_sync_server_description">Allows you to change the Sync Server to a self-hosted one.</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="configure_browsing_behavior">Configure browsing behavior</string>
|
||||
@@ -439,6 +440,9 @@
|
||||
<string name="delete_watchlist_on_finish_description">After you leave a video that you mostly watched, it will be removed from watch later.</string>
|
||||
<string name="shorts_pregenerate">Pre-generate shorts sources</string>
|
||||
<string name="shorts_pregenerate_description">Generates short sources (when applicable) one video ahead</string>
|
||||
<string name="shorts_fit_video">Fit Shorts Video</string>
|
||||
<string name="shorts_fit_video_description">Will scale the video to fit the view, instead of filling the view properly.</string>
|
||||
<string name="shorts_fit_video_warning">This setting will require you to reboot Grayjay.</string>
|
||||
<string name="seek_offset">Seek duration</string>
|
||||
<string name="min_playback_speed">Minimum Playback Speed</string>
|
||||
<string name="min_playback_speed_description">Minimum Available Speed</string>
|
||||
@@ -474,6 +478,7 @@
|
||||
<string name="number_of_concurrent_threads_to_multiply_download_speeds_from_throttled_sources">Number of concurrent threads to multiply download speeds from throttled sources</string>
|
||||
<string name="payment">Payment</string>
|
||||
<string name="payment_status">Payment Status</string>
|
||||
<string name="relay_server">Sync Relay Server</string>
|
||||
<string name="bypass_rotation_prevention">Bypass Rotation Prevention</string>
|
||||
<string name="playlist_delete_confirmation">Playlist Delete Confirmation</string>
|
||||
<string name="playlist_delete_confirmation_description">Show confirmation dialog when deleting media from a playlist</string>
|
||||
@@ -510,8 +515,6 @@
|
||||
<string name="reinstall_embedded_plugins">Reinstall Embedded Plugins</string>
|
||||
<string name="remove_cached_version">Remove Cached Version</string>
|
||||
<string name="remove_the_last_downloaded_version">Remove the last downloaded version</string>
|
||||
<string name="unhide_creators">Manage Hidden Creators</string>
|
||||
<string name="unhide_creators_description">View and selectively unhide previously hidden creators, showing them again</string>
|
||||
<string name="clear_hidden">Clear Hidden</string>
|
||||
<string name="clear_hidden_description">Removes all hidden creators and videos, showing them again</string>
|
||||
<string name="reset_announcements">Reset announcements</string>
|
||||
|
||||
Submodule app/src/stable/assets/sources/spotify updated: 092faa64d5...0b50c2e61b
Submodule app/src/stable/assets/sources/youtube updated: bd6fbe8d49...f370a88604
Submodule app/src/unstable/assets/sources/spotify updated: 092faa64d5...0b50c2e61b
Submodule app/src/unstable/assets/sources/youtube updated: bd6fbe8d49...f370a88604
Reference in New Issue
Block a user