finish po token generation

pull/765/head
deniscerri 1 year ago
parent 90e16d203d
commit 1d560a98ca
No known key found for this signature in database
GPG Key ID: 95C43D517D830350

@ -449,6 +449,20 @@
</intent-filter>
</activity>
<activity
android:name=".ui.more.settings.advanced.generateyoutubepotokens.webview.PoTokenWebViewLoginActivity"
android:configChanges="locale"
android:exported="true"
android:launchMode="singleTask"
android:noHistory="true"
android:parentActivityName=".ui.more.settings.SettingsActivity"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="ytdlnis.PoTokenWebViewLoginActivity" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<receiver android:name=".receiver.CancelDownloadNotificationReceiver" />
<receiver android:name=".receiver.CancelWorkReceiver" />
<receiver android:name=".receiver.PauseDownloadNotificationReceiver" />

@ -4,5 +4,6 @@ data class YoutubeGeneratePoTokenItem(
var enabled: Boolean,
var clients: MutableList<String>,
var poTokens: MutableList<YoutubePoTokenItem>,
var visitorData: String
var visitorData: String,
var useVisitorData: Boolean
)

@ -6,7 +6,6 @@ import android.content.ClipboardManager
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteDatabase.OPEN_READONLY
import android.os.Environment
import android.util.Log
import android.webkit.CookieManager
import androidx.lifecycle.AndroidViewModel
@ -19,13 +18,10 @@ import com.deniscerri.ytdl.database.models.CookieItem
import com.deniscerri.ytdl.database.repository.CookieRepository
import com.deniscerri.ytdl.ui.more.WebViewActivity
import com.deniscerri.ytdl.util.FileUtil
import com.google.gson.GsonBuilder
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.io.File
import java.util.Calendar
import java.util.Date

@ -45,7 +45,6 @@ import com.deniscerri.ytdl.ui.adapter.SearchSuggestionsAdapter
import com.deniscerri.ytdl.ui.more.WebViewActivity
import com.deniscerri.ytdl.util.Extensions.enableFastScroll
import com.deniscerri.ytdl.util.Extensions.isURL
import com.deniscerri.ytdl.util.FileUtil
import com.deniscerri.ytdl.util.NotificationUtil
import com.deniscerri.ytdl.util.ThemeUtil
import com.deniscerri.ytdl.util.UiUtil
@ -67,7 +66,6 @@ import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
import java.net.URL
import kotlin.collections.ArrayList

@ -60,7 +60,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.net.URL

@ -17,7 +17,6 @@ import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment
@ -45,7 +44,6 @@ import com.google.android.material.chip.Chip
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.materialswitch.MaterialSwitch
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textfield.TextInputLayout
import it.xabaras.android.recyclerview.swipedecorator.RecyclerViewSwipeDecorator
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

@ -77,6 +77,7 @@ class WebViewActivity : BaseActivity() {
generateBtn = toolbar.findViewById(R.id.generate)
webViewCompose = findViewById(R.id.webview_compose)
cookieManager = CookieManager.getInstance()
cookieManager.removeAllCookies(null)
preferences = PreferenceManager.getDefaultSharedPreferences(this@WebViewActivity)
@ -92,7 +93,6 @@ class WebViewActivity : BaseActivity() {
}
toolbar.setNavigationOnClickListener {
cookieManager.flush()
onBackPressedDispatcher.onBackPressed()
}
@ -119,7 +119,6 @@ class WebViewActivity : BaseActivity() {
}
}
}
cookieManager.removeAllCookies(null)
withContext(Dispatchers.Main) {
this@WebViewActivity.setResult(RESULT_OK)
this@WebViewActivity.finish()

@ -45,8 +45,7 @@ class AdvancedSettingsFragment : BaseSettingsFragment() {
}
findPreference<Preference>("youtube_other_extractor_args")?.apply {
fun setValue() {
val pf = prefs.getString("youtube_other_extractor_args", "")!!
fun setValue(pf: String) {
if (pf.length > 50) {
this.summary = pf.take(50) + "..."
}else {
@ -54,14 +53,11 @@ class AdvancedSettingsFragment : BaseSettingsFragment() {
}
}
setOnPreferenceChangeListener { preference, newValue ->
setValue()
setOnPreferenceChangeListener { _, newValue ->
setValue(newValue as String)
true
}
setValue()
setValue(prefs.getString("youtube_other_extractor_args", "")!!)
}
val formatImportanceAudio: Preference? = findPreference("format_importance_audio")

@ -1,36 +1,37 @@
package com.deniscerri.ytdl.ui.more.settings.advanced
package com.deniscerri.ytdl.ui.more.settings.advanced.generateyoutubepotokens
import android.app.Activity
import android.app.ActivityManager
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import androidx.work.WorkManager
import com.afollestad.materialdialogs.utils.MDUtil.getStringArray
import com.deniscerri.ytdl.R
import com.deniscerri.ytdl.database.models.YoutubeGeneratePoTokenItem
import com.deniscerri.ytdl.database.models.YoutubePoTokenItem
import com.deniscerri.ytdl.ui.more.settings.SettingsActivity
import com.deniscerri.ytdl.util.Extensions.getIDFromYoutubeURL
import com.deniscerri.ytdl.ui.more.settings.advanced.generateyoutubepotokens.webview.PoTokenWebViewLoginActivity
import com.deniscerri.ytdl.util.UiUtil
import com.deniscerri.ytdl.util.extractors.potoken.PoTokenGenerator
import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.materialswitch.MaterialSwitch
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.textfield.TextInputLayout
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class GenerateYoutubePoTokensFragment : Fragment() {
private lateinit var settingsActivity: SettingsActivity
private lateinit var preferences: SharedPreferences
private lateinit var configuration : MutableList<YoutubeGeneratePoTokenItem>
private lateinit var workManager : WorkManager
private lateinit var poTokenGenerator: PoTokenGenerator
private val sampleURL = "https://www.youtube.com/watch?v=aqz-KE-bpKQ" // Big Buck Bunny
@ -44,6 +45,7 @@ class GenerateYoutubePoTokensFragment : Fragment() {
settingsActivity.changeTopAppbarTitle(getString(R.string.generate_potokens))
preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
poTokenGenerator = PoTokenGenerator()
workManager = WorkManager.getInstance(requireContext())
return inflater.inflate(R.layout.fragment_generate_youtube_po_token, container, false)
}
@ -59,7 +61,7 @@ class GenerateYoutubePoTokensFragment : Fragment() {
private fun initWeb() {
val conf = configuration.find { it.clients.any { it2 -> it2.contains("web") } }
?: YoutubeGeneratePoTokenItem(false, mutableListOf("mweb"), mutableListOf(), "")
?: YoutubeGeneratePoTokenItem(false, mutableListOf("mweb"), mutableListOf(), "", false)
val switch = requireView().findViewById<MaterialSwitch>(R.id.web_client_switch)
@ -71,8 +73,10 @@ class GenerateYoutubePoTokensFragment : Fragment() {
val playerClientText = requireView().findViewById<TextView>(R.id.content_playerclient)
val regenerate = requireView().findViewById<MaterialButton>(R.id.regenerate_webview_potokens)
val useVisitorData = requireView().findViewById<MaterialSwitch>(R.id.use_visitor_data)
switch.isChecked = conf.enabled
useVisitorData.isEnabled = conf.enabled
fun setValues(conf: YoutubeGeneratePoTokenItem) {
gvs.text = conf.poTokens.find { it.context == "gvs" }?.token ?: ""
@ -121,36 +125,53 @@ class GenerateYoutubePoTokensFragment : Fragment() {
.show()
}
regenerate.setOnClickListener {
lifecycleScope.launch {
val res = withContext(Dispatchers.IO) {
poTokenGenerator.getWebClientPoToken(sampleURL.getIDFromYoutubeURL())
}
if (res == null) {
Snackbar.make(requireView(), getString(R.string.network_error), Snackbar.LENGTH_LONG).show()
}else{
configuration.remove(conf)
conf.poTokens.clear()
conf.poTokens.add(YoutubePoTokenItem("gvs", res.streamingDataPoToken ?: ""))
conf.poTokens.add(YoutubePoTokenItem("player", res.playerRequestPoToken))
conf.visitorData = res.visitorData
configuration.add(conf)
setValues(conf)
preferences.edit().putString("youtube_generated_po_tokens", Gson().toJson(configuration).toString()).apply()
}
val webPoTokenResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val streamingDataPoTokenResult = result.data!!.getStringExtra("streaming_potoken") ?: ""
val playerRequestPoTokenResult = result.data!!.getStringExtra("player_potoken") ?: ""
val visitorDataResult = result.data!!.getStringExtra("visitor_data") ?: ""
configuration.remove(conf)
conf.poTokens.clear()
conf.poTokens.add(YoutubePoTokenItem("gvs", streamingDataPoTokenResult))
conf.poTokens.add(YoutubePoTokenItem("player", playerRequestPoTokenResult))
conf.visitorData = visitorDataResult
configuration.add(conf)
setValues(conf)
preferences.edit().putString("youtube_generated_po_tokens", Gson().toJson(configuration).toString()).apply()
}else {
Snackbar.make(requireView(), R.string.network_error, Snackbar.LENGTH_SHORT).show()
}
}
regenerate.setOnClickListener {
webPoTokenResultLauncher.launch(Intent(requireContext(), PoTokenWebViewLoginActivity::class.java))
}
switch.setOnClickListener {
configuration.remove(conf)
conf.enabled = switch.isChecked
useVisitorData.isEnabled = switch.isChecked
configuration.add(conf)
preferences.edit().putString("youtube_generated_po_tokens", Gson().toJson(configuration).toString()).apply()
if (conf.poTokens.isEmpty()) {
regenerate.performClick()
}
}
useVisitorData?.apply {
this.isChecked = conf.useVisitorData
setOnCheckedChangeListener { _, b ->
configuration.remove(conf)
conf.useVisitorData = b
configuration.add(conf)
preferences.edit().putString("youtube_generated_po_tokens", Gson().toJson(configuration).toString()).apply()
}
}
}
}

@ -1,13 +1,12 @@
package com.deniscerri.ytdl.util.extractors.potoken
package com.deniscerri.ytdl.ui.more.settings.advanced.generateyoutubepotokens
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.webkit.CookieManager
import androidx.preference.PreferenceManager
import com.deniscerri.ytdl.App
import com.deniscerri.ytdl.BuildConfig
import com.deniscerri.ytdl.util.extractors.potoken.webview.PoTokenWebView
import com.deniscerri.ytdl.ui.more.settings.advanced.generateyoutubepotokens.webview.PoTokenWebView
import kotlinx.coroutines.runBlocking
import org.schabi.newpipe.extractor.NewPipe
import org.schabi.newpipe.extractor.services.youtube.InnertubeClientRequestInfo

@ -1,6 +1,5 @@
package com.deniscerri.ytdl.util.extractors.potoken.webview
package com.deniscerri.ytdl.ui.more.settings.advanced.generateyoutubepotokens.webview
import com.deniscerri.ytdl.util.extractors.potoken.PoTokenException
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonObject

@ -1,4 +1,4 @@
package com.deniscerri.ytdl.util.extractors.potoken
package com.deniscerri.ytdl.ui.more.settings.advanced.generateyoutubepotokens.webview
class PoTokenException(message: String) : Exception(message)

@ -1,4 +1,4 @@
package com.deniscerri.ytdl.util.extractors.potoken.webview
package com.deniscerri.ytdl.ui.more.settings.advanced.generateyoutubepotokens.webview
import android.content.Context
import android.os.Build
@ -9,7 +9,6 @@ import android.webkit.WebChromeClient
import android.webkit.WebView
import androidx.annotation.MainThread
import androidx.collection.ArrayMap
import androidx.preference.PreferenceManager
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
@ -18,13 +17,10 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import com.deniscerri.ytdl.BuildConfig
import com.deniscerri.ytdl.util.extractors.potoken.BadWebViewException
import com.deniscerri.ytdl.util.extractors.potoken.PoTokenException
import com.deniscerri.ytdl.util.extractors.potoken.buildExceptionForJsError
import com.deniscerri.ytdl.util.extractors.potoken.webview.JavascriptUtil.parseChallengeData
import com.deniscerri.ytdl.util.extractors.potoken.webview.JavascriptUtil.parseIntegrityTokenData
import com.deniscerri.ytdl.util.extractors.potoken.webview.JavascriptUtil.stringToU8
import com.deniscerri.ytdl.util.extractors.potoken.webview.JavascriptUtil.u8ToBase64
import com.deniscerri.ytdl.ui.more.settings.advanced.generateyoutubepotokens.webview.JavascriptUtil.parseChallengeData
import com.deniscerri.ytdl.ui.more.settings.advanced.generateyoutubepotokens.webview.JavascriptUtil.parseIntegrityTokenData
import com.deniscerri.ytdl.ui.more.settings.advanced.generateyoutubepotokens.webview.JavascriptUtil.stringToU8
import com.deniscerri.ytdl.ui.more.settings.advanced.generateyoutubepotokens.webview.JavascriptUtil.u8ToBase64
import okhttp3.Headers.Companion.toHeaders
import okhttp3.OkHttpClient
import okhttp3.RequestBody.Companion.toRequestBody

@ -0,0 +1,141 @@
package com.deniscerri.ytdl.ui.more.settings.advanced.generateyoutubepotokens.webview
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.webkit.CookieManager
import android.webkit.WebView
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.core.view.forEach
import androidx.lifecycle.lifecycleScope
import com.deniscerri.ytdl.R
import com.deniscerri.ytdl.ui.BaseActivity
import com.deniscerri.ytdl.ui.more.settings.advanced.generateyoutubepotokens.PoTokenGenerator
import com.google.accompanist.web.AccompanistWebChromeClient
import com.google.accompanist.web.AccompanistWebViewClient
import com.google.accompanist.web.WebView
import com.google.accompanist.web.rememberWebViewState
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.button.MaterialButton
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class PoTokenWebViewLoginActivity : BaseActivity() {
private lateinit var webView: WebView
private lateinit var webViewCompose: ComposeView
private lateinit var toolbar: MaterialToolbar
private lateinit var generateBtn: MaterialButton
private lateinit var cookieManager: CookieManager
private lateinit var webViewClient: AccompanistWebViewClient
private val sampleVideoID = "aqz-KE-bpKQ" //Big Buck Bunny
@SuppressLint("SetJavaScriptEnabled")
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.webview_activity)
lifecycleScope.launch {
val appbar = findViewById<AppBarLayout>(R.id.webview_appbarlayout)
toolbar = appbar.findViewById(R.id.webviewToolbar)
toolbar.menu.forEach { it.isVisible = false }
generateBtn = toolbar.findViewById(R.id.generate)
webViewCompose = findViewById(R.id.webview_compose)
cookieManager = CookieManager.getInstance()
webViewClient = object : AccompanistWebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
kotlin.runCatching {
toolbar.title = view?.title ?: ""
}
}
}
toolbar.setNavigationOnClickListener {
finishAndRemoveTask()
}
generateBtn.setOnClickListener {
generateBtn.isEnabled = false
lifecycleScope.launch {
val generator = PoTokenGenerator()
val res = withContext(Dispatchers.IO) {
generator.getWebClientPoToken(sampleVideoID)
}
if (res == null) {
setResult(RESULT_CANCELED)
}else {
val intent = Intent()
intent.putExtra("visitor_data", res.visitorData)
intent.putExtra("player_potoken", res.playerRequestPoToken)
intent.putExtra("streaming_potoken", res.streamingDataPoToken)
setResult(RESULT_OK, intent)
}
webView.clearCache(true)
// ensures that the WebView isn't doing anything when destroying it
webView.loadUrl("about:blank")
webView.onPause()
webView.removeAllViews()
webView.destroy()
finish()
}
}
webViewCompose.apply {
setContent { WebViewView() }
}
}
}
@SuppressLint("SetJavaScriptEnabled", "JavascriptInterface")
@Composable
fun WebViewView() {
val webViewChromeClient = remember {
object : AccompanistWebChromeClient() {
}
}
Scaffold(modifier = Modifier.fillMaxSize()) { paddingValues ->
WebView(
state = rememberWebViewState("https://youtube.com/account"), client = webViewClient, chromeClient = webViewChromeClient,
modifier = Modifier
.padding(paddingValues)
.fillMaxSize(),
captureBackPresses = false, factory = { context ->
WebView(context).apply {
webView = this
settings.run {
javaScriptEnabled = true
domStorageEnabled = true
javaScriptCanOpenWindowsAutomatically = true
if (Build.VERSION.SDK_INT >= 26) {
safeBrowsingEnabled = true
}
}
cookieManager.setAcceptThirdPartyCookies(this, true)
}
}
)
}
}
companion object {
const val TAG = "PoTokenWebViewActivity"
}
}

@ -1321,7 +1321,7 @@ class YTDLPUtil(private val context: Context, private val commandTemplateDao: Co
}
}
if (dataSyncID.isBlank()) {
if (dataSyncID.isBlank() && value.useVisitorData) {
extractorArgs.add("player_skip=webpage,configs")
extractorArgs.add("visitor_data=${value.visitorData}")
}

@ -10,7 +10,7 @@ import com.deniscerri.ytdl.database.models.ResultItem
import com.deniscerri.ytdl.database.viewmodel.ResultViewModel
import com.deniscerri.ytdl.util.Extensions.getIDFromYoutubeURL
import com.deniscerri.ytdl.util.Extensions.toStringDuration
import com.deniscerri.ytdl.util.extractors.potoken.PoTokenGenerator
import com.deniscerri.ytdl.ui.more.settings.advanced.generateyoutubepotokens.PoTokenGenerator
import com.google.gson.Gson
import okhttp3.OkHttpClient
import org.json.JSONException
@ -24,7 +24,6 @@ import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler
import org.schabi.newpipe.extractor.localization.Localization
import org.schabi.newpipe.extractor.playlist.PlaylistInfo
import org.schabi.newpipe.extractor.search.SearchInfo
import org.schabi.newpipe.extractor.services.youtube.PoTokenResult
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory
import org.schabi.newpipe.extractor.stream.StreamInfo

@ -0,0 +1,37 @@
package com.deniscerri.ytdl.work
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
import android.os.Build
import android.os.Environment
import android.os.Handler
import android.os.Looper
import android.widget.Toast
import androidx.work.ForegroundInfo
import androidx.work.Worker
import androidx.work.WorkerParameters
import com.deniscerri.ytdl.App
import com.deniscerri.ytdl.MainActivity
import com.deniscerri.ytdl.R
import com.deniscerri.ytdl.util.FileUtil
import com.deniscerri.ytdl.util.NotificationUtil
import java.io.File
import java.nio.file.Files
import java.nio.file.StandardCopyOption
class AutoGenerateWebPoTokenWorker(
private val context: Context,
workerParams: WorkerParameters
) : Worker(context, workerParams) {
override fun doWork(): Result {
return Result.success()
}
companion object {
const val TAG = "AutoGenerateWebPoTokenWorker"
}
}

@ -222,6 +222,16 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/use_visitor_data"
android:layout_width="match_parent"
android:paddingHorizontal="20dp"
android:layout_height="70dp"
android:checked="true"
android:textSize="16sp"
android:textStyle="bold"
android:text="Use Visitor Data"
tools:ignore="HardcodedText" />
<TextView
android:layout_width="wrap_content"

@ -6,65 +6,56 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinatorLayout2"
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/webview_appbarlayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
android:background="@android:color/transparent"
android:elevation="0dp"
android:fitsSystemWindows="true"
app:elevation="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
app:layout_constraintTop_toTopOf="parent"
app:liftOnScroll="false">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/webview_appbarlayout"
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/webviewToolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:elevation="0dp"
android:fitsSystemWindows="true"
app:elevation="0dp"
app:liftOnScroll="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/webviewToolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll"
app:menu="@menu/webview_menu"
app:navigationContentDescription="@string/back"
app:navigationIcon="?attr/homeAsUpIndicator">
<Button
android:id="@+id/generate"
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginHorizontal="10dp"
android:autoLink="all"
android:text="@string/ok"
app:icon="@drawable/ic_check"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="PrivateResource" />
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll"
app:menu="@menu/webview_menu"
app:navigationContentDescription="@string/back"
app:navigationIcon="?attr/homeAsUpIndicator">
<Button
android:id="@+id/generate"
style="@style/Widget.Material3.Button.ElevatedButton.Icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginHorizontal="10dp"
android:autoLink="all"
android:text="@string/ok"
app:icon="@drawable/ic_check"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="PrivateResource" />
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.AppBarLayout>
<androidx.compose.ui.platform.ComposeView
android:id="@+id/webview_compose"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/coordinatorLayout2"
tools:composableName="com.deniscerri.ytdl.ui.more.WebViewActivity.WebViewView" />
app:layout_constraintTop_toBottomOf="@+id/webview_appbarlayout"
tools:composableName="com.deniscerri.ytdl.ui.more.settings.advanced.generateyoutubepotokens.webview.WebViewActivity.WebViewView" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -70,6 +70,6 @@
android:label="ChangeLogFragment" />
<fragment
android:id="@+id/generateYoutubePoTokensFragment"
android:name="com.deniscerri.ytdl.ui.more.settings.advanced.GenerateYoutubePoTokensFragment"
android:name="com.deniscerri.ytdl.ui.more.settings.advanced.generateyoutubepotokens.GenerateYoutubePoTokensFragment"
android:label="GenerateYoutubePoTokensFragment" />
</navigation>
Loading…
Cancel
Save