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);
|
showProgress(true);
|
||||||
}
|
}
|
||||||
} else if (appUpdate.url != null) {
|
} else if (appUpdate.url != null) {
|
||||||
|
if (getContext() instanceof Activity activity) {
|
||||||
|
ApkInstaller.downloadAndInstall(activity, appUpdate.url, appUpdate.version);
|
||||||
|
} else {
|
||||||
Browser.openUrl(getContext(), appUpdate.url);
|
Browser.openUrl(getContext(), appUpdate.url);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
acceptTextView = new TextView(context);
|
acceptTextView = new TextView(context);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
import android.animation.AnimatorSet;
|
import android.animation.AnimatorSet;
|
||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
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.Components.spoilers.SpoilersTextView;
|
||||||
import org.telegram.ui.Stories.recorder.ButtonWithCounterView;
|
import org.telegram.ui.Stories.recorder.ButtonWithCounterView;
|
||||||
|
|
||||||
|
import tw.nekomimi.nekogram.helpers.ApkInstaller;
|
||||||
|
|
||||||
public class UpdateAppAlertDialog extends BottomSheet {
|
public class UpdateAppAlertDialog extends BottomSheet {
|
||||||
|
|
||||||
private TLRPC.TL_help_appUpdate appUpdate;
|
private TLRPC.TL_help_appUpdate appUpdate;
|
||||||
|
|
@ -297,9 +300,13 @@ public class UpdateAppAlertDialog extends BottomSheet {
|
||||||
doneButton.setOnClickListener(v -> {
|
doneButton.setOnClickListener(v -> {
|
||||||
if (update.document instanceof TLRPC.TL_document) {
|
if (update.document instanceof TLRPC.TL_document) {
|
||||||
FileLoader.getInstance(accountNum).loadFile(appUpdate.document, "update", FileLoader.PRIORITY_NORMAL, 1);
|
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 {
|
} else {
|
||||||
Browser.openUrl(context, appUpdate.url);
|
Browser.openUrl(context, appUpdate.url);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
dismiss();
|
dismiss();
|
||||||
});
|
});
|
||||||
container.addView(doneButton, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 48, Gravity.FILL_HORIZONTAL | Gravity.BOTTOM, 20, 0, 20, 48 + 4 + 8));
|
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.UserConfig;
|
||||||
import org.telegram.messenger.Utilities;
|
import org.telegram.messenger.Utilities;
|
||||||
import org.telegram.messenger.XiaomiUtilities;
|
import org.telegram.messenger.XiaomiUtilities;
|
||||||
|
import org.telegram.messenger.browser.Browser;
|
||||||
import org.telegram.tgnet.TLRPC;
|
import org.telegram.tgnet.TLRPC;
|
||||||
import org.telegram.ui.ActionBar.AlertDialog;
|
import org.telegram.ui.ActionBar.AlertDialog;
|
||||||
import org.telegram.ui.ActionBar.Theme;
|
import org.telegram.ui.ActionBar.Theme;
|
||||||
|
|
@ -44,9 +45,12 @@ import org.telegram.ui.LaunchActivity;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
|
@ -107,6 +111,113 @@ public final class ApkInstaller {
|
||||||
if (apk == null) {
|
if (apk == null) {
|
||||||
return;
|
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()) {
|
if (dialog != null && dialog.isShowing()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,7 @@
|
||||||
<string name="ShareNekogram">Share FoxiGram...</string>
|
<string name="ShareNekogram">Share FoxiGram...</string>
|
||||||
<string name="NekogramVersion">FoxiGram %1$s\nBased on Telegram %2$s\nDesigned by %3$s</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="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="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="UpdateInstallingRelaunch">The app will relaunch when the update completes.</string>
|
||||||
<string name="UpdateInstalledNotification">Update installation finished, tap to launch the app.</string>
|
<string name="UpdateInstalledNotification">Update installation finished, tap to launch the app.</string>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue