Skip to content

Commit acbe703

Browse files
committed
Add Update settings screen for NewPipe Material
1 parent b0b63a4 commit acbe703

10 files changed

Lines changed: 598 additions & 125 deletions

File tree

app/src/main/java/org/schabi/newpipe/MainActivity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ protected void onPostCreate(final Bundle savedInstanceState) {
216216

217217
final App app = App.getInstance();
218218

219-
if (sharedPreferences.getBoolean(app.getString(R.string.update_app_key), false)
219+
if (sharedPreferences.getBoolean(app.getString(R.string.update_app_key), true)
220220
&& sharedPreferences
221221
.getBoolean(app.getString(R.string.update_check_consent_key), false)) {
222222
// Start the worker which is checking all conditions

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

Lines changed: 90 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -3,60 +3,48 @@ package org.schabi.newpipe
33
import android.content.Context
44
import android.content.Intent
55
import android.util.Log
6-
import android.widget.Toast
76
import androidx.core.app.NotificationCompat
87
import androidx.core.app.NotificationManagerCompat
98
import androidx.core.app.PendingIntentCompat
10-
import androidx.core.content.ContextCompat
119
import androidx.core.content.edit
1210
import androidx.core.net.toUri
1311
import androidx.preference.PreferenceManager
12+
import androidx.work.Data
1413
import androidx.work.OneTimeWorkRequestBuilder
1514
import androidx.work.WorkManager
1615
import androidx.work.Worker
1716
import androidx.work.WorkerParameters
1817
import androidx.work.workDataOf
19-
import com.grack.nanojson.JsonParser
20-
import com.grack.nanojson.JsonParserException
2118
import java.io.IOException
22-
import org.schabi.newpipe.extractor.downloader.Response
19+
import java.util.UUID
2320
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException
21+
import org.schabi.newpipe.update.NewPipeMaterialUpdateRepository
22+
import org.schabi.newpipe.update.NewPipeMaterialUpdateRepository.VersionComparison
2423
import org.schabi.newpipe.util.ReleaseVersionUtil
2524

2625
class 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
}

app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
import org.schabi.newpipe.MainActivity;
1111
import org.schabi.newpipe.R;
12-
import org.schabi.newpipe.util.ReleaseVersionUtil;
1312

1413
public class MainSettingsFragment extends BasePreferenceFragment {
1514
public static final boolean DEBUG = MainActivity.DEBUG;
@@ -22,14 +21,6 @@ public void onCreatePreferences(final Bundle savedInstanceState, final String ro
2221

2322
setHasOptionsMenu(true); // Otherwise onCreateOptionsMenu is not called
2423

25-
// Check if the app is updatable
26-
if (!ReleaseVersionUtil.INSTANCE.isReleaseApk()) {
27-
getPreferenceScreen().removePreference(
28-
requirePreference(R.string.update_pref_screen_key));
29-
30-
defaultPreferences.edit().putBoolean(getString(R.string.update_app_key), false).apply();
31-
}
32-
3324
// Hide debug preferences in RELEASE build variant
3425
if (!DEBUG) {
3526
getPreferenceScreen().removePreference(

0 commit comments

Comments
 (0)