diff --git a/app/src/main/java/com/futo/platformplayer/Settings.kt b/app/src/main/java/com/futo/platformplayer/Settings.kt index 5fd9a052865eb476a4eee19e885f173b8e0e5f0a..3caae0753dfbc5fd9ddced73b34cbcc10399d7e7 100644 --- a/app/src/main/java/com/futo/platformplayer/Settings.kt +++ b/app/src/main/java/com/futo/platformplayer/Settings.kt @@ -23,6 +23,7 @@ import com.futo.platformplayer.views.fields.FormField import com.futo.platformplayer.views.fields.FieldForm 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 import kotlinx.coroutines.withContext @@ -44,7 +45,7 @@ class Settings : FragmentedStorageFileJson() { @Transient val onTabsChanged = Event0(); - @FormField(R.string.manage_polycentric_identity, FieldForm.BUTTON, R.string.manage_your_polycentric_identity, -5) + @FormField(R.string.manage_polycentric_identity, FieldForm.BUTTON, R.string.manage_your_polycentric_identity, -6) @FormFieldButton(R.drawable.ic_person) fun managePolycentricIdentity() { SettingsActivity.getActivity()?.let { @@ -60,7 +61,7 @@ class Settings : FragmentedStorageFileJson() { } } - @FormField(R.string.show_faq, FieldForm.BUTTON, R.string.get_answers_to_common_questions, -4) + @FormField(R.string.show_faq, FieldForm.BUTTON, R.string.get_answers_to_common_questions, -5) @FormFieldButton(R.drawable.ic_quiz) fun openFAQ() { try { @@ -70,7 +71,7 @@ class Settings : FragmentedStorageFileJson() { //Ignored } } - @FormField(R.string.show_issues, FieldForm.BUTTON, R.string.a_list_of_user_reported_and_self_reported_issues, -3) + @FormField(R.string.show_issues, FieldForm.BUTTON, R.string.a_list_of_user_reported_and_self_reported_issues, -4) @FormFieldButton(R.drawable.ic_data_alert) fun openIssues() { try { @@ -102,7 +103,7 @@ class Settings : FragmentedStorageFileJson() { } }*/ - @FormField(R.string.manage_tabs, FieldForm.BUTTON, R.string.change_tabs_visible_on_the_home_screen, -2) + @FormField(R.string.manage_tabs, FieldForm.BUTTON, R.string.change_tabs_visible_on_the_home_screen, -3) @FormFieldButton(R.drawable.ic_tabs) fun manageTabs() { try { @@ -114,6 +115,17 @@ class Settings : FragmentedStorageFileJson() { } } + + + @FormField(R.string.import_data, FieldForm.BUTTON, R.string.import_data_description, -2) + @FormFieldButton(R.drawable.ic_move_up) + fun import() { + val act = SettingsActivity.getActivity() ?: return; + val intent = MainActivity.getImportOptionsIntent(act); + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK; + act.startActivity(intent); + } + @FormField(R.string.link_handling, FieldForm.BUTTON, R.string.allow_grayjay_to_handle_links, -1) @FormFieldButton(R.drawable.ic_link) fun manageLinks() { @@ -405,6 +417,9 @@ class Settings : FragmentedStorageFileJson() { @FormField(R.string.default_comment_section, FieldForm.DROPDOWN, -1, 0) @DropdownFieldOptionsId(R.array.comment_sections) var defaultCommentSection: Int = 0; + + @FormField(R.string.bad_reputation_comments_fading, FieldForm.TOGGLE, R.string.bad_reputation_comments_fading_description, 0) + var badReputationCommentsFading: Boolean = true; } @FormField(R.string.downloads, "group", R.string.configure_downloading_of_videos, 7) @@ -712,25 +727,16 @@ class Settings : FragmentedStorageFileJson() { @FormField(R.string.export_data, FieldForm.BUTTON, R.string.creates_a_zip_file_with_your_data_which_can_be_imported_by_opening_it_with_grayjay, 3) fun export() { - StateBackup.startExternalBackup(); + val activity = SettingsActivity.getActivity() ?: return; + UISlideOverlays.showOverlay(activity.overlay, "Select export type", null, {}, + SlideUpMenuItem(activity, R.drawable.ic_share, "Share", "", null, { + StateBackup.shareExternalBackup(); + }), + SlideUpMenuItem(activity, R.drawable.ic_download, "File", "", null, { + StateBackup.saveExternalBackup(activity); + }) + ) } - - - /* - @FormField(R.string.import_data, FieldForm.BUTTON, R.string.import_data_description, 4) - fun import() { - val act = SettingsActivity.getActivity() ?: return; - StateApp.instance.requestFileReadAccess(act, null) { - if(it != null && it.exists()) { - val name = it.name; - val contents = it.readBytes(act); - if(contents != null) { - if(name != null && name.endsWith(".zip", true)) - StateBackup.importZipBytes(act, act.lifecycleScope, contents); - } - } - } - }*/ } @FormField(R.string.payment, FieldForm.GROUP, -1, 17) diff --git a/app/src/main/java/com/futo/platformplayer/UIDialogs.kt b/app/src/main/java/com/futo/platformplayer/UIDialogs.kt index 86d73e28021262ef4601d378176d5954ff1d4933..a32c1b80869af9a8f548ca960ba92eaba2407cd0 100644 --- a/app/src/main/java/com/futo/platformplayer/UIDialogs.kt +++ b/app/src/main/java/com/futo/platformplayer/UIDialogs.kt @@ -13,6 +13,7 @@ import android.view.View import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.widget.* import androidx.core.content.ContextCompat +import com.futo.platformplayer.activities.MainActivity import com.futo.platformplayer.api.media.models.comments.IPlatformComment import com.futo.platformplayer.casting.StateCasting import com.futo.platformplayer.dialogs.* @@ -189,8 +190,10 @@ class UIDialogs { view.findViewById<TextView>(R.id.dialog_text_code).apply { if(code == null) this.visibility = View.GONE; - else + else { this.text = code; + this.visibility = View.VISIBLE; + } }; view.findViewById<LinearLayout>(R.id.dialog_buttons).apply { val buttons = actions.map<Action, TextView> { act -> @@ -326,6 +329,12 @@ class UIDialogs { dialog.setOnDismissListener { registerDialogClosed(dialog) }; dialog.show(); } + fun showImportOptionsDialog(context: MainActivity) { + val dialog = ImportOptionsDialog(context); + registerDialogOpened(dialog); + dialog.setOnDismissListener { registerDialogClosed(dialog) }; + dialog.show(); + } fun showCastingDialog(context: Context) { diff --git a/app/src/main/java/com/futo/platformplayer/UISlideOverlays.kt b/app/src/main/java/com/futo/platformplayer/UISlideOverlays.kt index 6b401f1cbb28fe1640cde45af6fd52ee9ddecab8..80f33705883aba5f5edbd1749da4f0333a3cdb12 100644 --- a/app/src/main/java/com/futo/platformplayer/UISlideOverlays.kt +++ b/app/src/main/java/com/futo/platformplayer/UISlideOverlays.kt @@ -1,30 +1,21 @@ package com.futo.platformplayer import android.content.ContentResolver -import android.graphics.Color -import android.util.TypedValue import android.view.View import android.view.ViewGroup -import android.widget.FrameLayout -import android.widget.ImageButton -import android.widget.LinearLayout -import android.widget.TextView import com.futo.platformplayer.api.http.ManagedHttpClient import com.futo.platformplayer.api.media.models.ResultCapabilities import com.futo.platformplayer.api.media.models.streams.VideoUnMuxedSourceDescriptor import com.futo.platformplayer.api.media.models.streams.sources.HLSVariantAudioUrlSource -import com.futo.platformplayer.api.media.models.streams.sources.HLSVariantSubtitleUrlSource import com.futo.platformplayer.api.media.models.streams.sources.HLSVariantVideoUrlSource import com.futo.platformplayer.api.media.models.streams.sources.IAudioUrlSource import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestAudioSource import com.futo.platformplayer.api.media.models.streams.sources.IHLSManifestSource import com.futo.platformplayer.api.media.models.streams.sources.IVideoUrlSource -import com.futo.platformplayer.api.media.models.streams.sources.SubtitleRawSource import com.futo.platformplayer.api.media.models.subtitles.ISubtitleSource import com.futo.platformplayer.api.media.models.video.IPlatformVideo import com.futo.platformplayer.api.media.models.video.IPlatformVideoDetails import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo -import com.futo.platformplayer.casting.StateCasting import com.futo.platformplayer.downloads.VideoLocal import com.futo.platformplayer.helpers.VideoHelper import com.futo.platformplayer.logging.Logger @@ -40,12 +31,10 @@ import com.futo.platformplayer.views.pills.RoundButton import com.futo.platformplayer.views.pills.RoundButtonGroup import com.futo.platformplayer.views.overlays.slideup.* import com.futo.platformplayer.views.video.FutoVideoPlayerBase -import com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import okhttp3.internal.notifyAll import java.lang.IllegalStateException class UISlideOverlays { diff --git a/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt b/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt index 84dbb104b68b409c82a0629df4428a068b7697b9..9f031ed5eccd2809e3a53bb077c4cd86b6c666ad 100644 --- a/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt +++ b/app/src/main/java/com/futo/platformplayer/activities/MainActivity.kt @@ -515,6 +515,9 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { val url = intent.getStringExtra("VIDEO"); navigate(_fragVideoDetail, url); } + "IMPORT_OPTIONS" -> { + UIDialogs.showImportOptionsDialog(this); + } "TAB" -> { when(intent.getStringExtra("TAB")){ "Sources" -> { @@ -730,18 +733,7 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { if (!newPipeSubsParsed.has("subscriptions") || !newPipeSubsParsed["subscriptions"].isJsonArray) return false;//throw IllegalArgumentException("Invalid NewPipe json structure found"); - val jsonSubs = newPipeSubsParsed["subscriptions"] - val jsonSubsArray = jsonSubs.asJsonArray; - val jsonSubsArrayItt = jsonSubsArray.iterator(); - val subs = mutableListOf<String>() - while(jsonSubsArrayItt.hasNext()) { - val jsonSubObj = jsonSubsArrayItt.next().asJsonObject; - - if(jsonSubObj.has("url")) - subs.add(jsonSubObj["url"].asString); - } - - navigate(_fragImportSubscriptions, subs); + StateBackup.importNewPipeSubs(this, newPipeSubsParsed); } catch(ex: Exception) { Logger.e(TAG, ex.message, ex); @@ -1054,5 +1046,12 @@ class MainActivity : AppCompatActivity, IWithResultLauncher { sourcesIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); return sourcesIntent; } + + fun getImportOptionsIntent(context: Context): Intent { + val sourcesIntent = Intent(context, MainActivity::class.java); + sourcesIntent.action = "IMPORT_OPTIONS"; + sourcesIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + return sourcesIntent; + } } } \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/activities/SettingsActivity.kt b/app/src/main/java/com/futo/platformplayer/activities/SettingsActivity.kt index 8527e2d6de4f67832f1ff5baee7a5e06032c1a1f..e10d855a9115f118a0f54fbc73e8d3522cec7c03 100644 --- a/app/src/main/java/com/futo/platformplayer/activities/SettingsActivity.kt +++ b/app/src/main/java/com/futo/platformplayer/activities/SettingsActivity.kt @@ -5,6 +5,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.view.View +import android.widget.FrameLayout import android.widget.ImageButton import android.widget.LinearLayout import androidx.activity.result.ActivityResult @@ -30,6 +31,8 @@ class SettingsActivity : AppCompatActivity(), IWithResultLauncher { private var _isFinished = false; + lateinit var overlay: FrameLayout; + override fun attachBaseContext(newBase: Context?) { Logger.i("SettingsActivity", "SettingsActivity.attachBaseContext") super.attachBaseContext(StateApp.instance.getLocaleContext(newBase)) @@ -44,6 +47,7 @@ class SettingsActivity : AppCompatActivity(), IWithResultLauncher { _buttonDev = findViewById(R.id.button_dev); _devSets = findViewById(R.id.dev_settings); _loaderView = findViewById(R.id.loader); + overlay = findViewById(R.id.overlay_container); _form.onChanged.subscribe { field, value -> Logger.i("SettingsActivity", "Setting [${field.field?.name}] changed, saving"); diff --git a/app/src/main/java/com/futo/platformplayer/dialogs/ImportOptionsDialog.kt b/app/src/main/java/com/futo/platformplayer/dialogs/ImportOptionsDialog.kt new file mode 100644 index 0000000000000000000000000000000000000000..4abdb16c30cd23901f02be911de78847623a046e --- /dev/null +++ b/app/src/main/java/com/futo/platformplayer/dialogs/ImportOptionsDialog.kt @@ -0,0 +1,73 @@ +package com.futo.platformplayer.dialogs + +import android.app.AlertDialog +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.widget.Button +import com.futo.platformplayer.R +import com.futo.platformplayer.activities.MainActivity +import com.futo.platformplayer.readBytes +import com.futo.platformplayer.states.StateApp +import com.futo.platformplayer.states.StateBackup +import com.futo.platformplayer.views.buttons.BigButton + +class ImportOptionsDialog: AlertDialog { + private val _context: MainActivity; + + private lateinit var _button_import_zip: BigButton; + private lateinit var _button_import_ezip: BigButton; + private lateinit var _button_import_txt: BigButton; + private lateinit var _button_import_newpipe_subs: BigButton; + private lateinit var _button_close: Button; + + + constructor(context: MainActivity): super(context) { + _context = context; + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState); + setContentView(LayoutInflater.from(context).inflate(R.layout.dialog_import_options, null)); + _button_import_zip = findViewById(R.id.button_import_zip); + _button_import_ezip = findViewById(R.id.button_import_ezip); + _button_import_txt = findViewById(R.id.button_import_txt); + _button_import_newpipe_subs = findViewById(R.id.button_import_newpipe_subs); + _button_close = findViewById(R.id.button_cancel); + + _button_import_zip.onClick.subscribe { + dismiss(); + StateApp.instance.requestFileReadAccess(_context, null, "application/zip") { + val zipBytes = it?.readBytes(context) ?: return@requestFileReadAccess; + StateBackup.importZipBytes(_context, StateApp.instance.scope, zipBytes); + }; + } + _button_import_ezip.setOnClickListener { + + } + _button_import_txt.onClick.subscribe { + dismiss(); + StateApp.instance.requestFileReadAccess(_context, null, "text/plain") { + val txtBytes = it?.readBytes(context) ?: return@requestFileReadAccess; + val txt = String(txtBytes); + StateBackup.importTxt(_context, txt); + }; + } + _button_import_newpipe_subs.onClick.subscribe { + dismiss(); + StateApp.instance.requestFileReadAccess(_context, null, "application/json") { + val jsonBytes = it?.readBytes(context) ?: return@requestFileReadAccess; + val json = String(jsonBytes); + StateBackup.importNewPipeSubs(_context, json); + }; + }; + _button_close.setOnClickListener { + dismiss(); + } + } + + override fun dismiss() { + super.dismiss(); + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/CreatorsFragment.kt b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/CreatorsFragment.kt index 8884410800bae0e8b8caa4d9c3f17b8fcc0d65e4..b62098a7bb1f8991477b92cef8c5af0133e83dcc 100644 --- a/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/CreatorsFragment.kt +++ b/app/src/main/java/com/futo/platformplayer/fragment/mainactivity/main/CreatorsFragment.kt @@ -6,8 +6,10 @@ import android.view.View import android.view.ViewGroup import android.widget.AdapterView import android.widget.ArrayAdapter +import android.widget.EditText import android.widget.FrameLayout import android.widget.Spinner +import androidx.core.widget.addTextChangedListener import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.futo.platformplayer.R @@ -21,9 +23,13 @@ class CreatorsFragment : MainFragment() { private var _spinnerSortBy: Spinner? = null; private var _overlayContainer: FrameLayout? = null; + private var _containerSearch: FrameLayout? = null; + private var _editSearch: EditText? = null; override fun onCreateMainView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { val view = inflater.inflate(R.layout.fragment_creators, container, false); + _containerSearch = view.findViewById(R.id.container_search); + _editSearch = view.findViewById(R.id.edit_search); val adapter = SubscriptionAdapter(inflater, getString(R.string.confirm_delete_subscription)); adapter.onClick.subscribe { platformUser -> navigate<ChannelFragment>(platformUser) }; @@ -44,6 +50,10 @@ class CreatorsFragment : MainFragment() { _spinnerSortBy = spinnerSortBy; + _editSearch?.addTextChangedListener { + adapter.query = it.toString(); + } + val recyclerView = view.findViewById<RecyclerView>(R.id.recycler_subscriptions); recyclerView.adapter = adapter; recyclerView.layoutManager = LinearLayoutManager(view.context); @@ -54,6 +64,8 @@ class CreatorsFragment : MainFragment() { super.onDestroyMainView(); _spinnerSortBy = null; _overlayContainer = null; + _editSearch = null; + _containerSearch = null; } companion object { 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 bfdc4836a386417edad4880d514ad27f7c68c578..69b9f2aab73bbaac6a1cd0589b7d6509e2caf6f3 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StateApp.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StateApp.kt @@ -235,14 +235,33 @@ class StateApp { return state; } - fun requestFileReadAccess(activity: IWithResultLauncher, path: Uri?, handle: (DocumentFile?)->Unit) { + fun requestFileReadAccess(activity: IWithResultLauncher, path: Uri?, contentType: String, handle: (DocumentFile?)->Unit) { if(activity is Context) { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT); if(path != null) intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, path); intent.flags = Intent.FLAG_GRANT_WRITE_URI_PERMISSION .or(Intent.FLAG_GRANT_READ_URI_PERMISSION); - + intent.setType(contentType); + activity.launchForResult(intent, 98) { + if(it.resultCode == Activity.RESULT_OK) { + val uri = it.data?.data; + if(uri != null) + handle(DocumentFile.fromSingleUri(activity, uri)); + } + else + UIDialogs.showDialogOk(context, R.drawable.ic_security_pred, "No access granted"); + }; + } + } + fun requestFileCreateAccess(activity: IWithResultLauncher, path: Uri?, contentType: String, handle: (DocumentFile?)->Unit) { + if(activity is Context) { + val intent = Intent(Intent.ACTION_CREATE_DOCUMENT); + if(path != null) + intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, path); + intent.flags = Intent.FLAG_GRANT_WRITE_URI_PERMISSION + .or(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setType(contentType); activity.launchForResult(intent, 98) { if(it.resultCode == Activity.RESULT_OK) { val uri = it.data?.data; diff --git a/app/src/main/java/com/futo/platformplayer/states/StateBackup.kt b/app/src/main/java/com/futo/platformplayer/states/StateBackup.kt index 69fbb5fef8897bd2c395b0c1c9592c380cde2106..b3a7ea3ecbbdf1056d752e44dbe44b677df5dc68 100644 --- a/app/src/main/java/com/futo/platformplayer/states/StateBackup.kt +++ b/app/src/main/java/com/futo/platformplayer/states/StateBackup.kt @@ -8,17 +8,21 @@ import com.futo.platformplayer.R import com.futo.platformplayer.Settings import com.futo.platformplayer.UIDialogs import com.futo.platformplayer.activities.IWithResultLauncher +import com.futo.platformplayer.activities.MainActivity import com.futo.platformplayer.activities.SettingsActivity import com.futo.platformplayer.api.media.models.video.SerializedPlatformVideo import com.futo.platformplayer.copyTo import com.futo.platformplayer.encryption.GPasswordEncryptionProvider import com.futo.platformplayer.encryption.GPasswordEncryptionProviderV0 +import com.futo.platformplayer.fragment.mainactivity.main.ImportSubscriptionsFragment import com.futo.platformplayer.getNowDiffHours import com.futo.platformplayer.logging.Logger import com.futo.platformplayer.readBytes import com.futo.platformplayer.stores.FragmentedStorage import com.futo.platformplayer.stores.v2.ManagedStore import com.futo.platformplayer.writeBytes +import com.google.gson.JsonObject +import com.google.gson.JsonParser import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -52,15 +56,6 @@ class StateBackup { val secondaryBackupFile = dir.findFile("GrayjayBackup.ezip.old") ?: if(create) dir.createFile("grayjay/ezip", "GrayjayBackup.ezip.old") else null; return Pair(mainBackupFile, secondaryBackupFile); } - /* - private fun getAutomaticBackupFiles(): Pair<File, File> { - val dir = StateApp.instance.getExternalRootDirectory(); - if(dir == null) - throw IllegalStateException("Can't access external files"); - return Pair(File(dir, "GrayjayBackup.ezip"), File(dir, "GrayjayBackup.ezip.old")) - }*/ - - fun getAllMigrationStores(): List<ManagedStore<*>> = listOf( StateSubscriptions.instance.toMigrateCheck(), StatePlaylists.instance.toMigrateCheck() @@ -192,7 +187,19 @@ class StateBackup { importZipBytes(context, scope, backupBytes); } - fun startExternalBackup() { + fun saveExternalBackup(activity: IWithResultLauncher) { + val data = export(); + if(activity is Context) + StateApp.instance.requestFileCreateAccess(activity, null, "application/zip") { + if(it == null) { + UIDialogs.toast("Cancelled"); + return@requestFileCreateAccess; + } + it.writeBytes(activity, data.asZip()); + UIDialogs.toast("Export saved"); + }; + } + fun shareExternalBackup() { val data = export(); val now = OffsetDateTime.now(); val exportFile = File( @@ -401,6 +408,46 @@ class StateBackup { ).withCondition { doImport } else null ); } + + fun importTxt(context: MainActivity, text: String, allowFailure: Boolean = false): Boolean { + if(text.startsWith("@/Subscription") || text.startsWith("Subscriptions")) { + val lines = text.split("\n").map { it.trim() }.drop(1).filter { it.isNotEmpty() }; + context.navigate(context.getFragment<ImportSubscriptionsFragment>(), lines); + return true; + } + else if(allowFailure) { + UIDialogs.showGeneralErrorDialog(context, "Unknown text header [${text}]"); + } + return false; + } + fun importNewPipeSubs(context: MainActivity, json: String) { + val newPipeSubsParsed = JsonParser.parseString(json).asJsonObject; + if (!newPipeSubsParsed.has("subscriptions") || !newPipeSubsParsed["subscriptions"].isJsonArray) + UIDialogs.showGeneralErrorDialog(context, "Invalid json"); + else { + importNewPipeSubs(context, newPipeSubsParsed); + } + } + fun importNewPipeSubs(context: MainActivity, obj: JsonObject) { + try { + val jsonSubs = obj["subscriptions"] + val jsonSubsArray = jsonSubs.asJsonArray; + val jsonSubsArrayItt = jsonSubsArray.iterator(); + val subs = mutableListOf<String>() + while(jsonSubsArrayItt.hasNext()) { + val jsonSubObj = jsonSubsArrayItt.next().asJsonObject; + + if(jsonSubObj.has("url")) + subs.add(jsonSubObj["url"].asString); + } + + context.navigate(context.getFragment<ImportSubscriptionsFragment>(), subs); + } + catch(ex: Exception) { + Logger.e("StateBackup", ex.message, ex); + UIDialogs.showGeneralErrorDialog(context, context.getString(R.string.failed_to_parse_newpipe_subscriptions), ex); + } + } } class ExportStructure( diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/CommentViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/CommentViewHolder.kt index 89c3c1bb1039828ae953c5dc8c630bde13e1b0c0..68103c99a43787edbbc80fd60dc1e7b0a40954bf 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/CommentViewHolder.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/CommentViewHolder.kt @@ -117,7 +117,8 @@ class CommentViewHolder : ViewHolder { val rating = comment.rating; if (rating is RatingLikeDislikes) { - _layoutComment.alpha = if (rating.dislikes > 2 && rating.dislikes.toFloat() / (rating.likes + rating.dislikes).toFloat() >= 0.7f) 0.5f else 1.0f; + _layoutComment.alpha = if (Settings.instance.comments.badReputationCommentsFading && + rating.dislikes > 2 && rating.dislikes.toFloat() / (rating.likes + rating.dislikes).toFloat() >= 0.7f) 0.5f else 1.0f; } else { _layoutComment.alpha = 1.0f; } diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/CommentWithReferenceViewHolder.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/CommentWithReferenceViewHolder.kt index f0581abd1fb785814439b3926cbcaa7ff8637d03..c47db9f64a4f7da433d6d8f35be3221fdbc130ac 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/CommentWithReferenceViewHolder.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/CommentWithReferenceViewHolder.kt @@ -23,7 +23,6 @@ import com.futo.platformplayer.views.pills.PillRatingLikesDislikes import com.futo.polycentric.core.Opinion import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import userpackage.Protocol import java.util.IdentityHashMap class CommentWithReferenceViewHolder : ViewHolder { @@ -135,7 +134,8 @@ class CommentWithReferenceViewHolder : ViewHolder { val rating = comment.rating; if (rating is RatingLikeDislikes) { - _layoutComment.alpha = if (rating.dislikes > 2 && rating.dislikes.toFloat() / (rating.likes + rating.dislikes).toFloat() >= 0.7f) 0.5f else 1.0f; + _layoutComment.alpha = if (Settings.instance.comments.badReputationCommentsFading && + rating.dislikes > 2 && rating.dislikes.toFloat() / (rating.likes + rating.dislikes).toFloat() >= 0.7f) 0.5f else 1.0f; } else { _layoutComment.alpha = 1.0f; } diff --git a/app/src/main/java/com/futo/platformplayer/views/adapters/SubscriptionAdapter.kt b/app/src/main/java/com/futo/platformplayer/views/adapters/SubscriptionAdapter.kt index f7a63d535c246885cfc6580ea8e331e9cf35b9dc..fe33c0dac43125c963d3d092090e68658d692b9d 100644 --- a/app/src/main/java/com/futo/platformplayer/views/adapters/SubscriptionAdapter.kt +++ b/app/src/main/java/com/futo/platformplayer/views/adapters/SubscriptionAdapter.kt @@ -15,11 +15,16 @@ class SubscriptionAdapter : RecyclerView.Adapter<SubscriptionViewHolder> { var onClick = Event1<Subscription>(); var onSettings = Event1<Subscription>(); - var sortBy: Int = 3 + var sortBy: Int = 5 set(value) { field = value updateDataset() } + var query: String? = null + set(value) { + field = value; + updateDataset(); + } constructor(inflater: LayoutInflater, confirmationMessage: String) : super() { _inflater = inflater; @@ -53,6 +58,7 @@ class SubscriptionAdapter : RecyclerView.Adapter<SubscriptionViewHolder> { } private fun updateDataset() { + val queryLower = query?.lowercase() ?: ""; _sortedDataset = when (sortBy) { 0 -> StateSubscriptions.instance.getSubscriptions().sortedBy({ u -> u.channel.name.lowercase() }) 1 -> StateSubscriptions.instance.getSubscriptions().sortedByDescending({ u -> u.channel.name.lowercase() }) @@ -61,7 +67,9 @@ class SubscriptionAdapter : RecyclerView.Adapter<SubscriptionViewHolder> { 4 -> StateSubscriptions.instance.getSubscriptions().sortedBy { it.playbackSeconds } 5 -> StateSubscriptions.instance.getSubscriptions().sortedByDescending { it.playbackSeconds } else -> throw IllegalStateException("Invalid sorting algorithm selected."); - }.toList(); + } + .filter { (queryLower.isNullOrBlank() || it.channel.name.lowercase().contains(queryLower)) } + .toList(); notifyDataSetChanged(); } diff --git a/app/src/main/java/com/futo/platformplayer/views/overlays/slideup/SlideUpMenuItem.kt b/app/src/main/java/com/futo/platformplayer/views/overlays/slideup/SlideUpMenuItem.kt index 0cb714903b90679b599fa5fc67a5b25677d34a63..373132c403440bebc0cf9a5c5f6a691b13cb198e 100644 --- a/app/src/main/java/com/futo/platformplayer/views/overlays/slideup/SlideUpMenuItem.kt +++ b/app/src/main/java/com/futo/platformplayer/views/overlays/slideup/SlideUpMenuItem.kt @@ -25,7 +25,7 @@ class SlideUpMenuItem : RelativeLayout { init(); } - constructor(context: Context, imageRes: Int = 0, mainText: String, subText: String = "", tag: Any, call: (()->Unit)? = null, invokeParent: Boolean = true): super(context){ + constructor(context: Context, imageRes: Int = 0, mainText: String, subText: String = "", tag: Any?, call: (()->Unit)? = null, invokeParent: Boolean = true): super(context){ init(); _image.setImageResource(imageRes); _text.text = mainText; diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index ac34014a5695173e9e9eee482ab89546b4b4615c..b0bf134b47f3ae0d08e6d097aa3731edb36e8774 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -1,89 +1,105 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:paddingStart="20dp" - android:paddingEnd="20dp" - android:background="@color/black"> - + android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:gravity="center_vertical" - android:paddingTop="20dp" - android:paddingBottom="15dp"> - - <ImageButton - android:id="@+id/button_back" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:paddingRight="20dp" - app:srcCompat="@drawable/ic_back_thin_white_16dp" /> - - <FrameLayout - android:layout_width="0dp" + android:layout_height="match_parent" + android:orientation="vertical" + android:paddingStart="20dp" + android:paddingEnd="20dp" + android:background="@color/black"> + <LinearLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_weight="1"> + android:orientation="horizontal" + android:gravity="center_vertical" + android:paddingTop="20dp" + android:paddingBottom="15dp"> - <TextView + <ImageButton + android:id="@+id/button_back" android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/settings" - android:textSize="24dp" - android:textColor="@color/white" - android:fontFamily="@font/inter_extra_light" /> - </FrameLayout> + android:layout_height="match_parent" + android:paddingRight="20dp" + app:srcCompat="@drawable/ic_back_thin_white_16dp" /> - <Space - android:layout_width="20dp" - android:layout_height="match_parent" /> + <FrameLayout + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1"> - </LinearLayout> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/settings" + android:textSize="24dp" + android:textColor="@color/white" + android:fontFamily="@font/inter_extra_light" /> + </FrameLayout> - <ScrollView - android:layout_width="match_parent" - android:layout_height="match_parent"> - <LinearLayout - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <com.futo.platformplayer.views.LoaderView - android:id="@+id/loader" - android:layout_marginBottom="15dp" - android:layout_marginTop="15dp" - android:layout_width="match_parent" - android:layout_height="60dp" /> + <Space + android:layout_width="20dp" + android:layout_height="match_parent" /> - <com.futo.platformplayer.views.fields.FieldForm - android:id="@+id/settings_form" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> + </LinearLayout> + <ScrollView + android:layout_width="match_parent" + android:layout_height="match_parent"> <LinearLayout - android:id="@+id/dev_settings" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> - <TextView + <com.futo.platformplayer.views.LoaderView + android:id="@+id/loader" + android:layout_marginBottom="15dp" + android:layout_marginTop="15dp" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textColor="@color/white" - android:textSize="14dp" - android:textAlignment="center" - android:layout_margin="5dp" - android:text="@string/you_re_apparantly_a_developer" /> - <com.google.android.material.button.MaterialButton - android:id="@+id/button_dev" + android:layout_height="60dp" /> + + <com.futo.platformplayer.views.fields.FieldForm + android:id="@+id/settings_form" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/developer_settings" /> + android:layout_height="wrap_content" /> + + <LinearLayout + android:id="@+id/dev_settings" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textColor="@color/white" + android:textSize="14dp" + android:textAlignment="center" + android:layout_margin="5dp" + android:text="@string/you_re_apparantly_a_developer" /> + <com.google.android.material.button.MaterialButton + android:id="@+id/button_dev" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/developer_settings" /> + </LinearLayout> </LinearLayout> - </LinearLayout> - </ScrollView> -</LinearLayout> \ No newline at end of file + </ScrollView> + </LinearLayout> + + + <FrameLayout + android:id="@+id/overlay_container" + android:layout_width="0dp" + android:layout_height="0dp" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + android:visibility="gone" + android:elevation="15dp"> + </FrameLayout> +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_import_options.xml b/app/src/main/res/layout/dialog_import_options.xml index e4b1df2ba29f9b174238bd002699bb9a1ff01008..ad50a314d042fc4b9777d662df863062037de199 100644 --- a/app/src/main/res/layout/dialog_import_options.xml +++ b/app/src/main/res/layout/dialog_import_options.xml @@ -4,7 +4,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:orientation="vertical" android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="wrap_content" android:gravity="center" android:background="@color/gray_1d"> @@ -13,7 +13,7 @@ android:layout_height="wrap_content" android:orientation="vertical" android:gravity="center" - android:paddingTop="40dp"> + android:paddingTop="20dp"> <FrameLayout android:layout_width="wrap_content" @@ -21,8 +21,8 @@ <ImageView android:id="@+id/update_spinner" - android:layout_width="100dp" - android:layout_height="100dp" + android:layout_width="70dp" + android:layout_height="70dp" app:srcCompat="@drawable/ic_move_up" /> <TextView @@ -46,7 +46,19 @@ android:textSize="14dp" android:textColor="@color/white" android:fontFamily="@font/inter_regular" - android:layout_marginTop="30dp" + android:layout_marginTop="10dp" + android:layout_marginStart="30dp" + android:layout_marginEnd="30dp" /> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="You can open and share files directly to Grayjay as well." + android:textAlignment="center" + android:textSize="13dp" + android:textColor="@color/white" + android:fontFamily="@font/inter_regular" + android:layout_marginTop="10dp" android:layout_marginStart="30dp" android:layout_marginEnd="30dp" /> @@ -55,39 +67,47 @@ android:layout_height="wrap_content" android:orientation="vertical" android:gravity="center" - android:padding="10dp" - android:layout_marginTop="28dp" - android:layout_marginBottom="28dp"> + android:paddingBottom="10dp" + android:paddingTop="10dp" + android:layout_marginTop="5dp" + android:layout_marginBottom="10dp"> <com.futo.platformplayer.views.buttons.BigButton + android:id="@+id/button_import_zip" android:layout_width="match_parent" android:layout_height="wrap_content" + android:scaleY="0.9" + android:scaleX="0.9" app:buttonIcon="@drawable/ic_zip" app:buttonText="Import Grayjay export (.zip)" - android:layout_margin="5dp" app:buttonBackground="@drawable/background_big_button_black" app:buttonSubText="Pick a Grayjay export zip file" /> <com.futo.platformplayer.views.buttons.BigButton + android:id="@+id/button_import_ezip" android:layout_width="match_parent" android:layout_height="wrap_content" app:buttonIcon="@drawable/ic_encrypted" + android:scaleY="0.9" + android:scaleX="0.9" android:alpha="0.5" app:buttonBackground="@drawable/background_big_button_black" app:buttonText="Import Grayjay Auto-Backup (.ezip)" - android:layout_margin="5dp" app:buttonSubText="Pick a Grayjay auto-backup encrypted zip file" /> <com.futo.platformplayer.views.buttons.BigButton + android:id="@+id/button_import_txt" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="5dp" + android:scaleY="0.9" + android:scaleX="0.9" app:buttonIcon="@drawable/ic_lines" - android:alpha="0.5" app:buttonBackground="@drawable/background_big_button_black" app:buttonText="Import Line Text file (.txt)" app:buttonSubText="Pick a text file with one entry per line" /> <com.futo.platformplayer.views.buttons.BigButton + android:id="@+id/button_import_newpipe_subs" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="5dp" + android:scaleY="0.9" + android:scaleX="0.9" app:buttonIcon="@drawable/ic_play" app:buttonBackground="@drawable/background_big_button_black" app:buttonText="Import NewPipe Subscriptions (.json)" @@ -98,7 +118,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/close" - android:layout_marginTop="20dp" + android:layout_marginTop="10dp" android:textSize="14dp" android:fontFamily="@font/inter_regular" android:textColor="@color/colorPrimary" diff --git a/app/src/main/res/layout/dialog_multi_button.xml b/app/src/main/res/layout/dialog_multi_button.xml index 70c9e86e140ba391af5a0a7bfe00bf03bc5f4943..87365f8c096657585a377d751db7191652d039aa 100644 --- a/app/src/main/res/layout/dialog_multi_button.xml +++ b/app/src/main/res/layout/dialog_multi_button.xml @@ -50,7 +50,7 @@ android:layout_width="match_parent" android:textColor="#AAAAAA" android:fontFamily="monospace" - android:text="source.getVideoDetails(...)" + android:text="" android:textAlignment="center" android:layout_marginStart="30dp" android:layout_marginEnd="30dp" @@ -58,6 +58,7 @@ android:padding="5dp" android:background="#111111" android:textSize="8dp" + android:visibility="gone" android:layout_height="wrap_content" tools:ignore="HardcodedText" /> diff --git a/app/src/main/res/layout/fragment_creators.xml b/app/src/main/res/layout/fragment_creators.xml index f8bc6aa0849d926ced19ad011c17fda30b889ce7..b39ad6f429b6a65675258dd86f2e0fc3db4153a5 100644 --- a/app/src/main/res/layout/fragment_creators.xml +++ b/app/src/main/res/layout/fragment_creators.xml @@ -16,33 +16,67 @@ <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" - android:layout_height="40dp" + android:layout_height="100dp" android:minHeight="0dp" app:layout_scrollFlags="scroll" app:contentInsetStart="0dp" app:contentInsetEnd="0dp"> - <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:gravity="center_vertical"> + android:orientation="vertical"> - <TextView - android:layout_width="wrap_content" + <!--Search Text--> + <FrameLayout + android:id="@+id/container_search" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:textSize="14dp" - android:textColor="@color/gray_ac" - android:fontFamily="@font/inter_light" - android:text="@string/sort_by" - android:paddingStart="20dp" /> + android:visibility="visible" + android:layout_margin="10dp"> + + <EditText + android:id="@+id/edit_search" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="text" + android:imeOptions="actionDone" + android:singleLine="true" + android:hint="Search" + android:paddingEnd="46dp" /> + + <ImageButton + android:id="@+id/button_clear_search" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:paddingStart="18dp" + android:paddingEnd="18dp" + android:layout_gravity="right|center_vertical" + android:visibility="invisible" + android:src="@drawable/ic_clear_16dp" /> + </FrameLayout> - <Spinner - android:id="@+id/spinner_sortby" - android:layout_width="0dp" - android:layout_weight="1" + <LinearLayout + android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingStart="20dp" - android:paddingEnd="20dp" /> + android:gravity="center_vertical"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="14dp" + android:textColor="@color/gray_ac" + android:fontFamily="@font/inter_light" + android:text="@string/sort_by" + android:paddingStart="20dp" /> + + <Spinner + android:id="@+id/spinner_sortby" + android:layout_width="0dp" + android:layout_weight="1" + android:layout_height="wrap_content" + android:paddingStart="20dp" + android:paddingEnd="20dp" /> + </LinearLayout> </LinearLayout> </androidx.appcompat.widget.Toolbar> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 507a44f94e8d2228b2856f5286b967f879a207db..7f8fde77881f331623910745f82b2650d73d800c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -370,6 +370,8 @@ <string name="preferred_preview_quality_description">Default quality while previewing a video in a feed</string> <string name="primary_language">Primary Language</string> <string name="default_comment_section">Default Comment Section</string> + <string name="bad_reputation_comments_fading">Bad Reputation Comment Fading</string> + <string name="bad_reputation_comments_fading_description">If comments with a very bad reputation should be faded. Disabling may worsen experience.</string> <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> diff --git a/app/src/stable/assets/sources/youtube b/app/src/stable/assets/sources/youtube index 8f10daba1ef9cbcd99f3c640d86808f8c94aa84a..07aa5a9aab441657f89ae14ff3cfd9d9ca977fe6 160000 --- a/app/src/stable/assets/sources/youtube +++ b/app/src/stable/assets/sources/youtube @@ -1 +1 @@ -Subproject commit 8f10daba1ef9cbcd99f3c640d86808f8c94aa84a +Subproject commit 07aa5a9aab441657f89ae14ff3cfd9d9ca977fe6 diff --git a/app/src/unstable/assets/sources/youtube b/app/src/unstable/assets/sources/youtube index 8f10daba1ef9cbcd99f3c640d86808f8c94aa84a..07aa5a9aab441657f89ae14ff3cfd9d9ca977fe6 160000 --- a/app/src/unstable/assets/sources/youtube +++ b/app/src/unstable/assets/sources/youtube @@ -1 +1 @@ -Subproject commit 8f10daba1ef9cbcd99f3c640d86808f8c94aa84a +Subproject commit 07aa5a9aab441657f89ae14ff3cfd9d9ca977fe6