diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fa1cabaa..f8e0091c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -48,7 +48,7 @@ android { targetSdk = 33 versionCode = 55 - versionName = "3.3.0" + versionName = "3.4.0" resValue("string", "app_version", "${defaultConfig.versionName}${versionNameSuffix ?: ""}") diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 6ae21691..ee54783f 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -4,25 +4,20 @@ import android.content.ComponentName import android.content.Intent import android.content.res.ColorStateList import android.content.res.Configuration -import android.os.Build import android.os.Bundle import android.util.Log import android.view.KeyEvent import android.view.Menu import android.view.MenuItem import android.view.WindowManager -import android.widget.FrameLayout -import android.widget.LinearLayout +import android.widget.Toast import androidx.activity.result.ActivityResultLauncher import androidx.annotation.IdRes import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.isVisible -import androidx.core.view.marginRight -import androidx.core.view.setMargins import androidx.fragment.app.FragmentActivity -import androidx.fragment.app.FragmentContainerView import androidx.navigation.NavController import androidx.navigation.NavDestination import androidx.navigation.NavDestination.Companion.hierarchy @@ -77,6 +72,7 @@ import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.updateT import com.lagradost.cloudstream3.ui.settings.SettingsGeneral import com.lagradost.cloudstream3.ui.setup.HAS_DONE_SETUP_KEY import com.lagradost.cloudstream3.ui.setup.SetupFragmentExtensions +import com.lagradost.cloudstream3.utils.* import com.lagradost.cloudstream3.utils.AppUtils.isCastApiAvailable import com.lagradost.cloudstream3.utils.AppUtils.loadCache import com.lagradost.cloudstream3.utils.AppUtils.loadRepository @@ -87,8 +83,6 @@ import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.DataStore.getKey import com.lagradost.cloudstream3.utils.DataStore.setKey import com.lagradost.cloudstream3.utils.DataStoreHelper.migrateResumeWatching -import com.lagradost.cloudstream3.utils.Event -import com.lagradost.cloudstream3.utils.IOnBackPressed import com.lagradost.cloudstream3.utils.InAppUpdater.Companion.runAutoUpdate import com.lagradost.cloudstream3.utils.UIHelper.changeStatusBarState import com.lagradost.cloudstream3.utils.UIHelper.checkWrite @@ -97,9 +91,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.getResourceColor import com.lagradost.cloudstream3.utils.UIHelper.hideKeyboard import com.lagradost.cloudstream3.utils.UIHelper.navigate import com.lagradost.cloudstream3.utils.UIHelper.requestRW -import com.lagradost.cloudstream3.utils.UIHelper.toPx -import com.lagradost.cloudstream3.utils.USER_PROVIDER_API -import com.lagradost.cloudstream3.utils.USER_SELECTED_HOMEPAGE_API import com.lagradost.nicehttp.Requests import com.lagradost.nicehttp.ResponseParser import kotlinx.android.synthetic.main.activity_main.* @@ -475,6 +466,11 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { override fun onPause() { super.onPause() + + // Start any delayed updates + if (ApkInstaller.delayedInstaller?.startInstallation() == true) { + Toast.makeText(this, R.string.update_started, Toast.LENGTH_LONG).show() + } try { if (isCastApiAvailable()) { mSessionManager.removeSessionManagerListener(mSessionManagerListener) diff --git a/app/src/main/java/com/lagradost/cloudstream3/network/CloudflareKiller.kt b/app/src/main/java/com/lagradost/cloudstream3/network/CloudflareKiller.kt index 7dc8dba7..068cb968 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/network/CloudflareKiller.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/network/CloudflareKiller.kt @@ -5,6 +5,7 @@ import android.webkit.CookieManager import androidx.annotation.AnyThread import com.lagradost.cloudstream3.app import com.lagradost.cloudstream3.mvvm.debugWarning +import com.lagradost.cloudstream3.mvvm.normalSafeApiCall import com.lagradost.nicehttp.Requests.Companion.await import com.lagradost.nicehttp.cookies import kotlinx.coroutines.runBlocking @@ -26,7 +27,10 @@ class CloudflareKiller : Interceptor { init { // Needs to clear cookies between sessions to generate new cookies. - CookieManager.getInstance().removeAllCookies(null) + normalSafeApiCall { + // This can throw an exception on unsupported devices :( + CookieManager.getInstance().removeAllCookies(null) + } } val savedCookies: MutableMap> = mutableMapOf() @@ -35,7 +39,7 @@ class CloudflareKiller : Interceptor { * Gets the headers with cookies, webview user agent included! * */ fun getCookieHeaders(url: String): Headers { - val userAgentHeaders = WebViewResolver.webViewUserAgent?.let { + val userAgentHeaders = WebViewResolver.webViewUserAgent?.let { mapOf("user-agent" to it) } ?: emptyMap() diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/InAppUpdater.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/InAppUpdater.kt index 796d509e..3acfb9a1 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/InAppUpdater.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/InAppUpdater.kt @@ -296,12 +296,21 @@ class InAppUpdater { val context = this builder.apply { setPositiveButton(R.string.update) { _, _ -> + // Forcefully start any delayed installations + if (ApkInstaller.delayedInstaller?.startInstallation() == true) return@setPositiveButton + showToast(context, R.string.download_started, Toast.LENGTH_LONG) // Check if the setting hasn't been changed - if (settingsManager.getInt(getString(R.string.apk_installer_key), -1) == -1) { + if (settingsManager.getInt( + getString(R.string.apk_installer_key), + -1 + ) == -1 + ) { if (isMiUi()) // Set to legacy if using miui - settingsManager.edit().putInt(getString(R.string.apk_installer_key), 1).apply() + settingsManager.edit() + .putInt(getString(R.string.apk_installer_key), 1) + .apply() } val currentInstaller = @@ -358,30 +367,19 @@ class InAppUpdater { return false } - fun isMiUi(): Boolean { + private fun isMiUi(): Boolean { return !TextUtils.isEmpty(getSystemProperty("ro.miui.ui.version.name")) } - fun getSystemProperty(propName: String): String? { - var line: String? = null - var input: BufferedReader? = null - try { + private fun getSystemProperty(propName: String): String? { + return try { val p = Runtime.getRuntime().exec("getprop $propName") - input = BufferedReader(InputStreamReader(p.inputStream), 1024) - line = input.readLine() - input.close() - } catch (ex: IOException) { - return null - } finally { - if (input != null) { - try { - input.close() - } catch (e: IOException) { - e.printStackTrace() - } + BufferedReader(InputStreamReader(p.inputStream), 1024).use { + it.readLine() } + } catch (ex: IOException) { + null } - return line } } } diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/PackageInstaller.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/PackageInstaller.kt index 5cf6c359..6b6d3928 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/PackageInstaller.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/PackageInstaller.kt @@ -5,15 +5,42 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.content.IntentSender import android.content.pm.PackageInstaller import android.os.Build +import android.widget.Toast +import com.lagradost.cloudstream3.R import com.lagradost.cloudstream3.mvvm.logError +import com.lagradost.cloudstream3.mvvm.normalSafeApiCall +import com.lagradost.cloudstream3.utils.Coroutines.main import java.io.InputStream const val INSTALL_ACTION = "ApkInstaller.INSTALL_ACTION" class ApkInstaller(private val service: PackageInstallerService) { + + companion object { + /** + * Used for postponed installations + **/ + var delayedInstaller: DelayedInstaller? = null + } + + inner class DelayedInstaller( + private val session: PackageInstaller.Session, + private val intent: IntentSender + ) { + fun startInstallation(): Boolean { + return try { + session.commit(intent) + true + } catch (e: Exception) { + false + }.also { delayedInstaller = null } + } + } + private val packageInstaller = service.packageManager.packageInstaller enum class InstallProgressStatus { @@ -76,7 +103,6 @@ class ApkInstaller(private val service: PackageInstallerService) { inputStream.close() } - installProgressStatus.invoke(InstallProgressStatus.Installing) val intentSender = PendingIntent.getBroadcast( service, @@ -85,7 +111,22 @@ class ApkInstaller(private val service: PackageInstallerService) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) PendingIntent.FLAG_MUTABLE else 0, ).intentSender - session.commit(intentSender) + // Use delayed installations on android 13 and only if "allow from unknown sources" is enabled + // if the app lacks installation permission it cannot ask for the permission when it's closed. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && + context.packageManager.canRequestPackageInstalls() + ) { + // Save for later installation since it's more jarring to have the app exit abruptly + delayedInstaller = DelayedInstaller(session, intentSender) + main { + // Use real toast since it should show even if app is exited + Toast.makeText(context, R.string.delayed_update_notice, Toast.LENGTH_LONG) + .show() + } + } else { + installProgressStatus.invoke(InstallProgressStatus.Installing) + session.commit(intentSender) + } } catch (e: Exception) { logError(e) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/PackageInstallerService.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/PackageInstallerService.kt index fc50bed5..1625981e 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/PackageInstallerService.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/PackageInstallerService.kt @@ -187,7 +187,7 @@ class PackageInstallerService : Service() { const val UPDATE_CHANNEL_ID = "cloudstream3.updates" const val UPDATE_CHANNEL_NAME = "App Updates" const val UPDATE_CHANNEL_DESCRIPTION = "App updates notification channel" - const val UPDATE_NOTIFICATION_ID = -68454136 + const val UPDATE_NOTIFICATION_ID = -68454136 // Random unique fun getIntent( context: Context, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c8526aa5..7a88f498 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -137,6 +137,7 @@ Download Canceled Download Done %s - %s + Update Started Stream Error Loading Links Internal Storage @@ -611,5 +612,6 @@ Legacy PackageInstaller + App will be updated upon exit \ No newline at end of file