feat(MOB): 앱 설치 권한 없을 시 설정 화면 자동 안내

- installApk에 canRequestPackageInstalls() 권한 체크 추가
- 권한 없으면 안내 다이얼로그 → 설정 화면으로 이동
- onActivityResult로 설정 복귀 후 자동 설치 재시도

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-30 22:15:31 +09:00
parent e0bb074cea
commit 311325d7de
2 changed files with 90 additions and 3 deletions

View File

@@ -8,9 +8,11 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.Settings;
import android.util.Log;
import androidx.core.content.FileProvider;
@@ -33,6 +35,7 @@ public class AppUpdateChecker {
private final Activity activity;
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private String pendingInstallFileName;
public AppUpdateChecker(Activity activity) {
this.activity = activity;
@@ -178,9 +181,81 @@ public class AppUpdateChecker {
}
/**
* APK 설치 Intent 실행
* APK 설치 Intent 실행 (권한 체크 포함)
*/
private void installApk(String fileName) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (!activity.getPackageManager().canRequestPackageInstalls()) {
// 권한 없음 → 설정 화면 안내
pendingInstallFileName = fileName;
activity.runOnUiThread(() -> showInstallPermissionDialog());
return;
}
}
launchInstallIntent(fileName);
} catch (Exception e) {
Log.e(TAG, "Install APK error", e);
}
}
/**
* 출처를 알 수 없는 앱 설치 권한 안내 다이얼로그
*/
private void showInstallPermissionDialog() {
if (activity.isFinishing() || activity.isDestroyed()) return;
new AlertDialog.Builder(activity)
.setTitle("설치 권한 필요")
.setMessage("앱 업데이트를 설치하려면 '출처를 알 수 없는 앱 설치' 권한을 허용해주세요.\n\n설정 화면으로 이동합니다.")
.setCancelable(false)
.setPositiveButton("설정으로 이동", (dialog, which) -> {
dialog.dismiss();
openInstallPermissionSettings();
})
.setNegativeButton("취소", (dialog, which) -> {
dialog.dismiss();
pendingInstallFileName = null;
})
.show();
}
/**
* 출처를 알 수 없는 앱 설치 설정 화면 열기
*/
private void openInstallPermissionSettings() {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
intent.setData(Uri.parse("package:" + activity.getPackageName()));
activity.startActivityForResult(intent, 1001);
}
} catch (Exception e) {
Log.e(TAG, "Open install permission settings error", e);
}
}
/**
* 설정 화면에서 돌아온 후 설치 재시도
*/
public void onActivityResult(int requestCode, int resultCode) {
if (requestCode == 1001 && pendingInstallFileName != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
&& activity.getPackageManager().canRequestPackageInstalls()) {
launchInstallIntent(pendingInstallFileName);
} else {
Log.w(TAG, "Install permission still not granted");
}
pendingInstallFileName = null;
}
}
/**
* 실제 APK 설치 Intent 실행
*/
private void launchInstallIntent(String fileName) {
try {
File apkFile = new File(activity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), fileName);
if (!apkFile.exists()) {
@@ -202,7 +277,7 @@ public class AppUpdateChecker {
activity.startActivity(installIntent);
} catch (Exception e) {
Log.e(TAG, "Install APK error", e);
Log.e(TAG, "Launch install intent error", e);
}
}

View File

@@ -6,12 +6,15 @@ import android.content.Context;
import android.media.AudioAttributes;
import android.net.Uri;
import android.os.Build;
import android.content.Intent;
import android.os.Bundle;
import com.getcapacitor.BridgeActivity;
public class MainActivity extends BridgeActivity {
private AppUpdateChecker updateChecker;
// 채널 ID (FCM payload의 android.notification.channel_id 로 사용할 값)
public static final String CHANNEL_DEFAULT = "push_default";
public static final String CHANNEL_VENDOR_REGISTER = "push_vendor_register";
@@ -27,7 +30,8 @@ public class MainActivity extends BridgeActivity {
createNotificationChannels();
// 인앱 업데이트 체크
new AppUpdateChecker(this).checkForUpdate();
updateChecker = new AppUpdateChecker(this);
updateChecker.checkForUpdate();
// WebView 줌 설정 (핀치 줌 활성화)
getBridge().getWebView().getSettings().setSupportZoom(true);
@@ -137,4 +141,12 @@ public class MainActivity extends BridgeActivity {
nm.createNotificationChannel(chPurchaseOrder);
nm.createNotificationChannel(chContract);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (updateChecker != null) {
updateChecker.onActivityResult(requestCode, resultCode);
}
}
}