feat(ui): add ignore battery optimisation dialog for uniterrupted downloads and notifications (#915)

This commit is contained in:
IndusAryan 2024-03-25 05:48:26 +05:30 committed by GitHub
parent fb89fd60b8
commit 51d91bf9a7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 152 additions and 10 deletions

View file

@ -30,7 +30,7 @@ import com.google.android.gms.cast.framework.CastState
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.lagradost.cloudstream3.APIHolder
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
import com.lagradost.cloudstream3.CommonActivity
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.DubStatus
import com.lagradost.cloudstream3.LoadResponse
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
@ -61,6 +61,7 @@ import com.lagradost.cloudstream3.utils.AppUtils.getNameFull
import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable
import com.lagradost.cloudstream3.utils.AppUtils.loadCache
import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
import com.lagradost.cloudstream3.utils.BatteryOptimizationChecker.openBatteryOptimizationSettings
import com.lagradost.cloudstream3.utils.ExtractorLink
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
@ -442,8 +443,9 @@ open class ResultFragmentPhone : FullScreenPlayer() {
val name = (viewModel.page.value as? Resource.Success)?.value?.title
?: txt(R.string.no_data).asStringNull(context) ?: ""
CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
showToast(txt(message, name), Toast.LENGTH_SHORT)
}
context?.let { openBatteryOptimizationSettings(it) }
}
resultFavorite.setOnClickListener {
viewModel.toggleFavoriteStatus(context) { newStatus: Boolean? ->
@ -457,7 +459,7 @@ open class ResultFragmentPhone : FullScreenPlayer() {
val name = (viewModel.page.value as? Resource.Success)?.value?.title
?: txt(R.string.no_data).asStringNull(context) ?: ""
CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
showToast(txt(message, name), Toast.LENGTH_SHORT)
}
}
mediaRouteButton.apply {
@ -465,7 +467,7 @@ open class ResultFragmentPhone : FullScreenPlayer() {
alpha = if (chromecastSupport) 1f else 0.3f
if (!chromecastSupport) {
setOnClickListener {
CommonActivity.showToast(
showToast(
R.string.no_chromecast_support_toast,
Toast.LENGTH_LONG
)
@ -640,6 +642,8 @@ open class ResultFragmentPhone : FullScreenPlayer() {
),
null
) { click ->
context?.let { openBatteryOptimizationSettings(it) }
when (click.action) {
DOWNLOAD_ACTION_DOWNLOAD -> {
viewModel.handleAction(

View file

@ -27,11 +27,15 @@ import com.lagradost.cloudstream3.mvvm.logError
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
import com.lagradost.cloudstream3.network.initClient
import com.lagradost.cloudstream3.ui.EasterEggMonke
import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
import com.lagradost.cloudstream3.ui.settings.Globals.beneneCount
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.getPref
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setToolBarScrollFlags
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
import com.lagradost.cloudstream3.utils.BatteryOptimizationChecker.isAppRestricted
import com.lagradost.cloudstream3.utils.BatteryOptimizationChecker.showBatteryOptimizationDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
@ -45,7 +49,6 @@ import com.lagradost.safefile.SafeFile
// Change local language settings in the app.
fun getCurrentLocale(context: Context): String {
// val dm = res.displayMetrics
val res = context.resources
val conf = res.configuration
@ -204,6 +207,20 @@ class SettingsGeneral : PreferenceFragmentCompat() {
return@setOnPreferenceClickListener true
}
// disable preference on tvs and emulators
getPref(R.string.battery_optimisation_key)?.isEnabled = isLayout(PHONE)
getPref(R.string.battery_optimisation_key)?.setOnPreferenceClickListener {
val ctx = context ?: return@setOnPreferenceClickListener false
if (isAppRestricted(ctx)) {
showBatteryOptimizationDialog(ctx)
} else {
showToast(R.string.app_unrestricted_toast)
}
true
}
fun showAdd() {
val providers = synchronized(allProviders) { allProviders.distinctBy { it.javaClass }.sortedBy { it.name } }
activity?.showDialog(

View file

@ -0,0 +1,86 @@
package com.lagradost.cloudstream3.utils
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.PowerManager
import android.provider.Settings
import android.util.Log
import androidx.appcompat.app.AlertDialog
import androidx.preference.PreferenceManager
import com.lagradost.cloudstream3.BuildConfig
import com.lagradost.cloudstream3.CommonActivity.showToast
import com.lagradost.cloudstream3.R
import com.lagradost.cloudstream3.ui.settings.Globals.PHONE
import com.lagradost.cloudstream3.ui.settings.Globals.isLayout
const val packageName = BuildConfig.APPLICATION_ID
const val TAG = "PowerManagerAPI"
object BatteryOptimizationChecker {
fun isAppRestricted(context: Context?): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && context != null) {
val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
return !powerManager.isIgnoringBatteryOptimizations(context.packageName)
}
return false // below Marshmallow, it's always unrestricted when app is in background
}
fun openBatteryOptimizationSettings(context: Context) {
if (shouldShowBatteryOptimizationDialog(context)) {
showBatteryOptimizationDialog(context)
}
}
fun showBatteryOptimizationDialog(context: Context) {
val settingsManager = PreferenceManager.getDefaultSharedPreferences(context)
try {
context.let {
AlertDialog.Builder(it)
.setTitle(R.string.battery_dialog_title)
.setIcon(R.drawable.ic_battery)
.setMessage(R.string.battery_dialog_message)
.setPositiveButton(R.string.ok) { _, _ ->
intentOpenAppInfo(it)
}
.setNegativeButton(R.string.cancel) { _, _ ->
settingsManager.edit()
.putBoolean(context.getString(R.string.battery_optimisation_key), false)
.apply()
}
.show()
}
} catch (t: Throwable) {
Log.e(TAG, "Error showing battery optimization dialog", t)
}
}
private fun shouldShowBatteryOptimizationDialog(context: Context): Boolean {
val isRestricted = isAppRestricted(context)
val isOptimizedNotShown = PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(context.getString(R.string.battery_optimisation_key), true)
return isRestricted && isOptimizedNotShown && isLayout(PHONE)
}
private fun intentOpenAppInfo(context: Context) {
val intent = Intent()
try {
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(Uri.fromParts("package", packageName, null))
context.startActivity(intent, Bundle())
} catch (t: Throwable) {
Log.e(TAG, "Unable to invoke any intent", t)
if (t is ActivityNotFoundException) {
showToast("Exception: Activity Not Found")
} else {
showToast(R.string.app_info_intent_error)
}
}
}
}

View file

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/white"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M15.67,4L14,4L14,3c0,-0.55 -0.45,-1 -1,-1h-2c-0.55,0 -1,0.45 -1,1v1L8.33,4C7.6,4 7,4.6 7,5.33v15.33C7,21.4 7.6,22 8.34,22h7.32c0.74,0 1.34,-0.6 1.34,-1.33L17,5.33C17,4.6 16.4,4 15.67,4zM13,18h-2v-2h2v2zM13,13c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1v-3c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v3z" />
</vector>

View file

@ -654,6 +654,17 @@
<string name="confirm_exit_dialog">Are you sure you want to exit\?</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="ok">OK</string>
<string name="battery_dialog_title">Disable Battery optimization</string>
<string name="battery_dialog_message">To ensure uninterrupted downloads and notifications for subscribed
TV shows, CloudStream needs permission to run in background. By pressing "OK", you\'ll be directed to App info.
There, scroll to 𝘼𝙥𝙥 𝙗𝙖𝙩𝙩𝙚𝙧𝙮 𝙪𝙨𝙖𝙜𝙚 and set battery usage to 𝙐𝙣𝙧𝙚𝙨𝙩𝙧𝙞𝙘𝙩𝙚𝙙. Please note, this permission
does not mean CS3 will drain your battery. It will only operate in the background when necessary, such as
when receiving notifications or downloading videos from official extensions. If you choose to cancel,
you can adjust this setting later in 𝙂𝙚𝙣𝙚𝙧𝙖𝙡 𝙎𝙚𝙩𝙩𝙞𝙣𝙜𝙨.</string>
<string name="battery_optimisation_key" translatable="false">battery_optimisation</string>
<string name="app_unrestricted_toast">App battery usage is already set to unrestricted</string>
<string name="app_info_intent_error">Unable to open CloudStream\'s App info.</string>
<string name="update_notification_downloading">Downloading app update…</string>
<string name="update_notification_installing">Installing app update…</string>
<string name="update_notification_failed">Could not install the new version of the app</string>

View file

@ -6,10 +6,7 @@
android:title="@string/app_language"
android:icon="@drawable/ic_baseline_language_24" />
<Preference
android:key="@string/download_path_key"
android:title="@string/download_path_pref"
android:icon="@drawable/netflix_download" />
<Preference
android:key="@string/legal_notice_key"
@ -23,7 +20,22 @@
app:summary="@string/benene_des" />
<PreferenceCategory
android:title="@string/pref_category_bypass">
android:title="@string/title_downloads">
<Preference
android:key="@string/download_path_key"
android:title="@string/download_path_pref"
android:icon="@drawable/netflix_download" />
<Preference
android:key="@string/battery_optimisation_key"
android:title="@string/battery_dialog_title"
android:icon="@drawable/ic_battery" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/pref_category_bypass">
<Preference
android:key="@string/override_site_key"