mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
feat(ui): add ignore battery optimisation dialog for uniterrupted downloads and notifications (#915)
This commit is contained in:
parent
fb89fd60b8
commit
51d91bf9a7
6 changed files with 152 additions and 10 deletions
|
@ -30,7 +30,7 @@ import com.google.android.gms.cast.framework.CastState
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.lagradost.cloudstream3.APIHolder
|
import com.lagradost.cloudstream3.APIHolder
|
||||||
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
|
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.DubStatus
|
||||||
import com.lagradost.cloudstream3.LoadResponse
|
import com.lagradost.cloudstream3.LoadResponse
|
||||||
import com.lagradost.cloudstream3.MainActivity.Companion.afterPluginsLoadedEvent
|
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.isCastApiAvailable
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.loadCache
|
import com.lagradost.cloudstream3.utils.AppUtils.loadCache
|
||||||
import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
|
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.ExtractorLink
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
|
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
|
val name = (viewModel.page.value as? Resource.Success)?.value?.title
|
||||||
?: txt(R.string.no_data).asStringNull(context) ?: ""
|
?: 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 {
|
resultFavorite.setOnClickListener {
|
||||||
viewModel.toggleFavoriteStatus(context) { newStatus: Boolean? ->
|
viewModel.toggleFavoriteStatus(context) { newStatus: Boolean? ->
|
||||||
|
@ -457,7 +459,7 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||||
|
|
||||||
val name = (viewModel.page.value as? Resource.Success)?.value?.title
|
val name = (viewModel.page.value as? Resource.Success)?.value?.title
|
||||||
?: txt(R.string.no_data).asStringNull(context) ?: ""
|
?: txt(R.string.no_data).asStringNull(context) ?: ""
|
||||||
CommonActivity.showToast(txt(message, name), Toast.LENGTH_SHORT)
|
showToast(txt(message, name), Toast.LENGTH_SHORT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mediaRouteButton.apply {
|
mediaRouteButton.apply {
|
||||||
|
@ -465,7 +467,7 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||||
alpha = if (chromecastSupport) 1f else 0.3f
|
alpha = if (chromecastSupport) 1f else 0.3f
|
||||||
if (!chromecastSupport) {
|
if (!chromecastSupport) {
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
CommonActivity.showToast(
|
showToast(
|
||||||
R.string.no_chromecast_support_toast,
|
R.string.no_chromecast_support_toast,
|
||||||
Toast.LENGTH_LONG
|
Toast.LENGTH_LONG
|
||||||
)
|
)
|
||||||
|
@ -640,6 +642,8 @@ open class ResultFragmentPhone : FullScreenPlayer() {
|
||||||
),
|
),
|
||||||
null
|
null
|
||||||
) { click ->
|
) { click ->
|
||||||
|
context?.let { openBatteryOptimizationSettings(it) }
|
||||||
|
|
||||||
when (click.action) {
|
when (click.action) {
|
||||||
DOWNLOAD_ACTION_DOWNLOAD -> {
|
DOWNLOAD_ACTION_DOWNLOAD -> {
|
||||||
viewModel.handleAction(
|
viewModel.handleAction(
|
||||||
|
|
|
@ -27,11 +27,15 @@ import com.lagradost.cloudstream3.mvvm.logError
|
||||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||||
import com.lagradost.cloudstream3.network.initClient
|
import com.lagradost.cloudstream3.network.initClient
|
||||||
import com.lagradost.cloudstream3.ui.EasterEggMonke
|
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.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.getPref
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setPaddingBottom
|
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.setToolBarScrollFlags
|
||||||
import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.setUpToolbar
|
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.showBottomDialog
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
||||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
|
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showMultiDialog
|
||||||
|
@ -45,7 +49,6 @@ import com.lagradost.safefile.SafeFile
|
||||||
|
|
||||||
// Change local language settings in the app.
|
// Change local language settings in the app.
|
||||||
fun getCurrentLocale(context: Context): String {
|
fun getCurrentLocale(context: Context): String {
|
||||||
// val dm = res.displayMetrics
|
|
||||||
val res = context.resources
|
val res = context.resources
|
||||||
val conf = res.configuration
|
val conf = res.configuration
|
||||||
|
|
||||||
|
@ -204,6 +207,20 @@ class SettingsGeneral : PreferenceFragmentCompat() {
|
||||||
return@setOnPreferenceClickListener true
|
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() {
|
fun showAdd() {
|
||||||
val providers = synchronized(allProviders) { allProviders.distinctBy { it.javaClass }.sortedBy { it.name } }
|
val providers = synchronized(allProviders) { allProviders.distinctBy { it.javaClass }.sortedBy { it.name } }
|
||||||
activity?.showDialog(
|
activity?.showDialog(
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
app/src/main/res/drawable/ic_battery.xml
Normal file
12
app/src/main/res/drawable/ic_battery.xml
Normal 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>
|
|
@ -654,6 +654,17 @@
|
||||||
<string name="confirm_exit_dialog">Are you sure you want to exit\?</string>
|
<string name="confirm_exit_dialog">Are you sure you want to exit\?</string>
|
||||||
<string name="yes">Yes</string>
|
<string name="yes">Yes</string>
|
||||||
<string name="no">No</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_downloading">Downloading app update…</string>
|
||||||
<string name="update_notification_installing">Installing 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>
|
<string name="update_notification_failed">Could not install the new version of the app</string>
|
||||||
|
|
|
@ -6,10 +6,7 @@
|
||||||
android:title="@string/app_language"
|
android:title="@string/app_language"
|
||||||
android:icon="@drawable/ic_baseline_language_24" />
|
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
|
<Preference
|
||||||
android:key="@string/legal_notice_key"
|
android:key="@string/legal_notice_key"
|
||||||
|
@ -22,6 +19,21 @@
|
||||||
android:icon="@drawable/benene"
|
android:icon="@drawable/benene"
|
||||||
app:summary="@string/benene_des" />
|
app:summary="@string/benene_des" />
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
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
|
<PreferenceCategory
|
||||||
android:title="@string/pref_category_bypass">
|
android:title="@string/pref_category_bypass">
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue