@@ -3,60 +3,48 @@ package org.schabi.newpipe
33import android.content.Context
44import android.content.Intent
55import android.util.Log
6- import android.widget.Toast
76import androidx.core.app.NotificationCompat
87import androidx.core.app.NotificationManagerCompat
98import androidx.core.app.PendingIntentCompat
10- import androidx.core.content.ContextCompat
119import androidx.core.content.edit
1210import androidx.core.net.toUri
1311import androidx.preference.PreferenceManager
12+ import androidx.work.Data
1413import androidx.work.OneTimeWorkRequestBuilder
1514import androidx.work.WorkManager
1615import androidx.work.Worker
1716import androidx.work.WorkerParameters
1817import androidx.work.workDataOf
19- import com.grack.nanojson.JsonParser
20- import com.grack.nanojson.JsonParserException
2118import java.io.IOException
22- import org.schabi.newpipe.extractor.downloader.Response
19+ import java.util.UUID
2320import org.schabi.newpipe.extractor.exceptions.ReCaptchaException
21+ import org.schabi.newpipe.update.NewPipeMaterialUpdateRepository
22+ import org.schabi.newpipe.update.NewPipeMaterialUpdateRepository.VersionComparison
2423import org.schabi.newpipe.util.ReleaseVersionUtil
2524
2625class NewVersionWorker (
2726 context : Context ,
2827 workerParams : WorkerParameters
2928) : Worker(context, workerParams) {
3029
31- /* *
32- * Method to compare the current and latest available app version.
33- * If a newer version is available, we show the update notification.
34- *
35- * @param versionName Name of new version
36- * @param apkLocationUrl Url with the new apk
37- * @param versionCode Code of new version
38- */
39- private fun compareAppVersionAndShowNotification (
40- versionName : String ,
41- apkLocationUrl : String? ,
42- versionCode : Int
43- ) {
44- if (BuildConfig .VERSION_CODE >= versionCode) {
45- if (inputData.getBoolean(IS_MANUAL , false )) {
46- // Show toast stating that the app is up-to-date if the update check was manual.
47- ContextCompat .getMainExecutor(applicationContext).execute {
48- Toast .makeText(
49- applicationContext,
50- R .string.app_update_unavailable_toast,
51- Toast .LENGTH_SHORT
52- ).show()
53- }
54- }
55- return
30+ private data class UpdateCheckResult (
31+ val release : NewPipeMaterialUpdateRepository .Release ,
32+ val installedVersion : String ,
33+ val comparison : VersionComparison
34+ )
35+
36+ private fun handleUpdateCheckResult (result : UpdateCheckResult , isManual : Boolean ) {
37+ if (! isManual && result.comparison == VersionComparison .NEWER ) {
38+ showUpdateNotification(result.release, result.installedVersion)
5639 }
40+ }
5741
58- // A pending intent to open the apk location url in the browser.
59- val intent = Intent (Intent .ACTION_VIEW , apkLocationUrl?.toUri())
42+ private fun showUpdateNotification (
43+ release : NewPipeMaterialUpdateRepository .Release ,
44+ installedVersion : String
45+ ) {
46+ val releaseUrl = release.htmlUrl.ifBlank { NewPipeMaterialUpdateRepository .RELEASES_URL }
47+ val intent = Intent (Intent .ACTION_VIEW , releaseUrl.toUri())
6048 intent.addFlags(Intent .FLAG_ACTIVITY_NEW_TASK )
6149 val pendingIntent = PendingIntentCompat .getActivity(
6250 applicationContext,
@@ -76,8 +64,9 @@ class NewVersionWorker(
7664 )
7765 .setContentText(
7866 applicationContext.getString(
79- R .string.app_update_available_notification_text,
80- versionName
67+ R .string.app_update_available_notification_text_material,
68+ release.version,
69+ installedVersion
8170 )
8271 )
8372
@@ -88,99 +77,106 @@ class NewVersionWorker(
8877 }
8978
9079 @Throws(IOException ::class , ReCaptchaException ::class )
91- private fun checkNewVersion () {
92- // Check if the current apk is a github one or not.
93- if (! ReleaseVersionUtil .isReleaseApk) {
94- return
95- }
80+ private fun checkNewVersion (): UpdateCheckResult ? {
81+ val isManual = inputData.getBoolean(IS_MANUAL , false )
82+ val prefs = PreferenceManager .getDefaultSharedPreferences(applicationContext)
9683
97- if (! inputData.getBoolean(IS_MANUAL , false )) {
98- val prefs = PreferenceManager .getDefaultSharedPreferences(applicationContext)
99- // Check if the last request has happened a certain time ago
100- // to reduce the number of API requests.
84+ if (! isManual) {
10185 val expiry = prefs.getLong(applicationContext.getString(R .string.update_expiry_key), 0 )
10286 if (! ReleaseVersionUtil .isLastUpdateCheckExpired(expiry)) {
103- return
87+ return null
10488 }
10589 }
10690
107- // Make a network request to get latest NewPipe data.
108- val response = DownloaderImpl .getInstance().get(NEWPIPE_API_URL )
109- handleResponse(response)
110- }
91+ val releases = NewPipeMaterialUpdateRepository .fetchReleases()
92+ val release = NewPipeMaterialUpdateRepository .selectLatestCandidateRelease(releases)
11193
112- private fun handleResponse (response : Response ) {
113- val prefs = PreferenceManager .getDefaultSharedPreferences(applicationContext)
114- try {
115- // Store a timestamp which needs to be exceeded,
116- // before a new request to the API is made.
117- val newExpiry = ReleaseVersionUtil .coerceUpdateCheckExpiry(response.getHeader(" expires" ))
118- prefs.edit {
119- putLong(applicationContext.getString(R .string.update_expiry_key), newExpiry)
120- }
121- } catch (e: Exception ) {
122- if (DEBUG ) {
123- Log .w(TAG , " Could not extract and save new expiry date" , e)
124- }
94+ val newExpiry = ReleaseVersionUtil .coerceUpdateCheckExpiry(null )
95+ prefs.edit {
96+ putLong(applicationContext.getString(R .string.update_expiry_key), newExpiry)
97+ putLong(
98+ applicationContext.getString(R .string.latest_update_check_timestamp_key),
99+ System .currentTimeMillis()
100+ )
125101 }
126102
127- // Parse the json from the response.
128- try {
129- val newpipeVersionInfo = JsonParser .`object `()
130- .from(response.responseBody()).getObject(" flavors" )
131- .getObject(" newpipe" )
132-
133- val versionName = newpipeVersionInfo.getString(" version" )
134- val versionCode = newpipeVersionInfo.getInt(" version_code" )
135- val apkLocationUrl = newpipeVersionInfo.getString(" apk" )
136- compareAppVersionAndShowNotification(versionName, apkLocationUrl, versionCode)
137- } catch (e: JsonParserException ) {
138- // Most likely something is wrong in data received from NEWPIPE_API_URL.
139- // Do not alarm user and fail silently.
140- if (DEBUG ) {
141- Log .w(TAG , " Could not get NewPipe API: invalid json" , e)
142- }
103+ release ? : return null
104+ val installedVersion = NewPipeMaterialUpdateRepository .installedVersionName()
105+ val comparison = NewPipeMaterialUpdateRepository .compareInstalledToLatest(
106+ installedVersion,
107+ release.version
108+ )
109+
110+ prefs.edit {
111+ putString(
112+ applicationContext.getString(R .string.latest_available_version_value_key),
113+ release.version
114+ )
115+ putString(
116+ applicationContext.getString(R .string.latest_available_release_url_key),
117+ release.htmlUrl
118+ )
119+ putString(
120+ applicationContext.getString(R .string.latest_available_changelog_key),
121+ release.body
122+ )
123+ }
124+
125+ return UpdateCheckResult (release, installedVersion, comparison).also {
126+ handleUpdateCheckResult(it, isManual)
143127 }
144128 }
145129
146130 override fun doWork (): Result {
131+ val isManual = inputData.getBoolean(IS_MANUAL , false )
147132 return try {
148- checkNewVersion()
149- Result .success()
133+ val result = checkNewVersion()
134+ if (isManual && result == null ) {
135+ Result .failure()
136+ } else {
137+ Result .success(result?.toOutputData() ? : Data .EMPTY )
138+ }
150139 } catch (e: IOException ) {
151- Log .w(TAG , " Could not fetch NewPipe API : probably network problem" , e)
140+ Log .w(TAG , " Could not fetch NewPipe Material GitHub releases : probably network problem" , e)
152141 Result .failure()
153142 } catch (e: ReCaptchaException ) {
154143 Log .e(TAG , " ReCaptchaException should never happen here." , e)
155144 Result .failure()
145+ } catch (e: Exception ) {
146+ Log .w(TAG , " Could not check NewPipe Material GitHub releases" , e)
147+ Result .failure()
156148 }
157149 }
158150
151+ private fun UpdateCheckResult.toOutputData (): Data {
152+ return workDataOf(
153+ OUTPUT_COMPARISON to comparison.name,
154+ OUTPUT_LATEST_VERSION to release.version,
155+ OUTPUT_INSTALLED_VERSION to installedVersion,
156+ OUTPUT_RELEASE_URL to release.htmlUrl
157+ )
158+ }
159+
159160 companion object {
160- private val DEBUG = MainActivity .DEBUG
161161 private val TAG = NewVersionWorker ::class .java.simpleName
162- private const val NEWPIPE_API_URL = " https://newpipe.net/api/data.json"
163162 private const val IS_MANUAL = " isManual"
163+ const val OUTPUT_COMPARISON = " comparison"
164+ const val OUTPUT_LATEST_VERSION = " latestVersion"
165+ const val OUTPUT_INSTALLED_VERSION = " installedVersion"
166+ const val OUTPUT_RELEASE_URL = " releaseUrl"
164167
165168 /* *
166- * Start a new worker which checks if all conditions for performing a version check are met,
167- * fetches the API endpoint [.NEWPIPE_API_URL] containing info about the latest NewPipe
168- * version and displays a notification about an available update if one is available.
169- * <br></br>
170- * Following conditions need to be met, before data is requested from the server:
171- *
172- * * The app is signed with the correct signing key (by TeamNewPipe / schabi).
173- * If the signing key differs from the one used upstream, the update cannot be installed.
174- * * The user enabled searching for and notifying about updates in the settings.
175- * * The app did not recently check for updates.
176- * We do not want to make unnecessary connections and DOS our servers.
169+ * Start a worker which checks GitHub Releases for NewPipe Material updates.
170+ * Manual checks bypass the stored expiry timestamp, while automatic checks respect it
171+ * to avoid contacting GitHub on every app launch.
177172 */
178173 @JvmStatic
179- fun enqueueNewVersionCheckingWork (context : Context , isManual : Boolean ) {
174+ fun enqueueNewVersionCheckingWork (context : Context , isManual : Boolean ): UUID {
180175 val workRequest = OneTimeWorkRequestBuilder <NewVersionWorker >()
181176 .setInputData(workDataOf(IS_MANUAL to isManual))
182177 .build()
183178 WorkManager .getInstance(context).enqueue(workRequest)
179+ return workRequest.id
184180 }
185181 }
186182}
0 commit comments