finalize in-app updates

need to hook this up to update checks still

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/284/head
androidacy-user 1 year ago
parent d763b4b85b
commit cb562c7aa1

@ -338,7 +338,7 @@ dependencies {
implementation 'com.jakewharton.timber:timber:5.0.1'
// ksp
implementation "com.google.devtools.ksp:symbol-processing-api:1.8.0-1.0.8"
implementation "com.google.devtools.ksp:symbol-processing-api:1.8.0-1.0.9"
implementation "androidx.security:security-crypto:1.1.0-alpha04"
}
@ -364,7 +364,6 @@ android {
}
buildFeatures {
viewBinding true
compose true
}
kotlinOptions {

@ -20,6 +20,7 @@
<uses-permission-sdk-23 android:name="android.permission.QUERY_ALL_PACKAGES" /> <!-- Supposed to fix bugs with old firmware, only requested on pre Marshmallow -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- Post background notifications -->
<uses-permission-sdk-23 android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application
android:name=".MainApplication"
@ -41,7 +42,6 @@
<activity
android:name=".UpdateActivity"
android:hardwareAccelerated="true"
android:process=":updater"
android:exported="false" />
<activity
android:name=".CrashHandler"

@ -3,7 +3,6 @@ package com.fox2code.mmm;
import com.fox2code.mmm.utils.io.Files;
import com.fox2code.mmm.utils.io.Http;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.BufferedReader;
@ -81,10 +80,16 @@ public class AppUpdateManager {
return this.peekShouldUpdate();
boolean preReleaseNewer = true;
try {
JSONArray releases = new JSONArray(new String(Http.doHttpGet(RELEASES_API_URL, false), StandardCharsets.UTF_8));
JSONObject releases = new JSONObject(new String(Http.doHttpGet(RELEASES_API_URL, false), StandardCharsets.UTF_8));
String latestRelease = null, latestPreRelease = null;
for (int i = 0; i < releases.length(); i++) {
JSONObject release = releases.getJSONObject(i);
JSONObject release;
try {
release = releases.getJSONObject(String.valueOf(i));
} catch (
Exception e) {
continue;
}
// Skip invalid entries
if (release.getBoolean("draft"))
continue;

@ -375,5 +375,18 @@ public class SetupActivity extends FoxActivity implements LanguageActivity {
IOException e) {
Timber.e(e);
}
// we literally only use these to create the http cache folders
File httpCacheDir = MainApplication.getINSTANCE().getDataDirWithPath("cache/WebView/Default/HTTP Cache/Code Cache/js");
File httpCacheDir2 = MainApplication.getINSTANCE().getDataDirWithPath("cache/WebView/Default/HTTP Cache/Code Cache/wasm");
if (!httpCacheDir.exists()) {
if (httpCacheDir.mkdirs()) {
Timber.d("Created http cache dir");
}
}
if (!httpCacheDir2.exists()) {
if (httpCacheDir2.mkdirs()) {
Timber.d("Created http cache dir");
}
}
}
}

@ -1,12 +1,15 @@
package com.fox2code.mmm;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.FileProvider;
import com.fox2code.foxcompat.app.FoxActivity;
import com.fox2code.mmm.utils.io.Http;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.progressindicator.LinearProgressIndicator;
@ -21,8 +24,10 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Objects;
import timber.log.Timber;
@SuppressWarnings("UnnecessaryReturnStatement")
public class UpdateActivity extends AppCompatActivity {
public class UpdateActivity extends FoxActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -36,91 +41,105 @@ public class UpdateActivity extends AppCompatActivity {
// set status text to please wait
statusTextView.setText(R.string.please_wait);
// for debug builds, set update_debug_warning to visible and return
if (BuildConfig.DEBUG) {
findViewById(R.id.update_debug_warning).setVisibility(MaterialTextView.VISIBLE);
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(0, false);
statusTextView.setVisibility(MaterialTextView.INVISIBLE);
return;
}
// Now, parse the intent
Bundle extras = getIntent().getExtras();
// if extras is null, then we are in a bad state or user launched the activity manually
if (extras == null) {
// set status text to error
statusTextView.setText(R.string.error_no_extras);
// set progress bar to error
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(0, false);
// return
return;
}
// get action
ACTIONS action = ACTIONS.valueOf(extras.getString("action"));
// if action is null, then we are in a bad state or user launched the activity manually
if (Objects.isNull(action)) {
// set status text to error
statusTextView.setText(R.string.error_no_action);
// set progress bar to error
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(0, false);
// return
return;
}
// For check action, we need to check if there is an update using the AppUpdateManager.peekShouldUpdate()
if (action == ACTIONS.CHECK) {
checkForUpdate();
} else if (action == ACTIONS.DOWNLOAD) {
try {
downloadUpdate();
} catch (JSONException e) {
e.printStackTrace();
// set status text to error
statusTextView.setText(R.string.error_download_update);
// set progress bar to error
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(100, false);
}
} else if (action == ACTIONS.INSTALL) {
// ensure path was passed and points to a file within our cache directory
String path = extras.getString("path");
if (path == null) {
// set status text to error
statusTextView.setText(R.string.no_file_found);
// set progress bar to error
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(0, false);
// return
/**if (BuildConfig.DEBUG) {
findViewById(R.id.update_debug_warning).setVisibility(MaterialTextView.VISIBLE);
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(0, false);
statusTextView.setVisibility(MaterialTextView.INVISIBLE);
return;
}*/
new Thread(() -> {
// Now, parse the intent
String extras = getIntent().getAction();
// if extras is null, then we are in a bad state or user launched the activity manually
if (extras == null) {
runOnUiThread(() -> {
// set status text to error
statusTextView.setText(R.string.error_no_extras);
// set progress bar to error
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(0, false);
});
return;
}
File file = new File(path);
if (!file.exists()) {
// set status text to error
statusTextView.setText(R.string.no_file_found);
// set progress bar to error
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(0, false);
// get action
ACTIONS action = ACTIONS.valueOf(extras);
// if action is null, then we are in a bad state or user launched the activity manually
if (Objects.isNull(action)) {
runOnUiThread(() -> {
// set status text to error
statusTextView.setText(R.string.error_no_action);
// set progress bar to error
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(0, false);
});
// return
return;
}
if (!Objects.equals(file.getParentFile(), getCacheDir())) {
// set status text to error
statusTextView.setText(R.string.no_file_found);
// set progress bar to error
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(0, false);
// return
return;
// For check action, we need to check if there is an update using the AppUpdateManager.peekShouldUpdate()
if (action == ACTIONS.CHECK) {
checkForUpdate();
} else if (action == ACTIONS.DOWNLOAD) {
try {
downloadUpdate();
} catch (
JSONException e) {
e.printStackTrace();
runOnUiThread(() -> {
// set status text to error
statusTextView.setText(R.string.error_download_update);
// set progress bar to error
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(100, false);
});
}
} else if (action == ACTIONS.INSTALL) {
// ensure path was passed and points to a file within our cache directory
String path = getIntent().getStringExtra("path");
if (path == null) {
runOnUiThread(() -> {
// set status text to error
statusTextView.setText(R.string.no_file_found);
// set progress bar to error
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(0, false);
});
return;
}
File file = new File(path);
if (!file.exists()) {
runOnUiThread(() -> {
// set status text to error
statusTextView.setText(R.string.no_file_found);
// set progress bar to error
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(0, false);
});
// return
return;
}
if (!Objects.equals(file.getParentFile(), getCacheDir())) {
// set status text to error
runOnUiThread(() -> {
statusTextView.setText(R.string.no_file_found);
// set progress bar to error
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(0, false);
});
// return
return;
}
// set status text to installing
statusTextView.setText(R.string.installing_update);
// set progress bar to indeterminate
progressIndicator.setIndeterminate(true);
// install update
installUpdate(file);
}
// set status text to installing
statusTextView.setText(R.string.installing_update);
// set progress bar to indeterminate
progressIndicator.setIndeterminate(true);
// install update
installUpdate(file);
}
}).start();
}
public void checkForUpdate() {
@ -163,7 +182,11 @@ public class UpdateActivity extends AppCompatActivity {
lastestJSON = Http.doHttpGet(AppUpdateManager.RELEASES_API_URL, false);
} catch (
Exception e) {
e.printStackTrace();
// when logging, REMOVE the json from the log
String msg = e.getMessage();
// remove everything from the first { to the last }
msg = Objects.requireNonNull(msg).substring(0, msg.indexOf("{")) + msg.substring(msg.lastIndexOf("}") + 1);
Timber.e(msg);
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(100, false);
statusTextView.setText(R.string.error_download_update);
@ -174,11 +197,14 @@ public class UpdateActivity extends AppCompatActivity {
JSONArray assets = latestJSON.getJSONArray("assets");
// get the asset we want
JSONObject asset = null;
for (int i = 0; i < assets.length(); i++) {
JSONObject asset1 = assets.getJSONObject(i);
if (asset1.getString("name").contains(Build.SUPPORTED_ABIS[0])) {
asset = asset1;
break;
// iterate through assets until we find the one that contains Build.SUPPORTED_ABIS[0]
while (Objects.isNull(asset)) {
for (int i = 0; i < assets.length(); i++) {
JSONObject asset1 = assets.getJSONObject(i);
if (asset1.getString("name").contains(Build.SUPPORTED_ABIS[0])) {
asset = asset1;
break;
}
}
}
// if asset is null, then we are in a bad state
@ -195,52 +221,62 @@ public class UpdateActivity extends AppCompatActivity {
String downloadUrl = Objects.requireNonNull(asset).getString("browser_download_url");
// get the download size
long downloadSize = asset.getLong("size");
// set status text to downloading update
statusTextView.setText(getString(R.string.downloading_update, 0));
// set progress bar to 0
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(0, false);
runOnUiThread(() -> {
// set status text to downloading update
statusTextView.setText(getString(R.string.downloading_update, 0));
// set progress bar to 0
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(0, false);
});
// download the update
byte[] update = new byte[0];
try {
update = Http.doHttpGet(downloadUrl, (downloaded, total, done) -> {
update = Http.doHttpGet(downloadUrl, (downloaded, total, done) -> runOnUiThread(() -> {
// update progress bar
progressIndicator.setProgressCompat((int) (((float) downloaded / (float) total) * 100), true);
// update status text
statusTextView.setText(getString(R.string.downloading_update, (int) (((float) downloaded / (float) total) * 100)));
});
}));
} catch (
Exception e) {
e.printStackTrace();
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(100, false);
statusTextView.setText(R.string.error_download_update);
runOnUiThread(() -> {
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(100, false);
statusTextView.setText(R.string.error_download_update);
});
}
// if update is null, then we are in a bad state
if (Objects.isNull(update)) {
// set status text to error
statusTextView.setText(R.string.error_download_update);
// set progress bar to error
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(100, false);
runOnUiThread(() -> {
// set status text to error
statusTextView.setText(R.string.error_download_update);
// set progress bar to error
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(100, false);
});
// return
return;
}
// if update is not the same size as the download size, then we are in a bad state
if (update.length != downloadSize) {
// set status text to error
statusTextView.setText(R.string.error_download_update);
// set progress bar to error
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(100, false);
runOnUiThread(() -> {
// set status text to error
statusTextView.setText(R.string.error_download_update);
// set progress bar to error
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(100, false);
});
// return
return;
}
// set status text to installing update
statusTextView.setText(R.string.installing_update);
// set progress bar to 100
progressIndicator.setIndeterminate(true);
progressIndicator.setProgressCompat(100, false);
runOnUiThread(() -> {
statusTextView.setText(R.string.installing_update);
// set progress bar to 100
progressIndicator.setIndeterminate(true);
progressIndicator.setProgressCompat(100, false);
});
// save the update to the cache
File updateFile = null;
try {
@ -251,9 +287,11 @@ public class UpdateActivity extends AppCompatActivity {
} catch (
IOException e) {
e.printStackTrace();
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(100, false);
statusTextView.setText(R.string.error_download_update);
runOnUiThread(() -> {
progressIndicator.setIndeterminate(false);
progressIndicator.setProgressCompat(100, false);
statusTextView.setText(R.string.error_download_update);
});
}
// install the update
installUpdate(updateFile);
@ -261,19 +299,25 @@ public class UpdateActivity extends AppCompatActivity {
return;
}
@SuppressLint("RestrictedApi")
private void installUpdate(File updateFile) {
// get status text view
MaterialTextView statusTextView = findViewById(R.id.update_progress_text);
// set status text to installing update
statusTextView.setText(R.string.installing_update);
// set progress bar to 100
LinearProgressIndicator progressIndicator = findViewById(R.id.update_progress);
progressIndicator.setIndeterminate(true);
progressIndicator.setProgressCompat(100, false);
// install the update
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(updateFile), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
runOnUiThread(() -> {
MaterialTextView statusTextView = findViewById(R.id.update_progress_text);
// set status text to installing update
statusTextView.setText(R.string.installing_update);
// set progress bar to 100
LinearProgressIndicator progressIndicator = findViewById(R.id.update_progress);
progressIndicator.setIndeterminate(true);
progressIndicator.setProgressCompat(100, false);
});
// request install permissions
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
Context context = getApplicationContext();
Uri uri = FileProvider.getUriForFile(context, context.getPackageName() + ".file-provider", updateFile);
intent.setData(uri);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
// return
return;

@ -53,8 +53,11 @@ public class BackgroundUpdateChecker extends Worker {
if ("twrp-keep".equals(localModuleInfo.id))
continue;
// exclude all modules with id's stored in the pref pref_background_update_check_excludes
if (MainApplication.getSharedPreferences().getStringSet("pref_background_update_check_excludes", null).contains(localModuleInfo.id))
continue;
try {
if (MainApplication.getSharedPreferences().getStringSet("pref_background_update_check_excludes", null).contains(localModuleInfo.id))
continue;
} catch (Exception ignored) {
}
RepoModule repoModule = repoModules.get(localModuleInfo.id);
localModuleInfo.checkModuleUpdate();
if (localModuleInfo.updateVersionCode > localModuleInfo.versionCode && !PropUtils.isNullString(localModuleInfo.updateVersion)) {

@ -504,6 +504,16 @@ public class SettingsActivity extends FoxActivity implements LanguageActivity {
Toast.makeText(requireContext(), toastText, Toast.LENGTH_SHORT).show();
return true;
});
// for pref_background_update_check_debug_download, do the same as pref_update except with DOWNLOAD action
Preference debugDownload = findPreference("pref_background_update_check_debug_download");
debugDownload.setVisible(MainApplication.isDeveloper() && MainApplication.isBackgroundUpdateCheckEnabled() && !MainApplication.isWrapped());
debugDownload.setOnPreferenceClickListener(p -> {
devModeStep = 0;
Intent intent = new Intent(requireContext(), UpdateActivity.class);
intent.setAction(UpdateActivity.ACTIONS.DOWNLOAD.name());
startActivity(intent);
return true;
});
if (BuildConfig.DEBUG || BuildConfig.ENABLE_AUTO_UPDATER) {
linkClickable = findPreference("pref_report_bug");
linkClickable.setOnPreferenceClickListener(p -> {

@ -298,7 +298,7 @@
<string name="error_saving_logs">Could not save logs</string>
<string name="share_logs">Share FoxMMM logs</string>
<string name="not_official_build">This app is an unofficial FoxMMM build.</string>
<string name="crash_text">Uh-oh, we hit a snag!</string>=
<string name="crash_text">Uh-oh, we hit a snag!</string>
<string name="feedback_message">Give us more details about what you were doing when this happened. The more, the merrier!</string>
<string name="feedback_submit">Submit and restart</string>
<string name="please_feedback">Please help us out by telling us what you were trying to do when this happened.</string>
@ -330,7 +330,8 @@
<string name="title_activity_update">In-app Updater</string>
<string name="update_title">Update app</string>
<string name="update_message">An update may be available for FoxMMM. Please wait while we download and install it.</string>
<string name="update_button">Please wait...</string><string name="error_no_extras">ERROR: Invalid data received</string>
<string name="update_button">Please wait...</string>
<string name="error_no_extras">ERROR: Invalid data received on launch</string>
<string name="update_debug_warning">You appear to be running a debug build. Debug builds must be updated manually, and do not support in-app updates</string>
<string name="error_no_action">ERROR: Invalid action specified. Refusing to continue.</string><string name="update_available">Update found</string>
<string name="checking_for_update">Checking for updates…</string>
@ -338,5 +339,6 @@
<string name="download_update">Download update</string>
<string name="error_download_update">An error occurred downloading the update information.</string>
<string name="error_no_asset">ERROR: Failed to parse update information</string>
<string name="downloading_update">Downloading update… %1$d\%</string><string name="installing_update">Installing update…</string><string name="no_file_found">ERROR: Could not find update package.</string><string name="check_for_updates">Check for app updates</string>
<string name="downloading_update">Downloading update… %1$d%%</string>
<string name="installing_update">Installing update…</string><string name="no_file_found">ERROR: Could not find update package.</string><string name="check_for_updates">Check for app updates</string><string name="update_debug_download_pref">Test update download mechanism</string>
</resources>

@ -57,6 +57,12 @@
app:icon="@drawable/baseline_notification_important_24"
app:title="@string/notification_update_debug_pref" />
<!-- For debugging: launch update activity with download action -->
<Preference
app:icon="@drawable/ic_baseline_download_24"
app:key="pref_background_update_check_debug_download"
app:singleLineTitle="false"
app:title="@string/update_debug_download_pref" />
<com.fox2code.mmm.settings.LongClickablePreference
app:icon="@drawable/ic_baseline_system_update_24"

Loading…
Cancel
Save