diff --git a/app/build.gradle b/app/build.gradle index d78fb37d..343b6ecd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -36,7 +36,7 @@ android { targetSdkVersion 30 versionCode 47 - versionName "2.9.25" + versionName "2.10.25" resValue "string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}" @@ -89,12 +89,13 @@ repositories { dependencies { implementation 'com.google.android.mediahome:video:1.0.0' + implementation 'androidx.test.ext:junit-ktx:1.1.3' testImplementation 'org.json:json:20180813' implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.4.1' - implementation 'com.google.android.material:material:1.5.0' + implementation 'com.google.android.material:material:1.5.0' // dont change this to 1.6.0 it looks ugly af implementation 'androidx.constraintlayout:constraintlayout:2.1.3' implementation 'androidx.navigation:navigation-fragment-ktx:2.5.0-beta01' implementation 'androidx.navigation:navigation-ui-ktx:2.5.0-beta01' @@ -108,8 +109,6 @@ dependencies { // implementation 'org.jsoup:jsoup:1.13.1' // implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.12.3" - implementation "com.google.android.material:material:1.5.0" - implementation "androidx.preference:preference-ktx:1.2.0" implementation 'com.github.bumptech.glide:glide:4.13.1' diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index c5fbf0bd..6d871f8e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -128,7 +128,15 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { R.id.navigation_search, R.id.navigation_downloads, R.id.navigation_settings, - R.id.navigation_download_child + R.id.navigation_download_child, + R.id.navigation_subtitles, + R.id.navigation_chrome_subtitles, + R.id.navigation_settings_nginx, + R.id.navigation_settings_player, + R.id.navigation_settings_updates, + R.id.navigation_settings_ui, + R.id.navigation_settings_account, + R.id.navigation_settings_lang, ).contains(destination.id) val landscape = when (resources.configuration.orientation) { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt index 1a1b96fa..c6cdb777 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/download/DownloadFragment.kt @@ -9,7 +9,6 @@ import androidx.appcompat.app.AppCompatActivity import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider -import androidx.navigation.fragment.NavHostFragment import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.lagradost.cloudstream3.R @@ -22,6 +21,7 @@ import com.lagradost.cloudstream3.utils.DOWNLOAD_EPISODE_CACHE import com.lagradost.cloudstream3.utils.DataStore import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard +import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.VideoDownloadHelper import com.lagradost.cloudstream3.utils.VideoDownloadManager import kotlinx.android.synthetic.main.fragment_downloads.* @@ -57,7 +57,7 @@ class DownloadFragment : Fragment() { } override fun onDestroy() { - if(downloadDeleteEventListener != null) { + if (downloadDeleteEventListener != null) { VideoDownloadManager.downloadDeleteEvent -= downloadDeleteEventListener!! downloadDeleteEventListener = null } @@ -80,17 +80,26 @@ class DownloadFragment : Fragment() { } observe(downloadsViewModel.availableBytes) { download_free_txt?.text = - getString(R.string.storage_size_format).format(getString(R.string.free_storage), getBytesAsText(it)) + getString(R.string.storage_size_format).format( + getString(R.string.free_storage), + getBytesAsText(it) + ) download_free?.setLayoutWidth(it) } observe(downloadsViewModel.usedBytes) { download_used_txt?.text = - getString(R.string.storage_size_format).format(getString(R.string.used_storage), getBytesAsText(it)) + getString(R.string.storage_size_format).format( + getString(R.string.used_storage), + getBytesAsText(it) + ) download_used?.setLayoutWidth(it) } observe(downloadsViewModel.downloadBytes) { download_app_txt?.text = - getString(R.string.storage_size_format).format(getString(R.string.app_storage), getBytesAsText(it)) + getString(R.string.storage_size_format).format( + getString(R.string.app_storage), + getBytesAsText(it) + ) download_app?.setLayoutWidth(it) download_storage_appbar?.visibility = View.VISIBLE } @@ -112,16 +121,21 @@ class DownloadFragment : Fragment() { if (click.data.type.isMovieType()) { //wont be called } else { - val folder = DataStore.getFolderName(DOWNLOAD_EPISODE_CACHE, click.data.id.toString()) - val navHostFragment = activity?.supportFragmentManager?.findFragmentById(R.id.nav_host_fragment) as? NavHostFragment? - navHostFragment?.navController?.navigate( - R.id.navigation_download_child, + val folder = DataStore.getFolderName( + DOWNLOAD_EPISODE_CACHE, + click.data.id.toString() + ) + activity?.navigate( + R.id.action_navigation_downloads_to_navigation_download_child, DownloadChildFragment.newInstance(click.data.name, folder) ) } } 1 -> { - (activity as AppCompatActivity?)?.loadResult(click.data.url, click.data.apiName) + (activity as AppCompatActivity?)?.loadResult( + click.data.url, + click.data.apiName + ) } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt index 5e31e912..66a7e6fb 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/FullScreenPlayer.kt @@ -1027,7 +1027,6 @@ open class FullScreenPlayer : AbstractPlayerFragment() { // init variables setPlayBackSpeed(getKey(PLAYBACK_SPEED_KEY) ?: 1.0f) - fastForwardTime = getKey(PLAYBACK_FASTFORWARD) ?: 10000L // handle tv controls playerEventListener = { eventType -> @@ -1086,6 +1085,10 @@ open class FullScreenPlayer : AbstractPlayerFragment() { context?.let { ctx -> val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx) + fastForwardTime = + settingsManager.getInt(ctx.getString(R.string.double_tap_seek_time_key), 10) + .toLong() * 1000L + navigationBarHeight = ctx.getNavigationBarHeight() statusBarHeight = ctx.getStatusBarHeight() diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt index ea21d63e..0687547f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/GeneratorPlayer.kt @@ -52,6 +52,9 @@ class GeneratorPlayer : FullScreenPlayer() { } } + private var titleRez = 3 + private var limitTitle = 0 + private lateinit var viewModel: PlayerGeneratorViewModel //by activityViewModels() private lateinit var sync: SyncViewModel private var currentLinks: Set> = setOf() @@ -550,12 +553,7 @@ class GeneratorPlayer : FullScreenPlayer() { tvType = meta.tvType } } - //Get limit of characters on Video Title - var limitTitle = 0 - context?.let { - val settingsManager = PreferenceManager.getDefaultSharedPreferences(it) - limitTitle = settingsManager.getInt(getString(R.string.prefer_limit_title_key), 0) - } + //Generate video title var playerVideoTitle = if (headerName != null) { (headerName + @@ -577,7 +575,7 @@ class GeneratorPlayer : FullScreenPlayer() { val differenceInLength = playerVideoTitle.length - limitTitle val margin = 3 //If the difference is smaller than or equal to this value, ignore it if (limitTitle > 0 && differenceInLength > margin) { - playerVideoTitle = playerVideoTitle.substring(0, limitTitle-1) + "..." + playerVideoTitle = playerVideoTitle.substring(0, limitTitle - 1) + "..." } } @@ -589,13 +587,21 @@ class GeneratorPlayer : FullScreenPlayer() { fun setPlayerDimen(widthHeight: Pair?) { val extra = if (widthHeight != null) { val (width, height) = widthHeight - " - ${width}x${height}" + "${width}x${height}" } else { "" } - player_video_title_rez?.text = - (currentSelectedLink?.first?.name ?: currentSelectedLink?.second?.name - ?: "NULL") + extra + + val source = currentSelectedLink?.first?.name ?: currentSelectedLink?.second?.name + ?: "NULL" + + player_video_title_rez?.text = when (titleRez) { + 0 -> "" + 1 -> extra + 2 -> source + 3 -> "$source - $extra" + else -> "" + } } override fun playerDimensionsLoaded(widthHeight: Pair) { @@ -631,6 +637,12 @@ class GeneratorPlayer : FullScreenPlayer() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + context?.let { ctx -> + val settingsManager = PreferenceManager.getDefaultSharedPreferences(ctx) + titleRez = settingsManager.getInt(getString(R.string.prefer_limit_title_rez_key), 3) + limitTitle = settingsManager.getInt(getString(R.string.prefer_limit_title_key), 0) + } + unwrapBundle(savedInstanceState) unwrapBundle(arguments) diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt index dc666f4d..4f608330 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/player/IPlayer.kt @@ -57,7 +57,7 @@ const val PLAYBACK_SPEED = "playback_speed" const val RESIZE_MODE_KEY = "resize_mode" // Last used resize mode const val PLAYBACK_SPEED_KEY = "playback_speed" // Last used playback speed const val PREFERRED_SUBS_KEY = "preferred_subtitles" // Last used resize mode -const val PLAYBACK_FASTFORWARD = "playback_fastforward" // Last used resize mode +//const val PLAYBACK_FASTFORWARD = "playback_fastforward" // Last used resize mode /** Abstract Exoplayer logic, can be expanded to other players */ interface IPlayer { diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt index b929046e..bd2baf7c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/result/ResultFragment.kt @@ -1729,13 +1729,11 @@ class ResultFragment : Fragment(), PanelsChildGestureRegionObserver.GestureRegio setRecommendations(d.recommendations, null) setActors(d.actors) - if (SettingsFragment.accountEnabled) { - if (syncModel.addSyncs(d.syncData)) { - syncModel.updateMetaAndUser() - syncModel.updateSynced() - } else { - syncModel.addFromUrl(d.url) - } + if (syncModel.addSyncs(d.syncData)) { + syncModel.updateMetaAndUser() + syncModel.updateSynced() + } else { + syncModel.addFromUrl(d.url) } result_meta_site?.text = d.apiName diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt new file mode 100644 index 00000000..f3ea8478 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt @@ -0,0 +1,149 @@ +package com.lagradost.cloudstream3.ui.settings + +import android.content.Context +import android.os.Bundle +import android.widget.ImageView +import android.widget.TextView +import androidx.appcompat.app.AlertDialog +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.PreferenceManager +import androidx.recyclerview.widget.RecyclerView +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.syncproviders.AccountManager +import com.lagradost.cloudstream3.syncproviders.OAuth2API +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.beneneCount +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref +import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe +import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard +import com.lagradost.cloudstream3.utils.UIHelper.setImage + +class SettingsAccount : PreferenceFragmentCompat() { + private fun showLoginInfo(api: AccountManager, info: OAuth2API.LoginInfo) { + val builder = + AlertDialog.Builder(context ?: return, R.style.AlertDialogCustom) + .setView(R.layout.account_managment) + val dialog = builder.show() + + dialog.findViewById(R.id.account_profile_picture)?.setImage(info.profilePicture) + dialog.findViewById(R.id.account_logout)?.setOnClickListener { + api.logOut() + dialog.dismissSafe(activity) + } + + (info.name ?: context?.getString(R.string.no_data))?.let { + dialog.findViewById(R.id.account_name)?.text = it + } + dialog.findViewById(R.id.account_site)?.text = api.name + dialog.findViewById(R.id.account_switch_account)?.setOnClickListener { + dialog.dismissSafe(activity) + showAccountSwitch(it.context, api) + } + } + + private fun showAccountSwitch(context: Context, api: AccountManager) { + val accounts = api.getAccounts() ?: return + + val builder = + AlertDialog.Builder(context, R.style.AlertDialogCustom).setView(R.layout.account_switch) + val dialog = builder.show() + + dialog.findViewById(R.id.account_add)?.setOnClickListener { + try { + api.authenticate() + } catch (e: Exception) { + logError(e) + } + } + + val ogIndex = api.accountIndex + + val items = ArrayList() + + for (index in accounts) { + api.accountIndex = index + val accountInfo = api.loginInfo() + if (accountInfo != null) { + items.add(accountInfo) + } + } + api.accountIndex = ogIndex + val adapter = AccountAdapter(items, R.layout.account_single) { + dialog?.dismissSafe(activity) + api.changeAccount(it.card.accountIndex) + } + val list = dialog.findViewById(R.id.account_list) + list?.adapter = adapter + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + hideKeyboard() + setPreferencesFromResource(R.xml.settings_credits_account, rootKey) + val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()) + + getPref(R.string.legal_notice_key)?.setOnPreferenceClickListener { + val builder: AlertDialog.Builder = AlertDialog.Builder(it.context) + builder.setTitle(R.string.legal_notice) + builder.setMessage(R.string.legal_notice_text) + builder.show() + return@setOnPreferenceClickListener true + } + + val syncApis = + listOf( + Pair(R.string.mal_key, OAuth2API.malApi), Pair( + R.string.anilist_key, + OAuth2API.aniListApi + ) + ) + for ((key, api) in syncApis) { + getPref(key)?.apply { + title = + getString(R.string.login_format).format(api.name, getString(R.string.account)) + setOnPreferenceClickListener { _ -> + val info = api.loginInfo() + if (info != null) { + showLoginInfo(api, info) + } else { + try { + api.authenticate() + } catch (e: Exception) { + logError(e) + } + } + return@setOnPreferenceClickListener true + } + } + } + + + + try { + beneneCount = settingsManager.getInt(getString(R.string.benene_count), 0) + getPref(R.string.benene_count)?.let { pref -> + pref.summary = + if (beneneCount <= 0) getString(R.string.benene_count_text_none) else getString( + R.string.benene_count_text + ).format( + beneneCount + ) + + pref.setOnPreferenceClickListener { + try { + beneneCount++ + settingsManager.edit().putInt(getString(R.string.benene_count), beneneCount) + .apply() + it.summary = getString(R.string.benene_count_text).format(beneneCount) + } catch (e: Exception) { + logError(e) + } + + return@setOnPreferenceClickListener true + } + } + } catch (e: Exception) { + e.printStackTrace() + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt index c47a328d..6a8f8f2a 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsFragment.kt @@ -1,75 +1,52 @@ package com.lagradost.cloudstream3.ui.settings - import android.app.UiModeManager -import android.content.ClipData -import android.content.ClipboardManager import android.content.Context -import android.content.Intent import android.content.res.Configuration -import android.net.Uri import android.os.Build import android.os.Bundle -import android.os.Environment -import android.widget.ImageView -import android.widget.TextView -import android.widget.Toast -import androidx.activity.result.contract.ActivityResultContracts -import androidx.appcompat.app.AlertDialog +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceManager -import androidx.recyclerview.widget.RecyclerView -import com.hippo.unifile.UniFile -import com.lagradost.cloudstream3.APIHolder.apis -import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings -import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings -import com.lagradost.cloudstream3.AcraApplication -import com.lagradost.cloudstream3.AcraApplication.Companion.removeKey -import com.lagradost.cloudstream3.CommonActivity.setLocale -import com.lagradost.cloudstream3.CommonActivity.showToast -import com.lagradost.cloudstream3.DubStatus import com.lagradost.cloudstream3.R -import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.logError -import com.lagradost.cloudstream3.mvvm.normalSafeApiCall -import com.lagradost.cloudstream3.network.initClient -import com.lagradost.cloudstream3.syncproviders.AccountManager -import com.lagradost.cloudstream3.syncproviders.OAuth2API -import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.aniListApi -import com.lagradost.cloudstream3.syncproviders.OAuth2API.Companion.malApi -import com.lagradost.cloudstream3.ui.APIRepository -import com.lagradost.cloudstream3.ui.search.SearchResultBuilder -import com.lagradost.cloudstream3.ui.subtitles.ChromecastSubtitlesFragment -import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment -import com.lagradost.cloudstream3.utils.BackupUtils.backup -import com.lagradost.cloudstream3.utils.BackupUtils.restorePrompt -import com.lagradost.cloudstream3.utils.HOMEPAGE_API -import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate -import com.lagradost.cloudstream3.utils.Qualities -import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog -import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog -import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog -import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showNginxTextInputDialog -import com.lagradost.cloudstream3.utils.SubtitleHelper -import com.lagradost.cloudstream3.utils.SubtitleHelper.getFlagFromIso -import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe -import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard -import com.lagradost.cloudstream3.utils.UIHelper.setImage -import com.lagradost.cloudstream3.utils.VideoDownloadManager -import com.lagradost.cloudstream3.utils.VideoDownloadManager.getBasePath -import com.lagradost.cloudstream3.utils.VideoDownloadManager.getDownloadDir -import kotlinx.android.synthetic.main.logcat.* -import okhttp3.internal.closeQuietly -import java.io.BufferedReader +import com.lagradost.cloudstream3.utils.UIHelper.navigate +import kotlinx.android.synthetic.main.main_settings.* import java.io.File -import java.io.InputStreamReader -import java.io.OutputStream -import kotlin.concurrent.thread - -class SettingsFragment : PreferenceFragmentCompat() { +class SettingsFragment : Fragment() { companion object { + var beneneCount = 0 + + fun PreferenceFragmentCompat?.getPref(id: Int): Preference? { + if (this == null) return null + + return try { + findPreference(getString(id)) + } catch (e: Exception) { + logError(e) + null + } + } + + fun getFolderSize(dir: File): Long { + var size: Long = 0 + dir.listFiles()?.let { + for (file in it) { + size += if (file.isFile) { + // System.out.println(file.getName() + " " + file.length()); + file.length() + } else getFolderSize(file) + } + } + + return size + } + private fun Context.getLayoutInt(): Int { val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) return settingsManager.getInt(this.getString(R.string.app_layout_key), -1) @@ -98,762 +75,47 @@ class SettingsFragment : PreferenceFragmentCompat() { private fun Context.isAutoTv(): Boolean { val uiModeManager = getSystemService(Context.UI_MODE_SERVICE) as UiModeManager? // AFT = Fire TV - return uiModeManager?.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION || Build.MODEL.contains("AFT") - } - - const val accountEnabled = true - } - - private var beneneCount = 0 - - // Open file picker - private val pathPicker = - registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri -> - // It lies, it can be null if file manager quits. - if (uri == null) return@registerForActivityResult - val context = context ?: AcraApplication.context ?: return@registerForActivityResult - // RW perms for the path - val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or - Intent.FLAG_GRANT_WRITE_URI_PERMISSION - - context.contentResolver.takePersistableUriPermission(uri, flags) - - val file = UniFile.fromUri(context, uri) - println("Selected URI path: $uri - Full path: ${file.filePath}") - - // Stores the real URI using download_path_key - // Important that the URI is stored instead of filepath due to permissions. - PreferenceManager.getDefaultSharedPreferences(context) - .edit().putString(getString(R.string.download_path_key), uri.toString()).apply() - - // From URI -> File path - // File path here is purely for cosmetic purposes in settings - (file.filePath ?: uri.toString()).let { - PreferenceManager.getDefaultSharedPreferences(context) - .edit().putString(getString(R.string.download_path_pref), it).apply() - } - } - - // idk, if you find a way of automating this it would be great - // https://www.iemoji.com/view/emoji/1794/flags/antarctica - // Emoji Character Encoding Data --> C/C++/Java Src - // https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes leave blank for auto - private val languages = arrayListOf( - Triple("", "Spanish", "es"), - Triple("", "English", "en"), - Triple("", "Viet Nam", "vi"), - Triple("", "Dutch", "nl"), - Triple("", "French", "fr"), - Triple("", "Greek", "el"), - Triple("", "Swedish", "sv"), - Triple("", "Tagalog", "tl"), - Triple("", "Polish", "pl"), - Triple("", "Hindi", "hi"), - Triple("", "Malayalam", "ml"), - Triple("", "Norsk", "no"), - Triple("", "German", "de"), - Triple("", "Arabic", "ar"), - Triple("", "Turkish", "tr"), - Triple("", "Macedonian", "mk"), - Triple("", "Portuguese (Brazil)", "pt"), - Triple("", "Romanian", "ro"), - Triple("", "Italian", "it"), - Triple("", "Chinese", "zh"), - ).sortedBy { it.second } //ye, we go alphabetical, so ppl don't put their lang on top - - private fun showAccountSwitch(context: Context, api: AccountManager) { - val accounts = api.getAccounts() ?: return - - val builder = - AlertDialog.Builder(context, R.style.AlertDialogCustom).setView(R.layout.account_switch) - val dialog = builder.show() - - dialog.findViewById(R.id.account_add)?.setOnClickListener { - try { - api.authenticate() - } catch (e: Exception) { - logError(e) - } - } - - val ogIndex = api.accountIndex - - val items = ArrayList() - - for (index in accounts) { - api.accountIndex = index - val accountInfo = api.loginInfo() - if (accountInfo != null) { - items.add(accountInfo) - } - } - api.accountIndex = ogIndex - val adapter = AccountAdapter(items, R.layout.account_single) { - dialog?.dismissSafe(activity) - api.changeAccount(it.card.accountIndex) - } - val list = dialog.findViewById(R.id.account_list) - list?.adapter = adapter - } - - private fun showLoginInfo(api: AccountManager, info: OAuth2API.LoginInfo) { - val builder = - AlertDialog.Builder(context ?: return, R.style.AlertDialogCustom) - .setView(R.layout.account_managment) - val dialog = builder.show() - - dialog.findViewById(R.id.account_profile_picture)?.setImage(info.profilePicture) - dialog.findViewById(R.id.account_logout)?.setOnClickListener { - api.logOut() - dialog.dismissSafe(activity) - } - - (info.name ?: context?.getString(R.string.no_data))?.let { - dialog.findViewById(R.id.account_name)?.text = it - } - dialog.findViewById(R.id.account_site)?.text = api.name - dialog.findViewById(R.id.account_switch_account)?.setOnClickListener { - dialog.dismissSafe(activity) - showAccountSwitch(it.context, api) + return uiModeManager?.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION || Build.MODEL.contains( + "AFT" + ) } } - private fun getPref(id: Int): Preference? { - return try { - findPreference(getString(id)) - } catch (e: Exception) { - logError(e) - null - } + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle?, + ): View? { + return inflater.inflate(R.layout.main_settings, container, false) } - private fun getFolderSize(dir: File): Long { - var size: Long = 0 - dir.listFiles()?.let { - for (file in it) { - size += if (file.isFile) { - // System.out.println(file.getName() + " " + file.length()); - file.length() - } else getFolderSize(file) - } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + fun navigate(id: Int) { + activity?.navigate(id, Bundle()) } - return size - } - - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - hideKeyboard() - setPreferencesFromResource(R.xml.settings, rootKey) - val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()) - - getPref(R.string.video_buffer_length_key)?.setOnPreferenceClickListener { - val prefNames = resources.getStringArray(R.array.video_buffer_length_names) - val prefValues = resources.getIntArray(R.array.video_buffer_length_values) - - val currentPrefSize = - settingsManager.getInt(getString(R.string.video_buffer_length_key), 0) - - activity?.showDialog( - prefNames.toList(), - prefValues.indexOf(currentPrefSize), - getString(R.string.video_buffer_length_settings), - true, - {}) { - settingsManager.edit() - .putInt(getString(R.string.video_buffer_length_key), prefValues[it]) - .apply() - } - return@setOnPreferenceClickListener true + settings_player?.setOnClickListener { + navigate(R.id.action_navigation_settings_to_navigation_settings_player) } - getPref(R.string.video_buffer_size_key)?.setOnPreferenceClickListener { - val prefNames = resources.getStringArray(R.array.video_buffer_size_names) - val prefValues = resources.getIntArray(R.array.video_buffer_size_values) - - val currentPrefSize = - settingsManager.getInt(getString(R.string.video_buffer_size_key), 0) - - activity?.showDialog( - prefNames.toList(), - prefValues.indexOf(currentPrefSize), - getString(R.string.video_buffer_size_settings), - true, - {}) { - settingsManager.edit() - .putInt(getString(R.string.video_buffer_size_key), prefValues[it]) - .apply() - } - return@setOnPreferenceClickListener true + settings_credits?.setOnClickListener { + navigate(R.id.action_navigation_settings_to_navigation_settings_account) } - getPref(R.string.video_buffer_clear_key)?.let { pref -> - val cacheDir = context?.cacheDir ?: return@let - - fun updateSummery() { - try { - pref.summary = - getString(R.string.mb_format).format(getFolderSize(cacheDir) / (1024L * 1024L)) - } catch (e: Exception) { - logError(e) - } - } - - updateSummery() - - pref.setOnPreferenceClickListener { - try { - cacheDir.deleteRecursively() - updateSummery() - } catch (e: Exception) { - logError(e) - } - return@setOnPreferenceClickListener true - } + settings_ui?.setOnClickListener { + navigate(R.id.action_navigation_settings_to_navigation_settings_ui) } - getPref(R.string.video_buffer_disk_key)?.setOnPreferenceClickListener { - val prefNames = resources.getStringArray(R.array.video_buffer_size_names) - val prefValues = resources.getIntArray(R.array.video_buffer_size_values) - - val currentPrefSize = - settingsManager.getInt(getString(R.string.video_buffer_disk_key), 0) - - activity?.showDialog( - prefNames.toList(), - prefValues.indexOf(currentPrefSize), - getString(R.string.video_buffer_disk_settings), - true, - {}) { - settingsManager.edit() - .putInt(getString(R.string.video_buffer_disk_key), prefValues[it]) - .apply() - } - return@setOnPreferenceClickListener true + settings_lang?.setOnClickListener { + navigate(R.id.action_navigation_settings_to_navigation_settings_lang) } - getPref(R.string.subtitle_settings_key)?.setOnPreferenceClickListener { - SubtitlesFragment.push(activity, false) - return@setOnPreferenceClickListener true + settings_nginx?.setOnClickListener { + navigate(R.id.action_navigation_settings_to_navigation_settings_nginx) } - getPref(R.string.subtitle_settings_chromecast_key)?.setOnPreferenceClickListener { - ChromecastSubtitlesFragment.push(activity, false) - return@setOnPreferenceClickListener true - } - - getPref(R.string.poster_ui_key)?.setOnPreferenceClickListener { - val prefNames = resources.getStringArray(R.array.poster_ui_options) - val keys = resources.getStringArray(R.array.poster_ui_options_values) - val prefValues = keys.map { - settingsManager.getBoolean(it, true) - }.mapIndexedNotNull { index, b -> - if (b) { - index - } else null - } - - activity?.showMultiDialog( - prefNames.toList(), - prefValues, - getString(R.string.poster_ui_settings), - {}) { list -> - val edit = settingsManager.edit() - for ((i, key) in keys.withIndex()) { - edit.putBoolean(key, list.contains(i)) - } - edit.apply() - SearchResultBuilder.updateCache(it.context) - } - - return@setOnPreferenceClickListener true - } - - - val syncApis = - listOf(Pair(R.string.mal_key, malApi), Pair(R.string.anilist_key, aniListApi)) - for ((key, api) in syncApis) { - getPref(key)?.apply { - isVisible = accountEnabled - title = - getString(R.string.login_format).format(api.name, getString(R.string.account)) - setOnPreferenceClickListener { _ -> - val info = api.loginInfo() - if (info != null) { - showLoginInfo(api, info) - } else { - try { - api.authenticate() - } catch (e: Exception) { - logError(e) - } - } - return@setOnPreferenceClickListener true - } - } - } - - getPref(R.string.legal_notice_key)?.setOnPreferenceClickListener { - val builder: AlertDialog.Builder = AlertDialog.Builder(it.context) - builder.setTitle(R.string.legal_notice) - builder.setMessage(R.string.legal_notice_text) - builder.show() - return@setOnPreferenceClickListener true - } - - getPref(R.string.display_sub_key)?.setOnPreferenceClickListener { - activity?.getApiDubstatusSettings()?.let { current -> - val dublist = DubStatus.values() - val names = dublist.map { it.name } - - val currentList = ArrayList() - for (i in current) { - currentList.add(dublist.indexOf(i)) - } - - activity?.showMultiDialog( - names, - currentList, - getString(R.string.display_subbed_dubbed_settings), - {}) { selectedList -> - APIRepository.dubStatusActive = selectedList.map { dublist[it] }.toHashSet() - - settingsManager.edit().putStringSet( - this.getString(R.string.display_sub_key), - selectedList.map { names[it] }.toMutableSet() - ).apply() - } - } - - return@setOnPreferenceClickListener true - } - - getPref(R.string.provider_lang_key)?.setOnPreferenceClickListener { - activity?.getApiProviderLangSettings()?.let { current -> - val allLangs = HashSet() - for (api in apis) { - allLangs.add(api.lang) - } - - val currentList = ArrayList() - for (i in current) { - currentList.add(allLangs.indexOf(i)) - } - - val names = allLangs.map { - val emoji = getFlagFromIso(it) - val name = SubtitleHelper.fromTwoLettersToLanguage(it) - val fullName = "$emoji $name" - Pair(it, fullName) - } - - activity?.showMultiDialog( - names.map { it.second }, - currentList, - getString(R.string.provider_lang_settings), - {}) { selectedList -> - settingsManager.edit().putStringSet( - this.getString(R.string.provider_lang_key), - selectedList.map { names[it].first }.toMutableSet() - ).apply() - //APIRepository.providersActive = it.context.getApiSettings() - } - } - - return@setOnPreferenceClickListener true - } - - fun getDownloadDirs(): List { - return normalSafeApiCall { - val defaultDir = getDownloadDir()?.filePath - - // app_name_download_path = Cloudstream and does not change depending on release. - // DOES NOT WORK ON SCOPED STORAGE. - val secondaryDir = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) null else Environment.getExternalStorageDirectory().absolutePath + - File.separator + resources.getString(R.string.app_name_download_path) - val first = listOf(defaultDir, secondaryDir) - (try { - val currentDir = context?.getBasePath()?.let { it.first?.filePath ?: it.second } - - (first + - requireContext().getExternalFilesDirs("").mapNotNull { it.path } + - currentDir) - } catch (e: Exception) { - first - }).filterNotNull().distinct() - } ?: emptyList() - } - - getPref(R.string.download_path_key)?.setOnPreferenceClickListener { - val dirs = getDownloadDirs() - - val currentDir = - settingsManager.getString(getString(R.string.download_path_pref), null) - ?: getDownloadDir().toString() - - activity?.showBottomDialog( - dirs + listOf("Custom"), - dirs.indexOf(currentDir), - getString(R.string.download_path_pref), - true, - {}) { - // Last = custom - if (it == dirs.size) { - try { - pathPicker.launch(Uri.EMPTY) - } catch (e: Exception) { - logError(e) - } - } else { - // Sets both visual and actual paths. - // key = used path - // pref = visual path - settingsManager.edit() - .putString(getString(R.string.download_path_key), dirs[it]).apply() - settingsManager.edit() - .putString(getString(R.string.download_path_pref), dirs[it]).apply() - } - } - return@setOnPreferenceClickListener true - } - - getPref(R.string.nginx_url_key)?.setOnPreferenceClickListener { - activity?.showNginxTextInputDialog( - settingsManager.getString(getString(R.string.nginx_url_pref), "Nginx server url") - .toString(), - settingsManager.getString(getString(R.string.nginx_url_key), "") - .toString(), // key: the actual you use rn - android.text.InputType.TYPE_TEXT_VARIATION_URI, // uri - {}) { - settingsManager.edit() - .putString(getString(R.string.nginx_url_key), it) - .apply() // change the stored url in nginx_url_key to it - } - return@setOnPreferenceClickListener true - } - - getPref(R.string.nginx_credentials)?.setOnPreferenceClickListener { - activity?.showNginxTextInputDialog( - settingsManager.getString( - getString(R.string.nginx_credentials_title), - "Nginx Credentials" - ).toString(), - settingsManager.getString(getString(R.string.nginx_credentials), "") - .toString(), // key: the actual you use rn - android.text.InputType.TYPE_TEXT_VARIATION_URI, - {}) { - settingsManager.edit() - .putString(getString(R.string.nginx_credentials), it) - .apply() // change the stored url in nginx_url_key to it - } - return@setOnPreferenceClickListener true - } - - getPref(R.string.prefer_media_type_key)?.setOnPreferenceClickListener { - val prefNames = resources.getStringArray(R.array.media_type_pref) - val prefValues = resources.getIntArray(R.array.media_type_pref_values) - - val currentPrefMedia = - settingsManager.getInt(getString(R.string.prefer_media_type_key), 0) - - activity?.showBottomDialog( - prefNames.toList(), - prefValues.indexOf(currentPrefMedia), - getString(R.string.preferred_media_settings), - true, - {}) { - settingsManager.edit() - .putInt(getString(R.string.prefer_media_type_key), prefValues[it]) - .apply() - - removeKey(HOMEPAGE_API) - (context ?: AcraApplication.context)?.let { ctx -> app.initClient(ctx) } - } - return@setOnPreferenceClickListener true - } - - getPref(R.string.show_logcat_key)?.setOnPreferenceClickListener { pref -> - val builder = - AlertDialog.Builder(pref.context, R.style.AlertDialogCustom) - .setView(R.layout.logcat) - - val dialog = builder.create() - dialog.show() - val log = StringBuilder() - try { - //https://developer.android.com/studio/command-line/logcat - val process = Runtime.getRuntime().exec("logcat -d") - val bufferedReader = BufferedReader( - InputStreamReader(process.inputStream) - ) - - var line: String? - while (bufferedReader.readLine().also { line = it } != null) { - log.append(line) - } - } catch (e: Exception) { - logError(e) // kinda ironic - } - - val text = log.toString() - dialog.text1?.text = text - - dialog.copy_btt?.setOnClickListener { - val serviceClipboard = - (activity?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?) - ?: return@setOnClickListener - val clip = ClipData.newPlainText("logcat", text) - serviceClipboard.setPrimaryClip(clip) - dialog.dismissSafe(activity) - } - dialog.clear_btt?.setOnClickListener { - Runtime.getRuntime().exec("logcat -c") - dialog.dismissSafe(activity) - } - dialog.save_btt?.setOnClickListener { - var fileStream: OutputStream? = null - try { - fileStream = - VideoDownloadManager.setupStream( - it.context, - "logcat", - null, - "txt", - false - ).fileStream - fileStream?.writer()?.write(text) - } catch (e: Exception) { - logError(e) - } finally { - fileStream?.closeQuietly() - dialog.dismissSafe(activity) - } - } - dialog.close_btt?.setOnClickListener { - dialog.dismissSafe(activity) - } - return@setOnPreferenceClickListener true - } - - getPref(R.string.app_layout_key)?.setOnPreferenceClickListener { - val prefNames = resources.getStringArray(R.array.app_layout) - val prefValues = resources.getIntArray(R.array.app_layout_values) - - val currentLayout = - settingsManager.getInt(getString(R.string.app_layout_key), -1) - - activity?.showBottomDialog( - prefNames.toList(), - prefValues.indexOf(currentLayout), - getString(R.string.app_layout), - true, - {}) { - try { - settingsManager.edit() - .putInt(getString(R.string.app_layout_key), prefValues[it]) - .apply() - activity?.recreate() - } catch (e: Exception) { - logError(e) - } - } - return@setOnPreferenceClickListener true - } - - getPref(R.string.backup_key)?.setOnPreferenceClickListener { - activity?.backup() - return@setOnPreferenceClickListener true - } - - getPref(R.string.restore_key)?.setOnPreferenceClickListener { - activity?.restorePrompt() - return@setOnPreferenceClickListener true - } - - getPref(R.string.primary_color_key)?.setOnPreferenceClickListener { - val prefNames = resources.getStringArray(R.array.themes_overlay_names) - val prefValues = resources.getStringArray(R.array.themes_overlay_names_values) - - val currentLayout = - settingsManager.getString(getString(R.string.primary_color_key), prefValues.first()) - - activity?.showDialog( - prefNames.toList(), - prefValues.indexOf(currentLayout), - getString(R.string.primary_color_settings), - true, - {}) { - try { - settingsManager.edit() - .putString(getString(R.string.primary_color_key), prefValues[it]) - .apply() - activity?.recreate() - } catch (e: Exception) { - logError(e) - } - } - return@setOnPreferenceClickListener true - } - - getPref(R.string.app_theme_key)?.setOnPreferenceClickListener { - val prefNames = resources.getStringArray(R.array.themes_names) - val prefValues = resources.getStringArray(R.array.themes_names_values) - - val currentLayout = - settingsManager.getString(getString(R.string.app_theme_key), prefValues.first()) - - activity?.showBottomDialog( - prefNames.toList(), - prefValues.indexOf(currentLayout), - getString(R.string.app_theme_settings), - true, - {}) { - try { - settingsManager.edit() - .putString(getString(R.string.app_theme_key), prefValues[it]) - .apply() - activity?.recreate() - } catch (e: Exception) { - logError(e) - } - } - return@setOnPreferenceClickListener true - } - - getPref(R.string.quality_pref_key)?.setOnPreferenceClickListener { - val prefValues = Qualities.values().map { it.value }.reversed().toMutableList() - prefValues.remove(Qualities.Unknown.value) - - val prefNames = prefValues.map { Qualities.getStringByInt(it) } - - val currentQuality = - settingsManager.getInt( - getString(R.string.quality_pref_key), - Qualities.values().last().value - ) - - activity?.showBottomDialog( - prefNames.toList(), - prefValues.indexOf(currentQuality), - getString(R.string.watch_quality_pref), - true, - {}) { - settingsManager.edit().putInt(getString(R.string.quality_pref_key), prefValues[it]) - .apply() - } - return@setOnPreferenceClickListener true - } - - getPref(R.string.prefer_limit_title_key)?.setOnPreferenceClickListener { - val prefNames = resources.getStringArray(R.array.limit_title_pref_names) - val prefValues = resources.getIntArray(R.array.limit_title_pref_values) - val current = settingsManager.getInt(getString(R.string.prefer_limit_title_key), 0) - - activity?.showBottomDialog( - prefNames.toList(), - prefValues.indexOf(current), - getString(R.string.limit_title), - true, - {}) { - settingsManager.edit() - .putInt(getString(R.string.prefer_limit_title_key), prefValues[it]) - .apply() - } - return@setOnPreferenceClickListener true - } - - getPref(R.string.dns_key)?.setOnPreferenceClickListener { - val prefNames = resources.getStringArray(R.array.dns_pref) - val prefValues = resources.getIntArray(R.array.dns_pref_values) - - val currentDns = - settingsManager.getInt(getString(R.string.dns_pref), 0) - - activity?.showBottomDialog( - prefNames.toList(), - prefValues.indexOf(currentDns), - getString(R.string.dns_pref), - true, - {}) { - settingsManager.edit().putInt(getString(R.string.dns_pref), prefValues[it]).apply() - (context ?: AcraApplication.context)?.let { ctx -> app.initClient(ctx) } - } - return@setOnPreferenceClickListener true - } - - try { - beneneCount = settingsManager.getInt(getString(R.string.benene_count), 0) - getPref(R.string.benene_count)?.let { pref -> - pref.summary = - if (beneneCount <= 0) getString(R.string.benene_count_text_none) else getString( - R.string.benene_count_text - ).format( - beneneCount - ) - - pref.setOnPreferenceClickListener { - try { - beneneCount++ - settingsManager.edit().putInt(getString(R.string.benene_count), beneneCount) - .apply() - it.summary = getString(R.string.benene_count_text).format(beneneCount) - } catch (e: Exception) { - logError(e) - } - - return@setOnPreferenceClickListener true - } - } - } catch (e: Exception) { - e.printStackTrace() - } - - getPref(R.string.manual_check_update_key)?.setOnPreferenceClickListener { - thread { - if (!requireActivity().runAutoUpdate(false)) { - activity?.runOnUiThread { - showToast(activity, R.string.no_update_found, Toast.LENGTH_SHORT) - } - } - } - return@setOnPreferenceClickListener true - } - - getPref(R.string.locale_key)?.setOnPreferenceClickListener { pref -> - val tempLangs = languages.toMutableList() - if (beneneCount > 100) { - tempLangs.add(Triple("\uD83E\uDD8D", "mmmm... monke", "mo")) - } - val current = getCurrentLocale() - val languageCodes = tempLangs.map { it.third } - val languageNames = tempLangs.map { (emoji, name, iso) -> - val flag = emoji.ifBlank { getFlagFromIso(iso) ?: "ERROR" } - - "$flag $name" - } - val index = languageCodes.indexOf(current) - - activity?.showDialog( - languageNames, index, getString(R.string.app_language), true, { } - ) { languageIndex -> - try { - val code = languageCodes[languageIndex] - setLocale(activity, code) - settingsManager.edit().putString(getString(R.string.locale_key), code).apply() - activity?.recreate() - } catch (e: Exception) { - logError(e) - } - } - return@setOnPreferenceClickListener true + settings_updates?.setOnClickListener { + navigate(R.id.action_navigation_settings_to_navigation_settings_updates) } } - - private fun getCurrentLocale(): String { - val res = requireContext().resources -// Change locale settings in the app. - // val dm = res.displayMetrics - val conf = res.configuration - return conf?.locale?.language ?: "en" - } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsLang.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsLang.kt new file mode 100644 index 00000000..b2f8aa05 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsLang.kt @@ -0,0 +1,175 @@ +package com.lagradost.cloudstream3.ui.settings + +import android.os.Bundle +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.PreferenceManager +import com.lagradost.cloudstream3.* +import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings +import com.lagradost.cloudstream3.APIHolder.getApiProviderLangSettings +import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.network.initClient +import com.lagradost.cloudstream3.ui.APIRepository +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref +import com.lagradost.cloudstream3.utils.HOMEPAGE_API +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog +import com.lagradost.cloudstream3.utils.SubtitleHelper +import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard + +class SettingsLang : PreferenceFragmentCompat() { + // idk, if you find a way of automating this it would be great + // https://www.iemoji.com/view/emoji/1794/flags/antarctica + // Emoji Character Encoding Data --> C/C++/Java Src + // https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes leave blank for auto + private val languages = arrayListOf( + Triple("", "Spanish", "es"), + Triple("", "English", "en"), + Triple("", "Viet Nam", "vi"), + Triple("", "Dutch", "nl"), + Triple("", "French", "fr"), + Triple("", "Greek", "el"), + Triple("", "Swedish", "sv"), + Triple("", "Tagalog", "tl"), + Triple("", "Polish", "pl"), + Triple("", "Hindi", "hi"), + Triple("", "Malayalam", "ml"), + Triple("", "Norsk", "no"), + Triple("", "German", "de"), + Triple("", "Arabic", "ar"), + Triple("", "Turkish", "tr"), + Triple("", "Macedonian", "mk"), + Triple("\uD83C\uDDE7\uD83C\uDDF7", "Portuguese (Brazil)", "pt"), + Triple("", "Romanian", "ro"), + Triple("", "Italian", "it"), + Triple("", "Chinese", "zh"), + ).sortedBy { it.second } //ye, we go alphabetical, so ppl don't put their lang on top + + private fun getCurrentLocale(): String { + val res = requireContext().resources +// Change locale settings in the app. + // val dm = res.displayMetrics + val conf = res.configuration + return conf?.locale?.language ?: "en" + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + hideKeyboard() + setPreferencesFromResource(R.xml.settings_media_lang, rootKey) + val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()) + + getPref(R.string.display_sub_key)?.setOnPreferenceClickListener { + activity?.getApiDubstatusSettings()?.let { current -> + val dublist = DubStatus.values() + val names = dublist.map { it.name } + + val currentList = ArrayList() + for (i in current) { + currentList.add(dublist.indexOf(i)) + } + + activity?.showMultiDialog( + names, + currentList, + getString(R.string.display_subbed_dubbed_settings), + {}) { selectedList -> + APIRepository.dubStatusActive = selectedList.map { dublist[it] }.toHashSet() + + settingsManager.edit().putStringSet( + this.getString(R.string.display_sub_key), + selectedList.map { names[it] }.toMutableSet() + ).apply() + } + } + + return@setOnPreferenceClickListener true + } + + getPref(R.string.prefer_media_type_key)?.setOnPreferenceClickListener { + val prefNames = resources.getStringArray(R.array.media_type_pref) + val prefValues = resources.getIntArray(R.array.media_type_pref_values) + + val currentPrefMedia = + settingsManager.getInt(getString(R.string.prefer_media_type_key), 0) + + activity?.showBottomDialog( + prefNames.toList(), + prefValues.indexOf(currentPrefMedia), + getString(R.string.preferred_media_settings), + true, + {}) { + settingsManager.edit() + .putInt(getString(R.string.prefer_media_type_key), prefValues[it]) + .apply() + + AcraApplication.removeKey(HOMEPAGE_API) + (context ?: AcraApplication.context)?.let { ctx -> app.initClient(ctx) } + } + return@setOnPreferenceClickListener true + } + + getPref(R.string.locale_key)?.setOnPreferenceClickListener { pref -> + val tempLangs = languages.toMutableList() + //if (beneneCount > 100) { + // tempLangs.add(Triple("\uD83E\uDD8D", "mmmm... monke", "mo")) + //} + val current = getCurrentLocale() + val languageCodes = tempLangs.map { it.third } + val languageNames = tempLangs.map { (emoji, name, iso) -> + val flag = emoji.ifBlank { SubtitleHelper.getFlagFromIso(iso) ?: "ERROR" } + + "$flag $name" + } + val index = languageCodes.indexOf(current) + + activity?.showDialog( + languageNames, index, getString(R.string.app_language), true, { } + ) { languageIndex -> + try { + val code = languageCodes[languageIndex] + CommonActivity.setLocale(activity, code) + settingsManager.edit().putString(getString(R.string.locale_key), code).apply() + activity?.recreate() + } catch (e: Exception) { + logError(e) + } + } + return@setOnPreferenceClickListener true + } + + getPref(R.string.provider_lang_key)?.setOnPreferenceClickListener { + activity?.getApiProviderLangSettings()?.let { current -> + val allLangs = HashSet() + for (api in APIHolder.apis) { + allLangs.add(api.lang) + } + + val currentList = ArrayList() + for (i in current) { + currentList.add(allLangs.indexOf(i)) + } + + val names = allLangs.map { + val emoji = SubtitleHelper.getFlagFromIso(it) + val name = SubtitleHelper.fromTwoLettersToLanguage(it) + val fullName = "$emoji $name" + Pair(it, fullName) + } + + activity?.showMultiDialog( + names.map { it.second }, + currentList, + getString(R.string.provider_lang_settings), + {}) { selectedList -> + settingsManager.edit().putStringSet( + this.getString(R.string.provider_lang_key), + selectedList.map { names[it].first }.toMutableSet() + ).apply() + //APIRepository.providersActive = it.context.getApiSettings() + } + } + + return@setOnPreferenceClickListener true + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsNginx.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsNginx.kt new file mode 100644 index 00000000..1ab95735 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsNginx.kt @@ -0,0 +1,54 @@ +package com.lagradost.cloudstream3.ui.settings + +import android.os.Bundle +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.PreferenceManager +import com.lagradost.cloudstream3.AcraApplication +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.network.initClient +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref +import com.lagradost.cloudstream3.utils.HOMEPAGE_API +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showNginxTextInputDialog +import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard + +class SettingsNginx : PreferenceFragmentCompat() { + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + hideKeyboard() + setPreferencesFromResource(R.xml.settings_nginx, rootKey) + val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()) + + getPref(R.string.nginx_credentials)?.setOnPreferenceClickListener { + activity?.showNginxTextInputDialog( + settingsManager.getString( + getString(R.string.nginx_credentials_title), + "Nginx Credentials" + ).toString(), + settingsManager.getString(getString(R.string.nginx_credentials), "") + .toString(), // key: the actual you use rn + android.text.InputType.TYPE_TEXT_VARIATION_URI, + {}) { + settingsManager.edit() + .putString(getString(R.string.nginx_credentials), it) + .apply() // change the stored url in nginx_url_key to it + } + return@setOnPreferenceClickListener true + } + + getPref(R.string.nginx_url_key)?.setOnPreferenceClickListener { + activity?.showNginxTextInputDialog( + settingsManager.getString(getString(R.string.nginx_url_pref), "Nginx server url") + .toString(), + settingsManager.getString(getString(R.string.nginx_url_key), "") + .toString(), // key: the actual you use rn + android.text.InputType.TYPE_TEXT_VARIATION_URI, // uri + {}) { + settingsManager.edit() + .putString(getString(R.string.nginx_url_key), it) + .apply() // change the stored url in nginx_url_key to it + } + return@setOnPreferenceClickListener true + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt new file mode 100644 index 00000000..0baba5ed --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsPlayer.kt @@ -0,0 +1,295 @@ +package com.lagradost.cloudstream3.ui.settings + +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.os.Environment +import androidx.activity.result.contract.ActivityResultContracts +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.PreferenceManager +import com.hippo.unifile.UniFile +import com.lagradost.cloudstream3.AcraApplication +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.app +import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.mvvm.normalSafeApiCall +import com.lagradost.cloudstream3.network.initClient +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getFolderSize +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref +import com.lagradost.cloudstream3.ui.subtitles.ChromecastSubtitlesFragment +import com.lagradost.cloudstream3.ui.subtitles.SubtitlesFragment +import com.lagradost.cloudstream3.utils.Qualities +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog +import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard +import com.lagradost.cloudstream3.utils.VideoDownloadManager +import com.lagradost.cloudstream3.utils.VideoDownloadManager.getBasePath +import java.io.File + +class SettingsPlayer : PreferenceFragmentCompat() { + // Open file picker + private val pathPicker = + registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri -> + // It lies, it can be null if file manager quits. + if (uri == null) return@registerForActivityResult + val context = context ?: AcraApplication.context ?: return@registerForActivityResult + // RW perms for the path + val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or + Intent.FLAG_GRANT_WRITE_URI_PERMISSION + + context.contentResolver.takePersistableUriPermission(uri, flags) + + val file = UniFile.fromUri(context, uri) + println("Selected URI path: $uri - Full path: ${file.filePath}") + + // Stores the real URI using download_path_key + // Important that the URI is stored instead of filepath due to permissions. + PreferenceManager.getDefaultSharedPreferences(context) + .edit().putString(getString(R.string.download_path_key), uri.toString()).apply() + + // From URI -> File path + // File path here is purely for cosmetic purposes in settings + (file.filePath ?: uri.toString()).let { + PreferenceManager.getDefaultSharedPreferences(context) + .edit().putString(getString(R.string.download_path_pref), it).apply() + } + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + hideKeyboard() + setPreferencesFromResource(R.xml.settings_player, rootKey) + val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()) + + getPref(R.string.video_buffer_length_key)?.setOnPreferenceClickListener { + val prefNames = resources.getStringArray(R.array.video_buffer_length_names) + val prefValues = resources.getIntArray(R.array.video_buffer_length_values) + + val currentPrefSize = + settingsManager.getInt(getString(R.string.video_buffer_length_key), 0) + + activity?.showDialog( + prefNames.toList(), + prefValues.indexOf(currentPrefSize), + getString(R.string.video_buffer_length_settings), + true, + {}) { + settingsManager.edit() + .putInt(getString(R.string.video_buffer_length_key), prefValues[it]) + .apply() + } + return@setOnPreferenceClickListener true + } + getPref(R.string.dns_key)?.setOnPreferenceClickListener { + val prefNames = resources.getStringArray(R.array.dns_pref) + val prefValues = resources.getIntArray(R.array.dns_pref_values) + + val currentDns = + settingsManager.getInt(getString(R.string.dns_pref), 0) + + activity?.showBottomDialog( + prefNames.toList(), + prefValues.indexOf(currentDns), + getString(R.string.dns_pref), + true, + {}) { + settingsManager.edit().putInt(getString(R.string.dns_pref), prefValues[it]).apply() + (context ?: AcraApplication.context)?.let { ctx -> app.initClient(ctx) } + } + return@setOnPreferenceClickListener true + } + + getPref(R.string.prefer_limit_title_key)?.setOnPreferenceClickListener { + val prefNames = resources.getStringArray(R.array.limit_title_pref_names) + val prefValues = resources.getIntArray(R.array.limit_title_pref_values) + val current = settingsManager.getInt(getString(R.string.prefer_limit_title_key), 0) + + activity?.showBottomDialog( + prefNames.toList(), + prefValues.indexOf(current), + getString(R.string.limit_title), + true, + {}) { + settingsManager.edit() + .putInt(getString(R.string.prefer_limit_title_key), prefValues[it]) + .apply() + } + return@setOnPreferenceClickListener true + } + + /*(getPref(R.string.double_tap_seek_time_key) as? SeekBarPreference?)?.let { + + }*/ + + getPref(R.string.prefer_limit_title_rez_key)?.setOnPreferenceClickListener { + val prefNames = resources.getStringArray(R.array.limit_title_rez_pref_names) + val prefValues = resources.getIntArray(R.array.limit_title_rez_pref_values) + val current = settingsManager.getInt(getString(R.string.prefer_limit_title_rez_key), 3) + + activity?.showBottomDialog( + prefNames.toList(), + prefValues.indexOf(current), + getString(R.string.limit_title_rez), + true, + {}) { + settingsManager.edit() + .putInt(getString(R.string.prefer_limit_title_rez_key), prefValues[it]) + .apply() + } + return@setOnPreferenceClickListener true + } + + getPref(R.string.quality_pref_key)?.setOnPreferenceClickListener { + val prefValues = Qualities.values().map { it.value }.reversed().toMutableList() + prefValues.remove(Qualities.Unknown.value) + + val prefNames = prefValues.map { Qualities.getStringByInt(it) } + + val currentQuality = + settingsManager.getInt( + getString(R.string.quality_pref_key), + Qualities.values().last().value + ) + + activity?.showBottomDialog( + prefNames.toList(), + prefValues.indexOf(currentQuality), + getString(R.string.watch_quality_pref), + true, + {}) { + settingsManager.edit().putInt(getString(R.string.quality_pref_key), prefValues[it]) + .apply() + } + return@setOnPreferenceClickListener true + } + + getPref(R.string.subtitle_settings_key)?.setOnPreferenceClickListener { + SubtitlesFragment.push(activity, false) + return@setOnPreferenceClickListener true + } + + getPref(R.string.subtitle_settings_chromecast_key)?.setOnPreferenceClickListener { + ChromecastSubtitlesFragment.push(activity, false) + return@setOnPreferenceClickListener true + } + + getPref(R.string.video_buffer_disk_key)?.setOnPreferenceClickListener { + val prefNames = resources.getStringArray(R.array.video_buffer_size_names) + val prefValues = resources.getIntArray(R.array.video_buffer_size_values) + + val currentPrefSize = + settingsManager.getInt(getString(R.string.video_buffer_disk_key), 0) + + activity?.showDialog( + prefNames.toList(), + prefValues.indexOf(currentPrefSize), + getString(R.string.video_buffer_disk_settings), + true, + {}) { + settingsManager.edit() + .putInt(getString(R.string.video_buffer_disk_key), prefValues[it]) + .apply() + } + return@setOnPreferenceClickListener true + } + getPref(R.string.video_buffer_size_key)?.setOnPreferenceClickListener { + val prefNames = resources.getStringArray(R.array.video_buffer_size_names) + val prefValues = resources.getIntArray(R.array.video_buffer_size_values) + + val currentPrefSize = + settingsManager.getInt(getString(R.string.video_buffer_size_key), 0) + + activity?.showDialog( + prefNames.toList(), + prefValues.indexOf(currentPrefSize), + getString(R.string.video_buffer_size_settings), + true, + {}) { + settingsManager.edit() + .putInt(getString(R.string.video_buffer_size_key), prefValues[it]) + .apply() + } + return@setOnPreferenceClickListener true + } + + getPref(R.string.video_buffer_clear_key)?.let { pref -> + val cacheDir = context?.cacheDir ?: return@let + + fun updateSummery() { + try { + pref.summary = + getString(R.string.mb_format).format(getFolderSize(cacheDir) / (1024L * 1024L)) + } catch (e: Exception) { + logError(e) + } + } + + updateSummery() + + pref.setOnPreferenceClickListener { + try { + cacheDir.deleteRecursively() + updateSummery() + } catch (e: Exception) { + logError(e) + } + return@setOnPreferenceClickListener true + } + } + fun getDownloadDirs(): List { + return normalSafeApiCall { + val defaultDir = VideoDownloadManager.getDownloadDir()?.filePath + + // app_name_download_path = Cloudstream and does not change depending on release. + // DOES NOT WORK ON SCOPED STORAGE. + val secondaryDir = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) null else Environment.getExternalStorageDirectory().absolutePath + + File.separator + resources.getString(R.string.app_name_download_path) + val first = listOf(defaultDir, secondaryDir) + (try { + val currentDir = context?.getBasePath()?.let { it.first?.filePath ?: it.second } + + (first + + requireContext().getExternalFilesDirs("").mapNotNull { it.path } + + currentDir) + } catch (e: Exception) { + first + }).filterNotNull().distinct() + } ?: emptyList() + } + + getPref(R.string.download_path_key)?.setOnPreferenceClickListener { + val dirs = getDownloadDirs() + + val currentDir = + settingsManager.getString(getString(R.string.download_path_pref), null) + ?: VideoDownloadManager.getDownloadDir().toString() + + activity?.showBottomDialog( + dirs + listOf("Custom"), + dirs.indexOf(currentDir), + getString(R.string.download_path_pref), + true, + {}) { + // Last = custom + if (it == dirs.size) { + try { + pathPicker.launch(Uri.EMPTY) + } catch (e: Exception) { + logError(e) + } + } else { + // Sets both visual and actual paths. + // key = used path + // pref = visual path + settingsManager.edit() + .putString(getString(R.string.download_path_key), dirs[it]).apply() + settingsManager.edit() + .putString(getString(R.string.download_path_pref), dirs[it]).apply() + } + } + return@setOnPreferenceClickListener true + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt new file mode 100644 index 00000000..d688ab21 --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUI.kt @@ -0,0 +1,123 @@ +package com.lagradost.cloudstream3.ui.settings + +import android.os.Bundle +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.PreferenceManager +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.ui.search.SearchResultBuilder +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog +import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog +import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard + +class SettingsUI : PreferenceFragmentCompat() { + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + hideKeyboard() + setPreferencesFromResource(R.xml.settins_ui, rootKey) + val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()) + + getPref(R.string.poster_ui_key)?.setOnPreferenceClickListener { + val prefNames = resources.getStringArray(R.array.poster_ui_options) + val keys = resources.getStringArray(R.array.poster_ui_options_values) + val prefValues = keys.map { + settingsManager.getBoolean(it, true) + }.mapIndexedNotNull { index, b -> + if (b) { + index + } else null + } + + activity?.showMultiDialog( + prefNames.toList(), + prefValues, + getString(R.string.poster_ui_settings), + {}) { list -> + val edit = settingsManager.edit() + for ((i, key) in keys.withIndex()) { + edit.putBoolean(key, list.contains(i)) + } + edit.apply() + SearchResultBuilder.updateCache(it.context) + } + + return@setOnPreferenceClickListener true + } + + getPref(R.string.app_layout_key)?.setOnPreferenceClickListener { + val prefNames = resources.getStringArray(R.array.app_layout) + val prefValues = resources.getIntArray(R.array.app_layout_values) + + val currentLayout = + settingsManager.getInt(getString(R.string.app_layout_key), -1) + + activity?.showBottomDialog( + prefNames.toList(), + prefValues.indexOf(currentLayout), + getString(R.string.app_layout), + true, + {}) { + try { + settingsManager.edit() + .putInt(getString(R.string.app_layout_key), prefValues[it]) + .apply() + activity?.recreate() + } catch (e: Exception) { + logError(e) + } + } + return@setOnPreferenceClickListener true + } + + getPref(R.string.app_theme_key)?.setOnPreferenceClickListener { + val prefNames = resources.getStringArray(R.array.themes_names) + val prefValues = resources.getStringArray(R.array.themes_names_values) + + val currentLayout = + settingsManager.getString(getString(R.string.app_theme_key), prefValues.first()) + + activity?.showBottomDialog( + prefNames.toList(), + prefValues.indexOf(currentLayout), + getString(R.string.app_theme_settings), + true, + {}) { + try { + settingsManager.edit() + .putString(getString(R.string.app_theme_key), prefValues[it]) + .apply() + activity?.recreate() + } catch (e: Exception) { + logError(e) + } + } + return@setOnPreferenceClickListener true + } + getPref(R.string.primary_color_key)?.setOnPreferenceClickListener { + val prefNames = resources.getStringArray(R.array.themes_overlay_names) + val prefValues = resources.getStringArray(R.array.themes_overlay_names_values) + + val currentLayout = + settingsManager.getString(getString(R.string.primary_color_key), prefValues.first()) + + activity?.showDialog( + prefNames.toList(), + prefValues.indexOf(currentLayout), + getString(R.string.primary_color_settings), + true, + {}) { + try { + settingsManager.edit() + .putString(getString(R.string.primary_color_key), prefValues[it]) + .apply() + activity?.recreate() + } catch (e: Exception) { + logError(e) + } + } + return@setOnPreferenceClickListener true + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt new file mode 100644 index 00000000..d3d3182e --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsUpdates.kt @@ -0,0 +1,118 @@ +package com.lagradost.cloudstream3.ui.settings + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.os.Bundle +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import androidx.preference.PreferenceFragmentCompat +import androidx.preference.PreferenceManager +import com.lagradost.cloudstream3.CommonActivity +import com.lagradost.cloudstream3.R +import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref +import com.lagradost.cloudstream3.utils.BackupUtils.backup +import com.lagradost.cloudstream3.utils.BackupUtils.restorePrompt +import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate +import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe +import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard +import com.lagradost.cloudstream3.utils.VideoDownloadManager +import kotlinx.android.synthetic.main.logcat.* +import okhttp3.internal.closeQuietly +import java.io.BufferedReader +import java.io.InputStreamReader +import java.io.OutputStream +import kotlin.concurrent.thread + +class SettingsUpdates : PreferenceFragmentCompat() { + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + hideKeyboard() + setPreferencesFromResource(R.xml.settings_updates, rootKey) + val settingsManager = PreferenceManager.getDefaultSharedPreferences(requireContext()) + + + getPref(R.string.backup_key)?.setOnPreferenceClickListener { + activity?.backup() + return@setOnPreferenceClickListener true + } + + getPref(R.string.restore_key)?.setOnPreferenceClickListener { + activity?.restorePrompt() + return@setOnPreferenceClickListener true + } + getPref(R.string.show_logcat_key)?.setOnPreferenceClickListener { pref -> + val builder = + AlertDialog.Builder(pref.context, R.style.AlertDialogCustom) + .setView(R.layout.logcat) + + val dialog = builder.create() + dialog.show() + val log = StringBuilder() + try { + //https://developer.android.com/studio/command-line/logcat + val process = Runtime.getRuntime().exec("logcat -d") + val bufferedReader = BufferedReader( + InputStreamReader(process.inputStream) + ) + + var line: String? + while (bufferedReader.readLine().also { line = it } != null) { + log.append(line) + } + } catch (e: Exception) { + logError(e) // kinda ironic + } + + val text = log.toString() + dialog.text1?.text = text + + dialog.copy_btt?.setOnClickListener { + val serviceClipboard = + (activity?.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager?) + ?: return@setOnClickListener + val clip = ClipData.newPlainText("logcat", text) + serviceClipboard.setPrimaryClip(clip) + dialog.dismissSafe(activity) + } + dialog.clear_btt?.setOnClickListener { + Runtime.getRuntime().exec("logcat -c") + dialog.dismissSafe(activity) + } + dialog.save_btt?.setOnClickListener { + var fileStream: OutputStream? = null + try { + fileStream = + VideoDownloadManager.setupStream( + it.context, + "logcat", + null, + "txt", + false + ).fileStream + fileStream?.writer()?.write(text) + } catch (e: Exception) { + logError(e) + } finally { + fileStream?.closeQuietly() + dialog.dismissSafe(activity) + } + } + dialog.close_btt?.setOnClickListener { + dialog.dismissSafe(activity) + } + return@setOnPreferenceClickListener true + } + + getPref(R.string.manual_check_update_key)?.setOnPreferenceClickListener { + thread { + if (!requireActivity().runAutoUpdate(false)) { + activity?.runOnUiThread { + CommonActivity.showToast(activity, R.string.no_update_found, Toast.LENGTH_SHORT) + } + } + } + return@setOnPreferenceClickListener true + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt index 42582992..9ba43c22 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt @@ -302,7 +302,8 @@ object UIHelper { return result } - fun Context.fixPaddingStatusbar(v: View) { + fun Context?.fixPaddingStatusbar(v: View?) { + if (v == null || this == null) return v.setPadding( v.paddingLeft, v.paddingTop + getStatusBarHeight(), @@ -385,7 +386,7 @@ object UIHelper { } fun hideKeyboard(view: View?) { - if(view == null) return + if (view == null) return val inputMethodManager = view.context.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager? diff --git a/app/src/main/res/layout/main_settings.xml b/app/src/main/res/layout/main_settings.xml new file mode 100644 index 00000000..294f9d7e --- /dev/null +++ b/app/src/main/res/layout/main_settings.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 7ea867f8..d6505f5f 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -86,6 +86,69 @@ android:defaultValue="true" /> + + + + + + + + + + + + + + + - - - - - - + app:popExitAnim="@anim/exit_anim"> + + + + app:popExitAnim="@anim/exit_anim"> + + + app:destination="@id/navigation_download_child" + app:enterAnim="@anim/enter_anim" + app:exitAnim="@anim/exit_anim" + app:popEnterAnim="@anim/enter_anim" + app:popExitAnim="@anim/exit_anim"> @@ -167,6 +233,20 @@ android:name="folder" app:argType="string" /> + + + app:popExitAnim="@anim/exit_anim"> + + + + + + + + app:popExitAnim="@anim/exit_anim"> + + + app:popExitAnim="@anim/exit_anim"> + + + Yedekleme hatası %s Ara - Nginx Ayarları + Nginx Ayarları Nginx Kimlik Bilgileri Örnekteki formatta yazmalısın havalıkullanıcıadım:güvenlişifrem123 Nginx nedir? diff --git a/app/src/main/res/values/array.xml b/app/src/main/res/values/array.xml index b1c9ba7f..2571f779 100644 --- a/app/src/main/res/values/array.xml +++ b/app/src/main/res/values/array.xml @@ -42,6 +42,20 @@ 3 + + @string/resolution_and_title + @string/title + @string/resolution + @string/none + + + + 3 + 2 + 1 + 0 + + @string/none 16 characters @@ -59,6 +73,15 @@ -1 + + 5 + 10 + 15 + 20 + 25 + 30 + + @string/automatic 1min diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4131d31a..2ca74e99 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -14,6 +14,7 @@ subtitle_settings_chromecast_key quality_pref_key prefer_limit_title_key + prefer_limit_title_rez_key video_buffer_size_key video_buffer_length_key video_buffer_clear_key @@ -26,6 +27,7 @@ pip_enabled_key double_tap_enabled_key double_tap_pause_enabled_key + double_tap_seek_time_key swipe_vertical_enabled_key display_sub_key show_fillers_key @@ -213,6 +215,7 @@ Swipe on the left or right side to change brightness or volume Double tap to seek Double tap to pause + Player seek amount Tap twice on the right or left side to seek forwards or backwards Tap in the middle to pause @@ -236,7 +239,9 @@ Error backing up %s Search - Nginx Settings + Nginx + Credits and account + Updates and backup Nginx Credential You have to use the following format mycoolusername:mysecurepassword123 What is Nginx ? @@ -365,7 +370,9 @@ Skip this Update Update Preferred watch quality - Video player title max chars. + Video player title max chars + Video player resolution + Video buffer size Video buffer length Video cache on disk @@ -408,9 +415,11 @@ General Random Button Show random button on Homepage - Provider Languages + Provider languages App Layout - Preferred Media + Preferred media + Preferred media and language + User interface Auto TV layout @@ -507,4 +516,8 @@ Web Poster Image + Player + Resolution and title + Title + Resolution diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml deleted file mode 100644 index 2d74ef70..00000000 --- a/app/src/main/res/xml/settings.xml +++ /dev/null @@ -1,313 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/xml/settings_credits_account.xml b/app/src/main/res/xml/settings_credits_account.xml new file mode 100644 index 00000000..d30a25c1 --- /dev/null +++ b/app/src/main/res/xml/settings_credits_account.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/settings_media_lang.xml b/app/src/main/res/xml/settings_media_lang.xml new file mode 100644 index 00000000..f7681317 --- /dev/null +++ b/app/src/main/res/xml/settings_media_lang.xml @@ -0,0 +1,23 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/settings_nginx.xml b/app/src/main/res/xml/settings_nginx.xml new file mode 100644 index 00000000..3c133637 --- /dev/null +++ b/app/src/main/res/xml/settings_nginx.xml @@ -0,0 +1,24 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/settings_player.xml b/app/src/main/res/xml/settings_player.xml new file mode 100644 index 00000000..3d1a1ee7 --- /dev/null +++ b/app/src/main/res/xml/settings_player.xml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/settings_updates.xml b/app/src/main/res/xml/settings_updates.xml new file mode 100644 index 00000000..aedc9c2d --- /dev/null +++ b/app/src/main/res/xml/settings_updates.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/settins_ui.xml b/app/src/main/res/xml/settins_ui.xml new file mode 100644 index 00000000..d642d48f --- /dev/null +++ b/app/src/main/res/xml/settins_ui.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + \ No newline at end of file