package com.example.shiftalarm import android.app.Activity import android.app.ProgressDialog import android.content.Context import android.content.Intent import android.net.Uri import android.os.Build import android.widget.Toast import androidx.appcompat.app.AlertDialog import androidx.core.content.FileProvider import org.json.JSONObject import java.io.BufferedInputStream import java.io.File import java.io.FileOutputStream import java.net.HttpURLConnection import java.net.URL object AppUpdateManager { private const val VERSION_URL = "https://git.webpluss.net/sanjeok77/ShiftRing/raw/branch/main/version.json" fun checkUpdate(activity: Activity, silent: Boolean = false) { val ctx = activity.applicationContext val versionCheckUrl = "$VERSION_URL?t=${System.currentTimeMillis()}" Thread { try { val url = URL(versionCheckUrl) val connection = url.openConnection() as HttpURLConnection connection.connectTimeout = 5000 connection.readTimeout = 5000 connection.requestMethod = "GET" connection.useCaches = false if (connection.responseCode == 200) { val reader = connection.inputStream.bufferedReader() val result = reader.readText() reader.close() val json = JSONObject(result) val serverVersionCode = json.getInt("versionCode") val serverVersionName = json.getString("versionName") val apkUrl = json.getString("apkUrl") val changelog = json.optString("changelog", "버그 수정 및 성능 향상") val pInfo = ctx.packageManager.getPackageInfo(ctx.packageName, 0) val currentVersionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { pInfo.longVersionCode.toInt() } else { @Suppress("DEPRECATION") pInfo.versionCode } val currentVersionName = pInfo.versionName ?: "0.0.0" if (serverVersionCode > currentVersionCode) { activity.runOnUiThread { showUpdateDialog(activity, serverVersionName, changelog, apkUrl) } } else if (!silent) { activity.runOnUiThread { Toast.makeText(ctx, "현재 최신 버전을 사용 중입니다. ($currentVersionName)", Toast.LENGTH_SHORT).show() } } } else if (!silent) { activity.runOnUiThread { Toast.makeText(ctx, "서버 연결 실패", Toast.LENGTH_SHORT).show() } } } catch (e: Exception) { e.printStackTrace() if (!silent) { activity.runOnUiThread { Toast.makeText(ctx, "업데이트 확인 중 오류 발생", Toast.LENGTH_SHORT).show() } } } }.start() } private fun showUpdateDialog(activity: Activity, version: String, changelog: String, apkUrl: String) { com.google.android.material.dialog.MaterialAlertDialogBuilder(activity) .setTitle("새로운 업데이트 발견 (v$version)") .setMessage("업데이트 내용:\n$changelog\n\n지금 다운로드하시겠습니까?") .setPositiveButton("다운로드") { _, _ -> downloadAndInstallApk(activity, apkUrl, version) } .setNegativeButton("나중에", null) .show() } private fun downloadAndInstallApk(activity: Activity, apkUrl: String, version: String) { val progressDialog = ProgressDialog(activity).apply { setTitle("업데이트 다운로드 중") setMessage("v$version 다운로드 중...") setProgressStyle(ProgressDialog.STYLE_HORIZONTAL) setCancelable(false) max = 100 show() } Thread { try { val url = URL(apkUrl) val connection = url.openConnection() as HttpURLConnection connection.connectTimeout = 15000 connection.readTimeout = 15000 connection.requestMethod = "GET" connection.connect() val fileLength = connection.contentLength val inputStream = BufferedInputStream(connection.inputStream) val apkFile = File(activity.cacheDir, "update.apk") val outputStream = FileOutputStream(apkFile) val buffer = ByteArray(8192) var total: Long = 0 var count: Int while (inputStream.read(buffer).also { count = it } != -1) { total += count outputStream.write(buffer, 0, count) if (fileLength > 0) { val progress = (total * 100 / fileLength).toInt() activity.runOnUiThread { progressDialog.progress = progress } } } outputStream.flush() outputStream.close() inputStream.close() connection.disconnect() activity.runOnUiThread { progressDialog.dismiss() installApk(activity, apkFile) } } catch (e: Exception) { e.printStackTrace() activity.runOnUiThread { progressDialog.dismiss() Toast.makeText(activity, "다운로드 실패: ${e.message}", Toast.LENGTH_LONG).show() } } }.start() } private fun installApk(activity: Activity, apkFile: File) { try { val apkUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { FileProvider.getUriForFile(activity, "${activity.packageName}.provider", apkFile) } else { Uri.fromFile(apkFile) } val intent = Intent(Intent.ACTION_VIEW).apply { setDataAndType(apkUri, "application/vnd.android.package-archive") flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION } activity.startActivity(intent) } catch (e: Exception) { e.printStackTrace() Toast.makeText(activity, "설치 실패: ${e.message}", Toast.LENGTH_LONG).show() } } }