diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountHelper.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountHelper.kt index 1d6b41e5b..7725bad91 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/account/AccountHelper.kt @@ -37,8 +37,10 @@ import com.lagradost.cloudstream3.utils.DataStoreHelper import com.lagradost.cloudstream3.utils.DataStoreHelper.getDefaultAccount import com.lagradost.cloudstream3.utils.ImageLoader.loadImage import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe +import com.lagradost.cloudstream3.utils.UIHelper.hideProgress import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.UIHelper.showInputMethod +import com.lagradost.cloudstream3.utils.UIHelper.showProgress object AccountHelper { fun showAccountEditDialog( @@ -164,7 +166,7 @@ object AccountHelper { canSetPin = true - binding.editProfilePhotoButton.setOnClickListener({ + binding.editProfilePhotoButton.setOnClickListener { val bottomSheetDialog = BottomSheetDialog(context) val sheetBinding = BottomInputDialogBinding.inflate(LayoutInflater.from(context)) bottomSheetDialog.setContentView(sheetBinding.root) @@ -174,42 +176,46 @@ object AccountHelper { text1.text = context.getString(R.string.edit_profile_image_title) nginxTextInput.hint = context.getString(R.string.edit_profile_image_hint) - applyBtt.setOnClickListener({ + applyBtt.setOnClickListener { val url = sheetBinding.nginxTextInput.text.toString() - if (url.isNotEmpty()) { - val imageLoader = ImageLoader(context) - val request = ImageRequest.Builder(context) - .data(url) - .allowHardware(false) - .listener( - onSuccess = { _, _ -> - currentEditAccount = currentEditAccount.copy(customImage = url) - binding.accountImage.loadImage(url) - showToast( - R.string.edit_profile_image_success, - Toast.LENGTH_SHORT - ) - bottomSheetDialog.dismiss() - }, - onError = { _, _ -> - showToast( - R.string.edit_profile_image_error_invalid, - Toast.LENGTH_SHORT - ) - } - ) - .build() - imageLoader.enqueue(request) - } else { + if (url.isEmpty()) { showToast(R.string.edit_profile_image_error_empty, Toast.LENGTH_SHORT) + return@setOnClickListener } + applyBtt.showProgress() + val imageLoader = ImageLoader(context) + val request = ImageRequest.Builder(context) + .data(url) + .allowHardware(false) + .listener( + onSuccess = { _, _ -> + currentEditAccount = currentEditAccount.copy(customImage = url) + binding.accountImage.loadImage(url) + showToast( + R.string.edit_profile_image_success, + Toast.LENGTH_SHORT + ) + bottomSheetDialog.dismissSafe() + }, + onError = { _, _ -> + showToast( + R.string.edit_profile_image_error_invalid, + Toast.LENGTH_SHORT + ) + applyBtt.hideProgress() + }, + onCancel = { + applyBtt.hideProgress() + } + ) + .build() + imageLoader.enqueue(request) + } + sheetBinding.cancelBtt.setOnClickListener { bottomSheetDialog.dismissSafe() - }) - sheetBinding.cancelBtt.setOnClickListener({ - bottomSheetDialog.dismissSafe() - }) + } } - }) + } } fun showPinInputDialog( 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 d9e9cbded..b64355e23 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 @@ -117,8 +117,10 @@ import com.lagradost.cloudstream3.utils.UIHelper.clipboardHelper import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.fixSystemBarsPadding +import com.lagradost.cloudstream3.utils.UIHelper.hideProgress import com.lagradost.cloudstream3.utils.UIHelper.hideSystemUI import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage +import com.lagradost.cloudstream3.utils.UIHelper.showProgress import com.lagradost.cloudstream3.utils.UIHelper.toPx import com.lagradost.cloudstream3.utils.downloader.DownloadUtils.getImageBitmapFromUrl import com.lagradost.cloudstream3.utils.setText @@ -790,47 +792,58 @@ class GeneratorPlayer : FullScreenPlayer() { } binding.applyBtt.setOnClickListener { - currentSubtitle?.let { currentSubtitle -> - providers.firstOrNull { it.idPrefix == currentSubtitle.idPrefix }?.let { api -> - ioSafe { - when (val apiResource = - Resource.fromResult(api.resource(currentSubtitle))) { - is Resource.Success -> { - val subtitles = apiResource.value.getSubtitles().map { resource -> - SubtitleData( - originalName = resource.name ?: getName( - currentSubtitle, - true - ), - nameSuffix = "", - url = resource.url, - origin = resource.origin, - mimeType = resource.url.toSubtitleMimeType(), - headers = currentSubtitle.headers, - languageCode = currentSubtitle.lang - ) - } - if (subtitles.isEmpty()) { - showToast(R.string.no_subtitles) - return@ioSafe - } - runOnMainThread { - addAndSelectSubtitles(*subtitles.toTypedArray()) - } - } + val currentSubtitle = currentSubtitle + if (currentSubtitle == null) { + dialog.dismissSafe() + return@setOnClickListener + } - is Resource.Failure -> { - showToast(apiResource.errorString) - } + val api = providers.firstOrNull { it.idPrefix == currentSubtitle.idPrefix } + if (api == null) { + dialog.dismissSafe() + return@setOnClickListener + } - is Resource.Loading -> { - // not possible - } + binding.applyBtt.showProgress() + ioSafe { + val apiResource = + Resource.fromResult(api.resource(currentSubtitle)) + binding.applyBtt.hideProgress() + when (apiResource) { + is Resource.Success -> { + val subtitles = apiResource.value.getSubtitles().map { resource -> + SubtitleData( + originalName = resource.name ?: getName( + currentSubtitle, + true + ), + nameSuffix = "", + url = resource.url, + origin = resource.origin, + mimeType = resource.url.toSubtitleMimeType(), + headers = currentSubtitle.headers, + languageCode = currentSubtitle.lang + ) } + if (subtitles.isEmpty()) { + showToast(R.string.no_subtitles) + return@ioSafe + } + dialog.dismissSafe() + runOnMainThread { + addAndSelectSubtitles(*subtitles.toTypedArray()) + } + } + + is Resource.Failure -> { + showToast(apiResource.errorString) + } + + is Resource.Loading -> { + // not possible } } } - dialog.dismissSafe() } dialog.setOnDismissListener { 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 index 8d96a6b14..75ba55674 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/SettingsAccount.kt @@ -65,6 +65,8 @@ import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogTe import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard +import com.lagradost.cloudstream3.utils.UIHelper.hideProgress +import com.lagradost.cloudstream3.utils.UIHelper.showProgress import com.lagradost.cloudstream3.utils.setText import com.lagradost.cloudstream3.utils.txt import qrcode.QRCode @@ -348,6 +350,7 @@ class SettingsAccount : BasePreferenceFragmentCompat(), BiometricCallback { email = if (req.email) binding.loginEmailInput.text?.toString() else null, server = if (req.server) binding.loginServerInput.text?.toString() else null, ) + binding.applyBtt.showProgress() ioSafe { try { if (api.login(loginData)) { @@ -377,6 +380,8 @@ class SettingsAccount : BasePreferenceFragmentCompat(), BiometricCallback { api.name ) ) + } finally { + binding.applyBtt.hideProgress() } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt index ea0d5b2bd..107227497 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/ui/settings/extensions/ExtensionsFragment.kt @@ -39,6 +39,8 @@ import com.lagradost.cloudstream3.utils.AppContextUtils.setDefaultFocus import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe +import com.lagradost.cloudstream3.utils.UIHelper.hideProgress +import com.lagradost.cloudstream3.utils.UIHelper.showProgress import com.lagradost.cloudstream3.utils.setText class ExtensionsFragment : BaseFragment( @@ -275,45 +277,50 @@ class ExtensionsFragment : BaseFragment( } } - binding.applyBtt.setOnClickListener secondListener@{ + binding.applyBtt.setOnClickListener secondListener@{ val name = binding.repoNameInput.text?.toString() val urlInput = binding.repoUrlInput.text?.toString() if (urlInput.isNullOrEmpty()) { showToast(R.string.error_invalid_url, Toast.LENGTH_SHORT) return@secondListener } + binding.applyBtt.showProgress() ioSafe { - val url = RepositoryManager.parseRepoUrl(urlInput) - if (url.isNullOrBlank()) { - showToast(R.string.error_invalid_data, Toast.LENGTH_SHORT) - return@ioSafe + try { + val url = RepositoryManager.parseRepoUrl(urlInput) + if (url.isNullOrBlank()) { + showToast(R.string.error_invalid_data, Toast.LENGTH_SHORT) + return@ioSafe + } + val repository = RepositoryManager.parseRepository(url) + + // Exit if wrong repository + if (repository == null) { + showToast(R.string.no_repository_found_error, Toast.LENGTH_LONG) + return@ioSafe + } + + val fixedName = if (!name.isNullOrBlank()) name + else repository.name + val newRepo = RepositoryData(repository.iconUrl, fixedName, url) + RepositoryManager.addRepository(newRepo) + extensionViewModel.loadStats() + extensionViewModel.loadRepositories() + + dialog.dismissSafe(activity) // Only dismiss if the repo was added + + val plugins = RepositoryManager.getRepoPlugins(newRepo) + if (plugins.isNullOrEmpty()) { + showToast(R.string.no_plugins_found_error, Toast.LENGTH_LONG) + return@ioSafe + } + + this@ExtensionsFragment.activity?.addRepositoryDialog( + newRepo + ) + } finally { + binding.applyBtt.hideProgress() } - val repository = RepositoryManager.parseRepository(url) - - // Exit if wrong repository - if (repository == null) { - showToast(R.string.no_repository_found_error, Toast.LENGTH_LONG) - return@ioSafe - } - - val fixedName = if (!name.isNullOrBlank()) name - else repository.name - val newRepo = RepositoryData(repository.iconUrl, fixedName, url) - RepositoryManager.addRepository(newRepo) - extensionViewModel.loadStats() - extensionViewModel.loadRepositories() - - dialog.dismissSafe(activity) // Only dismiss if the repo was added - - val plugins = RepositoryManager.getRepoPlugins(newRepo) - if (plugins.isNullOrEmpty()) { - showToast(R.string.no_plugins_found_error, Toast.LENGTH_LONG) - return@ioSafe - } - - this@ExtensionsFragment.activity?.addRepositoryDialog( - newRepo - ) } } binding.cancelBtt.setOnClickListener { @@ -321,6 +328,7 @@ class ExtensionsFragment : BaseFragment( } } + val isTv = isLayout(TV) binding.apply { addRepoButton.isGone = isTv 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 c12674816..69f22e8ef 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/UIHelper.kt @@ -65,9 +65,12 @@ import androidx.navigation.fragment.NavHostFragment import androidx.palette.graphics.Palette import androidx.preference.PreferenceManager import com.google.android.material.appbar.AppBarLayout +import com.google.android.material.button.MaterialButton import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipDrawable import com.google.android.material.chip.ChipGroup +import com.google.android.material.progressindicator.CircularProgressIndicatorSpec +import com.google.android.material.progressindicator.IndeterminateDrawable import com.lagradost.cloudstream3.CloudStreamApp.Companion.context import com.lagradost.cloudstream3.CommonActivity.activity import com.lagradost.cloudstream3.CommonActivity.showToast @@ -583,6 +586,39 @@ object UIHelper { } } + /** + * Source: https://stackoverflow.com/questions/70954321/circular-progress-indicator-inside-buttons-android-material-design + * + * Shows indeterminate progress bar on this button in place of where icon would be. + * By default the tint of progress bar is the same as iconTint. + * + * @param tintColor (@ColorInt Int) Sets custom progress bar tint color. + */ + fun MaterialButton.showProgress(@ColorInt tintColor: Int = this.iconTint.defaultColor) = + // Use runOnMainThreadNative to allow process on io threads, to make the code a bit cleaner + runOnMainThreadNative { + val spec = CircularProgressIndicatorSpec( + context, null, 0, + com.google.android.material.R.style.Widget_Material3_CircularProgressIndicator_ExtraSmall + ) + + spec.indicatorColors = intArrayOf(tintColor) + + val progressIndicatorDrawable = + IndeterminateDrawable.createCircularDrawable(context, spec) + + this.icon = progressIndicatorDrawable + if (this.getTag(R.id.text1) == null) + this.setTag(R.id.text1, this.text) + this.text = "" + } + + fun MaterialButton.hideProgress() = + runOnMainThreadNative { + this.text = this.getTag(R.id.text1) as? String + this.icon = null + } + /**id, stringRes */ @SuppressLint("RestrictedApi") fun View.popupMenuNoIcons(