Add in-app download and silent install for GitHub updates
- ApkInstaller.downloadAndInstall downloads APK by URL with a progress dialog - Reuses existing PackageInstaller silent-install pipeline via shared File path - Update dialogs now download in-app instead of opening a browser - Add UpdateDownloading string Verified: TMessagesProj compileReleaseJavaWithJavac succeeds.
This commit is contained in:
parent
12792f77f3
commit
d79c149e3e
4 changed files with 126 additions and 3 deletions
|
|
@ -149,8 +149,12 @@ public class BlockingUpdateView extends FrameLayout implements NotificationCente
|
|||
showProgress(true);
|
||||
}
|
||||
} else if (appUpdate.url != null) {
|
||||
if (getContext() instanceof Activity activity) {
|
||||
ApkInstaller.downloadAndInstall(activity, appUpdate.url, appUpdate.version);
|
||||
} else {
|
||||
Browser.openUrl(getContext(), appUpdate.url);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
acceptTextView = new TextView(context);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.animation.Animator;
|
|||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
|
|
@ -38,6 +39,8 @@ import org.telegram.ui.ActionBar.Theme;
|
|||
import org.telegram.ui.Components.spoilers.SpoilersTextView;
|
||||
import org.telegram.ui.Stories.recorder.ButtonWithCounterView;
|
||||
|
||||
import tw.nekomimi.nekogram.helpers.ApkInstaller;
|
||||
|
||||
public class UpdateAppAlertDialog extends BottomSheet {
|
||||
|
||||
private TLRPC.TL_help_appUpdate appUpdate;
|
||||
|
|
@ -297,9 +300,13 @@ public class UpdateAppAlertDialog extends BottomSheet {
|
|||
doneButton.setOnClickListener(v -> {
|
||||
if (update.document instanceof TLRPC.TL_document) {
|
||||
FileLoader.getInstance(accountNum).loadFile(appUpdate.document, "update", FileLoader.PRIORITY_NORMAL, 1);
|
||||
} else if (appUpdate.url != null) {
|
||||
if (getContext() instanceof Activity activity) {
|
||||
ApkInstaller.downloadAndInstall(activity, appUpdate.url, appUpdate.version);
|
||||
} else {
|
||||
Browser.openUrl(context, appUpdate.url);
|
||||
}
|
||||
}
|
||||
dismiss();
|
||||
});
|
||||
container.addView(doneButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.FILL_HORIZONTAL | Gravity.BOTTOM, 20, 0, 20, 48 + 4 + 8));
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import org.telegram.messenger.R;
|
|||
import org.telegram.messenger.UserConfig;
|
||||
import org.telegram.messenger.Utilities;
|
||||
import org.telegram.messenger.XiaomiUtilities;
|
||||
import org.telegram.messenger.browser.Browser;
|
||||
import org.telegram.tgnet.TLRPC;
|
||||
import org.telegram.ui.ActionBar.AlertDialog;
|
||||
import org.telegram.ui.ActionBar.Theme;
|
||||
|
|
@ -44,9 +45,12 @@ import org.telegram.ui.LaunchActivity;
|
|||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
|
@ -107,6 +111,113 @@ public final class ApkInstaller {
|
|||
if (apk == null) {
|
||||
return;
|
||||
}
|
||||
showInstallDialog(context, apk);
|
||||
}
|
||||
|
||||
// Downloads an APK from a direct URL (e.g. a GitHub release asset) with a
|
||||
// progress dialog, then performs the same silent install as the Telegram
|
||||
// document update flow.
|
||||
public static void downloadAndInstall(Activity context, String url, String version) {
|
||||
if (context == null || TextUtils.isEmpty(url)) {
|
||||
return;
|
||||
}
|
||||
// Not a direct apk link (e.g. a release page) — just open it.
|
||||
if (!url.toLowerCase().endsWith(".apk")) {
|
||||
Browser.openUrl(context, url);
|
||||
return;
|
||||
}
|
||||
if (dialog != null && dialog.isShowing()) {
|
||||
return;
|
||||
}
|
||||
var progressDialog = new AlertDialog(context, AlertDialog.ALERT_TYPE_LOADING);
|
||||
progressDialog.setMessage(LocaleController.getString(R.string.UpdateDownloading));
|
||||
progressDialog.setCanCancel(false);
|
||||
progressDialog.show();
|
||||
dialog = progressDialog;
|
||||
|
||||
Utilities.globalQueue.postRunnable(() -> {
|
||||
File apk = downloadApk(context, url, version, progress -> AndroidUtilities.runOnUIThread(() -> {
|
||||
if (dialog != null && dialog.isShowing()) {
|
||||
dialog.setProgress(progress);
|
||||
}
|
||||
}));
|
||||
AndroidUtilities.runOnUIThread(() -> {
|
||||
if (dialog != null) {
|
||||
dialog.dismiss();
|
||||
dialog = null;
|
||||
}
|
||||
if (apk == null) {
|
||||
AlertsCreator.createSimpleAlert(context, LocaleController.getString(R.string.ErrorOccurred)).show();
|
||||
return;
|
||||
}
|
||||
showInstallDialog(context, apk);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// @WorkerThread @Nullable
|
||||
private static File downloadApk(Activity context, String url, String version, Utilities.Callback<Integer> onProgress) {
|
||||
HttpURLConnection connection = null;
|
||||
try {
|
||||
File dir = FileLoader.getDirectory(FileLoader.MEDIA_DIR_CACHE);
|
||||
String name = "update-" + (TextUtils.isEmpty(version) ? "latest" : version.replaceAll("[^A-Za-z0-9._-]", "_")) + ".apk";
|
||||
File apk = new File(dir, name);
|
||||
if (apk.exists()) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
apk.delete();
|
||||
}
|
||||
|
||||
URL downloadUrl = new URL(url);
|
||||
connection = (HttpURLConnection) downloadUrl.openConnection();
|
||||
connection.setInstanceFollowRedirects(true);
|
||||
connection.setConnectTimeout(15000);
|
||||
connection.setReadTimeout(30000);
|
||||
connection.setRequestProperty("User-Agent", ApplicationLoader.getApplicationId());
|
||||
|
||||
int code = connection.getResponseCode();
|
||||
if (code != HttpURLConnection.HTTP_OK) {
|
||||
FileLog.e("ApkInstaller: download failed, HTTP " + code);
|
||||
return null;
|
||||
}
|
||||
|
||||
long total = connection.getContentLength();
|
||||
long downloaded = 0;
|
||||
int lastReported = -1;
|
||||
try (InputStream in = connection.getInputStream();
|
||||
OutputStream out = new FileOutputStream(apk)) {
|
||||
byte[] buffer = new byte[8192];
|
||||
int read;
|
||||
while ((read = in.read(buffer)) >= 0) {
|
||||
out.write(buffer, 0, read);
|
||||
downloaded += read;
|
||||
if (total > 0 && onProgress != null) {
|
||||
int progress = (int) (downloaded * 100 / total);
|
||||
if (progress != lastReported) {
|
||||
lastReported = progress;
|
||||
onProgress.run(progress);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return apk;
|
||||
} catch (Exception e) {
|
||||
FileLog.e(e);
|
||||
return null;
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void showInstallDialog(Activity context, File apk) {
|
||||
if (context == null || apk == null || !apk.exists()) {
|
||||
return;
|
||||
}
|
||||
if (hasBrokenPackageInstaller(context)) {
|
||||
AndroidUtilities.openForView(apk, "install.apk", "application/vnd.android.package-archive", context, null, false);
|
||||
return;
|
||||
}
|
||||
if (dialog != null && dialog.isShowing()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,6 +149,7 @@
|
|||
<string name="ShareNekogram">Share FoxiGram...</string>
|
||||
<string name="NekogramVersion">FoxiGram %1$s\nBased on Telegram %2$s\nDesigned by %3$s</string>
|
||||
<string name="UpdateInstalling">Installing update...</string>
|
||||
<string name="UpdateDownloading">Downloading update...</string>
|
||||
<string name="UpdateInstallingNotification">A notification will be shown when the update completes.</string>
|
||||
<string name="UpdateInstallingRelaunch">The app will relaunch when the update completes.</string>
|
||||
<string name="UpdateInstalledNotification">Update installation finished, tap to launch the app.</string>
|
||||
|
|
|
|||
Loading…
Reference in a new issue