NewPipe/app/src/main/java/org/schabi/newpipe/NewVersionWorker.kt

181 lines
7.4 KiB
Kotlin
Raw Normal View History

2022-03-03 19:34:35 +01:00
package org.schabi.newpipe
import android.content.Context
import android.content.Intent
import android.util.Log
import android.widget.Toast
2022-03-03 19:34:35 +01:00
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.PendingIntentCompat
import androidx.core.content.ContextCompat
2022-03-03 19:34:35 +01:00
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.preference.PreferenceManager
import androidx.work.OneTimeWorkRequestBuilder
2022-03-03 19:34:35 +01:00
import androidx.work.WorkManager
import androidx.work.Worker
import androidx.work.WorkerParameters
import androidx.work.workDataOf
2022-03-03 19:34:35 +01:00
import com.grack.nanojson.JsonParser
import com.grack.nanojson.JsonParserException
import org.schabi.newpipe.extractor.downloader.Response
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException
import org.schabi.newpipe.util.ReleaseVersionUtil.coerceUpdateCheckExpiry
import org.schabi.newpipe.util.ReleaseVersionUtil.isLastUpdateCheckExpired
import org.schabi.newpipe.util.ReleaseVersionUtil.isReleaseApk
import java.io.IOException
class NewVersionWorker(
context: Context,
workerParams: WorkerParameters
) : Worker(context, workerParams) {
/**
* Method to compare the current and latest available app version.
* If a newer version is available, we show the update notification.
*
* @param versionName Name of new version
* @param apkLocationUrl Url with the new apk
* @param versionCode Code of new version
*/
private fun compareAppVersionAndShowNotification(
versionName: String,
apkLocationUrl: String?,
versionCode: Int
) {
if (BuildConfig.VERSION_CODE >= versionCode) {
if (inputData.getBoolean(IS_MANUAL, false)) {
// Show toast stating that the app is up-to-date if the update check was manual.
ContextCompat.getMainExecutor(applicationContext).execute {
2022-10-10 02:53:46 +02:00
Toast.makeText(
applicationContext, R.string.app_update_unavailable_toast,
Toast.LENGTH_SHORT
).show()
}
}
2022-03-03 19:34:35 +01:00
return
}
// A pending intent to open the apk location url in the browser.
val intent = Intent(Intent.ACTION_VIEW, apkLocationUrl?.toUri())
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
val pendingIntent = PendingIntentCompat.getActivity(
applicationContext, 0, intent, 0, false
)
val channelId = applicationContext.getString(R.string.app_update_notification_channel_id)
val notificationBuilder = NotificationCompat.Builder(applicationContext, channelId)
2022-03-03 19:34:35 +01:00
.setSmallIcon(R.drawable.ic_newpipe_update)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
2022-10-10 02:53:46 +02:00
.setContentTitle(
applicationContext.getString(R.string.app_update_available_notification_title)
)
.setContentText(
applicationContext.getString(
R.string.app_update_available_notification_text, versionName
)
)
val notificationManager = NotificationManagerCompat.from(applicationContext)
2022-03-03 19:34:35 +01:00
notificationManager.notify(2000, notificationBuilder.build())
}
@Throws(IOException::class, ReCaptchaException::class)
private fun checkNewVersion() {
// Check if the current apk is a github one or not.
if (!isReleaseApk()) {
return
}
if (!inputData.getBoolean(IS_MANUAL, false)) {
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
// Check if the last request has happened a certain time ago
// to reduce the number of API requests.
val expiry = prefs.getLong(applicationContext.getString(R.string.update_expiry_key), 0)
if (!isLastUpdateCheckExpired(expiry)) {
return
}
2022-03-03 19:34:35 +01:00
}
// Make a network request to get latest NewPipe data.
val response = DownloaderImpl.getInstance().get(NEWPIPE_API_URL)
handleResponse(response)
}
private fun handleResponse(response: Response) {
val prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext)
try {
// Store a timestamp which needs to be exceeded,
// before a new request to the API is made.
val newExpiry = coerceUpdateCheckExpiry(response.getHeader("expires"))
prefs.edit {
putLong(applicationContext.getString(R.string.update_expiry_key), newExpiry)
}
} catch (e: Exception) {
if (DEBUG) {
Log.w(TAG, "Could not extract and save new expiry date", e)
}
}
// Parse the json from the response.
try {
val newpipeVersionInfo = JsonParser.`object`()
2022-03-03 19:34:35 +01:00
.from(response.responseBody()).getObject("flavors")
.getObject("newpipe")
2022-03-03 19:34:35 +01:00
val versionName = newpipeVersionInfo.getString("version")
val versionCode = newpipeVersionInfo.getInt("version_code")
val apkLocationUrl = newpipeVersionInfo.getString("apk")
2022-03-03 19:34:35 +01:00
compareAppVersionAndShowNotification(versionName, apkLocationUrl, versionCode)
} catch (e: JsonParserException) {
// Most likely something is wrong in data received from NEWPIPE_API_URL.
// Do not alarm user and fail silently.
if (DEBUG) {
Log.w(TAG, "Could not get NewPipe API: invalid json", e)
}
}
}
override fun doWork(): Result {
return try {
2022-03-03 19:34:35 +01:00
checkNewVersion()
Result.success()
2022-03-03 19:34:35 +01:00
} catch (e: IOException) {
Log.w(TAG, "Could not fetch NewPipe API: probably network problem", e)
Result.failure()
2022-03-03 19:34:35 +01:00
} catch (e: ReCaptchaException) {
Log.e(TAG, "ReCaptchaException should never happen here.", e)
Result.failure()
2022-03-03 19:34:35 +01:00
}
}
companion object {
private val DEBUG = MainActivity.DEBUG
private val TAG = NewVersionWorker::class.java.simpleName
private const val NEWPIPE_API_URL = "https://newpipe.net/api/data.json"
private const val IS_MANUAL = "isManual"
2022-03-03 19:34:35 +01:00
/**
* Start a new worker which checks if all conditions for performing a version check are met,
* fetches the API endpoint [.NEWPIPE_API_URL] containing info about the latest NewPipe
* version and displays a notification about an available update if one is available.
2022-03-03 19:34:35 +01:00
* <br></br>
* Following conditions need to be met, before data is requested from the server:
2022-03-03 19:34:35 +01:00
*
* * The app is signed with the correct signing key (by TeamNewPipe / schabi).
* If the signing key differs from the one used upstream, the update cannot be installed.
* * The user enabled searching for and notifying about updates in the settings.
* * The app did not recently check for updates.
* We do not want to make unnecessary connections and DOS our servers.
*/
@JvmStatic
fun enqueueNewVersionCheckingWork(context: Context, isManual: Boolean) {
val workRequest = OneTimeWorkRequestBuilder<NewVersionWorker>()
.setInputData(workDataOf(IS_MANUAL to isManual))
.build()
2022-03-03 19:34:35 +01:00
WorkManager.getInstance(context).enqueue(workRequest)
}
}
}