|
|
|
|
@@ -0,0 +1,226 @@
|
|
|
|
|
package com.codebridgex.webapp;
|
|
|
|
|
|
|
|
|
|
import android.app.Activity;
|
|
|
|
|
import android.app.AlertDialog;
|
|
|
|
|
import android.app.DownloadManager;
|
|
|
|
|
import android.content.BroadcastReceiver;
|
|
|
|
|
import android.content.Context;
|
|
|
|
|
import android.content.Intent;
|
|
|
|
|
import android.content.IntentFilter;
|
|
|
|
|
import android.content.pm.PackageInfo;
|
|
|
|
|
import android.net.Uri;
|
|
|
|
|
import android.os.Build;
|
|
|
|
|
import android.os.Environment;
|
|
|
|
|
import android.util.Log;
|
|
|
|
|
|
|
|
|
|
import androidx.core.content.FileProvider;
|
|
|
|
|
|
|
|
|
|
import org.json.JSONObject;
|
|
|
|
|
|
|
|
|
|
import java.io.BufferedReader;
|
|
|
|
|
import java.io.File;
|
|
|
|
|
import java.io.InputStreamReader;
|
|
|
|
|
import java.net.HttpURLConnection;
|
|
|
|
|
import java.net.URL;
|
|
|
|
|
import java.util.concurrent.ExecutorService;
|
|
|
|
|
import java.util.concurrent.Executors;
|
|
|
|
|
|
|
|
|
|
public class AppUpdateChecker {
|
|
|
|
|
|
|
|
|
|
private static final String TAG = "AppUpdateChecker";
|
|
|
|
|
private static final String API_BASE_URL = "https://api.codebridge-x.com";
|
|
|
|
|
private static final String API_KEY = "sam-api-key-2025";
|
|
|
|
|
|
|
|
|
|
private final Activity activity;
|
|
|
|
|
private final ExecutorService executor = Executors.newSingleThreadExecutor();
|
|
|
|
|
|
|
|
|
|
public AppUpdateChecker(Activity activity) {
|
|
|
|
|
this.activity = activity;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 서버에서 최신 버전 확인
|
|
|
|
|
*/
|
|
|
|
|
public void checkForUpdate() {
|
|
|
|
|
executor.execute(() -> {
|
|
|
|
|
try {
|
|
|
|
|
int currentVersionCode = getCurrentVersionCode();
|
|
|
|
|
String urlStr = API_BASE_URL + "/api/v1/app/version?platform=android¤t_version_code=" + currentVersionCode;
|
|
|
|
|
|
|
|
|
|
URL url = new URL(urlStr);
|
|
|
|
|
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
|
|
|
|
conn.setRequestMethod("GET");
|
|
|
|
|
conn.setRequestProperty("X-API-KEY", API_KEY);
|
|
|
|
|
conn.setRequestProperty("Accept", "application/json");
|
|
|
|
|
conn.setConnectTimeout(10000);
|
|
|
|
|
conn.setReadTimeout(10000);
|
|
|
|
|
|
|
|
|
|
int responseCode = conn.getResponseCode();
|
|
|
|
|
if (responseCode != 200) {
|
|
|
|
|
Log.w(TAG, "Version check failed: HTTP " + responseCode);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
String line;
|
|
|
|
|
while ((line = reader.readLine()) != null) {
|
|
|
|
|
sb.append(line);
|
|
|
|
|
}
|
|
|
|
|
reader.close();
|
|
|
|
|
conn.disconnect();
|
|
|
|
|
|
|
|
|
|
JSONObject json = new JSONObject(sb.toString());
|
|
|
|
|
if (!json.optBoolean("success", false)) return;
|
|
|
|
|
|
|
|
|
|
JSONObject data = json.getJSONObject("data");
|
|
|
|
|
if (!data.optBoolean("has_update", false)) {
|
|
|
|
|
Log.d(TAG, "No update available");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JSONObject latestVersion = data.getJSONObject("latest_version");
|
|
|
|
|
String versionName = latestVersion.getString("version_name");
|
|
|
|
|
String releaseNotes = latestVersion.optString("release_notes", "");
|
|
|
|
|
boolean forceUpdate = latestVersion.optBoolean("force_update", false);
|
|
|
|
|
String downloadUrl = latestVersion.getString("download_url");
|
|
|
|
|
|
|
|
|
|
activity.runOnUiThread(() -> showUpdateDialog(versionName, releaseNotes, forceUpdate, downloadUrl));
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Log.e(TAG, "Update check error", e);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 업데이트 다이얼로그 표시
|
|
|
|
|
*/
|
|
|
|
|
private void showUpdateDialog(String versionName, String releaseNotes, boolean forceUpdate, String downloadUrl) {
|
|
|
|
|
if (activity.isFinishing() || activity.isDestroyed()) return;
|
|
|
|
|
|
|
|
|
|
StringBuilder message = new StringBuilder();
|
|
|
|
|
message.append("새 버전 v").append(versionName).append("이 있습니다.\n");
|
|
|
|
|
if (releaseNotes != null && !releaseNotes.isEmpty()) {
|
|
|
|
|
message.append("\n").append(releaseNotes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(activity)
|
|
|
|
|
.setTitle("업데이트 알림")
|
|
|
|
|
.setMessage(message.toString())
|
|
|
|
|
.setCancelable(!forceUpdate)
|
|
|
|
|
.setPositiveButton("업데이트", (dialog, which) -> {
|
|
|
|
|
dialog.dismiss();
|
|
|
|
|
startDownload(downloadUrl, versionName);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (forceUpdate) {
|
|
|
|
|
// 강제 업데이트: "나중에" 버튼 없음, 뒤로가기/외부 터치로 닫을 수 없음
|
|
|
|
|
builder.setOnCancelListener(dialog -> activity.finishAffinity());
|
|
|
|
|
} else {
|
|
|
|
|
builder.setNegativeButton("나중에", (dialog, which) -> dialog.dismiss());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
builder.show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* DownloadManager로 APK 다운로드
|
|
|
|
|
*/
|
|
|
|
|
private void startDownload(String downloadUrl, String versionName) {
|
|
|
|
|
try {
|
|
|
|
|
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downloadUrl));
|
|
|
|
|
request.addRequestHeader("X-API-KEY", API_KEY);
|
|
|
|
|
request.setTitle("SAM 업데이트 v" + versionName);
|
|
|
|
|
request.setDescription("앱 업데이트를 다운로드하고 있습니다...");
|
|
|
|
|
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
|
|
|
|
|
|
|
|
|
String fileName = "sam-v" + versionName + ".apk";
|
|
|
|
|
request.setDestinationInExternalFilesDir(activity, Environment.DIRECTORY_DOWNLOADS, fileName);
|
|
|
|
|
|
|
|
|
|
DownloadManager dm = (DownloadManager) activity.getSystemService(Context.DOWNLOAD_SERVICE);
|
|
|
|
|
long downloadId = dm.enqueue(request);
|
|
|
|
|
|
|
|
|
|
// 다운로드 완료 감지
|
|
|
|
|
registerDownloadReceiver(downloadId, fileName);
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Log.e(TAG, "Download start error", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 다운로드 완료 시 설치 화면 표시
|
|
|
|
|
*/
|
|
|
|
|
private void registerDownloadReceiver(long downloadId, String fileName) {
|
|
|
|
|
BroadcastReceiver receiver = new BroadcastReceiver() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onReceive(Context context, Intent intent) {
|
|
|
|
|
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
|
|
|
|
|
if (id != downloadId) return;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
activity.unregisterReceiver(this);
|
|
|
|
|
} catch (Exception ignored) {}
|
|
|
|
|
|
|
|
|
|
installApk(fileName);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
|
|
|
activity.registerReceiver(receiver,
|
|
|
|
|
new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
|
|
|
|
|
Context.RECEIVER_NOT_EXPORTED);
|
|
|
|
|
} else {
|
|
|
|
|
activity.registerReceiver(receiver,
|
|
|
|
|
new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* APK 설치 Intent 실행
|
|
|
|
|
*/
|
|
|
|
|
private void installApk(String fileName) {
|
|
|
|
|
try {
|
|
|
|
|
File apkFile = new File(activity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), fileName);
|
|
|
|
|
if (!apkFile.exists()) {
|
|
|
|
|
Log.e(TAG, "APK file not found: " + apkFile.getAbsolutePath());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Uri apkUri = FileProvider.getUriForFile(
|
|
|
|
|
activity,
|
|
|
|
|
activity.getPackageName() + ".fileprovider",
|
|
|
|
|
apkFile
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Intent installIntent = new Intent(Intent.ACTION_VIEW);
|
|
|
|
|
installIntent.setDataAndType(apkUri, "application/vnd.android.package-archive");
|
|
|
|
|
installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
|
|
|
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
|
|
|
|
|
|
|
activity.startActivity(installIntent);
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Log.e(TAG, "Install APK error", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 현재 앱 버전 코드
|
|
|
|
|
*/
|
|
|
|
|
private int getCurrentVersionCode() {
|
|
|
|
|
try {
|
|
|
|
|
PackageInfo pInfo = activity.getPackageManager()
|
|
|
|
|
.getPackageInfo(activity.getPackageName(), 0);
|
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
|
|
|
return (int) pInfo.getLongVersionCode();
|
|
|
|
|
} else {
|
|
|
|
|
return pInfo.versionCode;
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Log.e(TAG, "Get version code error", e);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|