Skip to content

Commit 05c40ea

Browse files
authored
Merge pull request #20 from wxxsfxyzm/notification-fixl, closes #19
Notification fix, display reason on fail, closes #19
2 parents 1e29201 + 223b6a8 commit 05c40ea

File tree

7 files changed

+133
-46
lines changed

7 files changed

+133
-46
lines changed

app/src/main/java/com/rosan/installer/data/app/model/impl/installer/IBinderInstallerRepoImpl.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ abstract class IBinderInstallerRepoImpl : InstallerRepo, KoinComponent {
239239
) {
240240
fun special() = null
241241
val authorizer = config.authorizer
242-
Log.d("useUserservice", "calling useUserService...")
242+
Log.d("useUserService", "calling useUserService...")
243243
useUserService(
244244
config, if (authorizer == ConfigEntity.Authorizer.None
245245
|| authorizer == ConfigEntity.Authorizer.Dhizuku

app/src/main/java/com/rosan/installer/data/installer/model/impl/installer/BroadcastHandler.kt

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import android.content.BroadcastReceiver
44
import android.content.Context
55
import android.content.Intent
66
import android.content.IntentFilter
7-
import android.os.Build
8-
import androidx.annotation.RequiresApi
7+
import androidx.core.content.ContextCompat
98
import com.rosan.installer.data.installer.repo.InstallerRepo
109
import com.rosan.installer.data.installer.util.pendingActivity
1110
import com.rosan.installer.data.installer.util.pendingBroadcast
@@ -37,7 +36,8 @@ class BroadcastHandler(scope: CoroutineScope, installer: InstallerRepo) :
3736
.pendingActivity(context, getRequestCode(installer, Name.Launch))
3837

3938
fun namedIntent(context: Context, installer: InstallerRepo, name: Name) =
40-
Intent(ACTION).putExtra(KEY_ID, installer.id)
39+
Intent(ACTION).setPackage(context.packageName)
40+
.putExtra(KEY_ID, installer.id)
4141
.putExtra(KEY_NAME, name.value)
4242
.pendingBroadcast(context, getRequestCode(installer, name))
4343
}
@@ -51,18 +51,24 @@ class BroadcastHandler(scope: CoroutineScope, installer: InstallerRepo) :
5151
}
5252

5353
private fun registerReceiver(receiver: Receiver) {
54-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) registerReceiverTiramisu(receiver)
55-
else context.registerReceiver(receiver, IntentFilter(ACTION))
54+
// ContextCompat 会自动处理 API 版本的差异
55+
ContextCompat.registerReceiver(
56+
context,
57+
receiver,
58+
IntentFilter(ACTION),
59+
ContextCompat.RECEIVER_NOT_EXPORTED
60+
)
5661
}
5762

58-
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
63+
// No longer needed as ContextCompat handles it
64+
/*@RequiresApi(Build.VERSION_CODES.TIRAMISU)
5965
private fun registerReceiverTiramisu(receiver: Receiver) {
6066
context.registerReceiver(
6167
receiver,
6268
IntentFilter(ACTION),
6369
Context.RECEIVER_NOT_EXPORTED
6470
)
65-
}
71+
}*/
6672

6773
override suspend fun onFinish() {
6874
context.unregisterReceiver(receiver)
@@ -97,7 +103,7 @@ class BroadcastHandler(scope: CoroutineScope, installer: InstallerRepo) :
97103
Launch("launch");
98104

99105
companion object {
100-
fun revert(value: String): Name = Name.values().first { it.value == value }
106+
fun revert(value: String): Name = entries.first { it.value == value }
101107
}
102108
}
103109
}

app/src/main/java/com/rosan/installer/data/installer/model/impl/installer/ForegroundInfoHandler.kt

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
package com.rosan.installer.data.installer.model.impl.installer
22

3+
import android.Manifest
34
import android.app.Notification
45
import android.content.Context
6+
import android.content.pm.PackageManager
7+
import android.os.Build
8+
import android.widget.Toast
59
import androidx.annotation.DrawableRes
610
import androidx.annotation.StringRes
711
import androidx.core.app.NotificationChannelCompat
812
import androidx.core.app.NotificationCompat
913
import androidx.core.app.NotificationManagerCompat
14+
import androidx.core.content.ContextCompat
1015
import androidx.core.graphics.drawable.toBitmapOrNull
1116
import com.rosan.installer.R
1217
import com.rosan.installer.data.app.util.getInfo
1318
import com.rosan.installer.data.installer.model.entity.ProgressEntity
1419
import com.rosan.installer.data.installer.repo.InstallerRepo
20+
import com.rosan.installer.util.getErrorMessage
1521
import kotlinx.coroutines.CoroutineScope
1622
import kotlinx.coroutines.Job
1723
import kotlinx.coroutines.launch
@@ -148,6 +154,28 @@ class ForegroundInfoHandler(scope: CoroutineScope, installer: InstallerRepo) :
148154
notificationManager.cancel(notificationId)
149155
return
150156
}
157+
// 检查版本是否为 Android 13 (Tiramisu) 或更高
158+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
159+
// 检查 POST_NOTIFICATIONS 权限是否已被授予
160+
if (ContextCompat.checkSelfPermission(
161+
context,
162+
Manifest.permission.POST_NOTIFICATIONS
163+
) != PackageManager.PERMISSION_GRANTED
164+
) {
165+
// 如果权限未被授予
166+
// 在这里不应该调用 notify()
167+
// TODO 应该在这里引导用户去开启权限
168+
// 显示一条 Toast 提示用户开启通知权限
169+
Toast.makeText(
170+
context,
171+
getString(R.string.enable_notification_hint),
172+
Toast.LENGTH_SHORT
173+
).show()
174+
return
175+
}
176+
}
177+
178+
// 如果权限已被授予,或者系统版本低于 Android 13,则正常显示通知
151179
notificationManager.notify(notificationId, notification)
152180
}
153181

@@ -210,10 +238,22 @@ class ForegroundInfoHandler(scope: CoroutineScope, installer: InstallerRepo) :
210238
.addAction(0, getString(R.string.cancel), finishIntent).build()
211239
}
212240

213-
private fun onInstallFailed(builder: NotificationCompat.Builder): Notification {
241+
private fun onInstallFailed(
242+
builder: NotificationCompat.Builder
243+
): Notification {
214244
val info = installer.entities.filter { it.selected }.map { it.app }.getInfo(context)
245+
val reason = installer.error.getErrorMessage(context)
246+
val contentText = getString(R.string.installer_install_failed)
247+
val bigTextStyle = NotificationCompat.BigTextStyle()
248+
.setBigContentTitle(info.title)
249+
.bigText(
250+
"$contentText\n" +
251+
getString(R.string.installer_error_reason) +
252+
": $reason"
253+
)
215254
return builder.setContentTitle(info.title)
216-
.setContentText(getString(R.string.installer_install_failed))
255+
.setContentText(contentText)
256+
.setStyle(bigTextStyle)
217257
.setLargeIcon(info.icon?.toBitmapOrNull())
218258
.addAction(0, getString(R.string.retry), installIntent)
219259
.addAction(0, getString(R.string.cancel), finishIntent).build()
Lines changed: 65 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.rosan.installer.util
22

3+
import android.content.Context
34
import androidx.compose.runtime.Composable
45
import androidx.compose.ui.res.stringResource
56
import com.rosan.installer.R
@@ -34,40 +35,71 @@ import com.rosan.installer.data.recycle.model.exception.DhizukuNotWorkException
3435
import com.rosan.installer.data.recycle.model.exception.RootNotWorkException
3536
import com.rosan.installer.data.recycle.model.exception.ShizukuNotWorkException
3637

38+
/**
39+
* [公共实现]
40+
*
41+
* 一个私有的辅助函数,它作为唯一的真实来源,
42+
* 负责将一个 Throwable 映射到其对应的字符串资源 ID。
43+
*
44+
* @return R.string 的资源 ID。
45+
*/
46+
private fun Throwable.getStringResourceId(): Int {
47+
return when (this) {
48+
is InstallFailedAlreadyExistsException -> R.string.exception_install_failed_already_exists
49+
is InstallFailedInvalidAPKException -> R.string.exception_install_failed_invalid_apk
50+
is InstallFailedInvalidURIException -> R.string.exception_install_failed_invalid_uri
51+
is InstallFailedInsufficientStorageException -> R.string.exception_install_failed_insufficient_storage
52+
is InstallFailedDuplicatePackageException -> R.string.exception_install_failed_duplicate_package
53+
is InstallFailedNoSharedUserException -> R.string.exception_install_failed_no_shared_user
54+
is InstallFailedUpdateIncompatibleException -> R.string.exception_install_failed_update_incompatible
55+
is InstallFailedSharedUserIncompatibleException -> R.string.exception_install_failed_shared_user_incompatible
56+
is InstallFailedMissingSharedLibraryException -> R.string.exception_install_failed_missing_shared_library
57+
is InstallFailedReplaceCouldntDeleteException -> R.string.exception_install_failed_replace_couldnt_delete
58+
is InstallFailedDexOptException -> R.string.exception_install_failed_dexopt
59+
is InstallFailedOlderSdkException -> R.string.exception_install_failed_older_sdk
60+
is InstallFailedConflictingProviderException -> R.string.exception_install_failed_conflicting_provider
61+
is InstallFailedNewerSDKException -> R.string.exception_install_failed_newer_sdk
62+
is InstallFailedTestOnlyException -> R.string.exception_install_failed_test_only
63+
is InstallFailedCpuAbiIncompatibleException -> R.string.exception_install_failed_cpu_abi_incompatible
64+
is InstallFailedMissingFeatureException -> R.string.exception_install_failed_missing_feature
65+
is InstallFailedContainerErrorException -> R.string.exception_install_failed_container_error
66+
is InstallFailedInvalidInstallLocationException -> R.string.exception_install_failed_invalid_install_location
67+
is InstallFailedMediaUnavailableException -> R.string.exception_install_failed_media_unavailable
68+
is InstallFailedVerificationTimeoutException -> R.string.exception_install_failed_verification_timeout
69+
is InstallFailedVerificationFailureException -> R.string.exception_install_failed_verification_failure
70+
is InstallFailedPackageChangedException -> R.string.exception_install_failed_package_changed
71+
is InstallFailedUidChangedException -> R.string.exception_install_failed_uid_changed
72+
is InstallFailedVersionDowngradeException -> R.string.exception_install_failed_version_downgrade
73+
is InstallFailedRejectedByBuildTypeException -> R.string.exception_install_failed_rejected_by_build_type
74+
is ShizukuNotWorkException -> R.string.exception_shizuku_not_work
75+
is DhizukuNotWorkException -> R.string.exception_dhizuku_not_work
76+
is RootNotWorkException -> R.string.exception_root_not_work
77+
is AppProcessNotWorkException -> R.string.exception_app_process_not_work
78+
else -> R.string.exception_install_failed_unknown
79+
}
80+
}
81+
82+
/**
83+
* [公开API - Composable]
84+
*
85+
* 用于在 Jetpack Compose UI 中获取用户友好的错误信息。
86+
*/
3787
@Composable
3888
fun Throwable.help(): String {
39-
return when (this) {
40-
is InstallFailedAlreadyExistsException -> stringResource(R.string.exception_install_failed_already_exists)
41-
is InstallFailedInvalidAPKException -> stringResource(R.string.exception_install_failed_invalid_apk)
42-
is InstallFailedInvalidURIException -> stringResource(R.string.exception_install_failed_invalid_uri)
43-
is InstallFailedInsufficientStorageException -> stringResource(R.string.exception_install_failed_insufficient_storage)
44-
is InstallFailedDuplicatePackageException -> stringResource(R.string.exception_install_failed_duplicate_package)
45-
is InstallFailedNoSharedUserException -> stringResource(R.string.exception_install_failed_no_shared_user)
46-
is InstallFailedUpdateIncompatibleException -> stringResource(R.string.exception_install_failed_update_incompatible)
47-
is InstallFailedSharedUserIncompatibleException -> stringResource(R.string.exception_install_failed_shared_user_incompatible)
48-
is InstallFailedMissingSharedLibraryException -> stringResource(R.string.exception_install_failed_missing_shared_library)
49-
is InstallFailedReplaceCouldntDeleteException -> stringResource(R.string.exception_install_failed_replace_couldnt_delete)
50-
is InstallFailedDexOptException -> stringResource(R.string.exception_install_failed_dexopt)
51-
is InstallFailedOlderSdkException -> stringResource(R.string.exception_install_failed_older_sdk)
52-
is InstallFailedConflictingProviderException -> stringResource(R.string.exception_install_failed_conflicting_provider)
53-
is InstallFailedNewerSDKException -> stringResource(R.string.exception_install_failed_newer_sdk)
54-
is InstallFailedTestOnlyException -> stringResource(R.string.exception_install_failed_test_only)
55-
is InstallFailedCpuAbiIncompatibleException -> stringResource(R.string.exception_install_failed_cpu_abi_incompatible)
56-
is InstallFailedMissingFeatureException -> stringResource(R.string.exception_install_failed_missing_feature)
57-
is InstallFailedContainerErrorException -> stringResource(R.string.exception_install_failed_container_error)
58-
is InstallFailedInvalidInstallLocationException -> stringResource(R.string.exception_install_failed_invalid_install_location)
59-
is InstallFailedMediaUnavailableException -> stringResource(R.string.exception_install_failed_media_unavailable)
60-
is InstallFailedVerificationTimeoutException -> stringResource(R.string.exception_install_failed_verification_timeout)
61-
is InstallFailedVerificationFailureException -> stringResource(R.string.exception_install_failed_verification_failure)
62-
is InstallFailedPackageChangedException -> stringResource(R.string.exception_install_failed_package_changed)
63-
is InstallFailedUidChangedException -> stringResource(R.string.exception_install_failed_uid_changed)
64-
is InstallFailedVersionDowngradeException -> stringResource(R.string.exception_install_failed_version_downgrade)
65-
is InstallFailedRejectedByBuildTypeException -> stringResource(R.string.exception_install_failed_rejected_by_build_type)
89+
// 1. 调用私有函数获取资源 ID
90+
val resourceId = this.getStringResourceId()
91+
// 2. 使用 Composable 的方式获取字符串
92+
return stringResource(resourceId)
93+
}
6694

67-
is ShizukuNotWorkException -> stringResource(R.string.exception_shizuku_not_work)
68-
is DhizukuNotWorkException -> stringResource(R.string.exception_dhizuku_not_work)
69-
is RootNotWorkException -> stringResource(R.string.exception_root_not_work)
70-
is AppProcessNotWorkException -> stringResource(R.string.exception_app_process_not_work)
71-
else -> stringResource(R.string.exception_install_failed_unknown)
72-
}
95+
/**
96+
* [公开API - Non-Composable]
97+
*
98+
* 用于在 Service, BroadcastReceiver, Handler 等非 Compose 环境中获取用户友好的错误信息。
99+
*/
100+
fun Throwable.getErrorMessage(context: Context): String {
101+
// 1. 调用私有函数获取资源 ID
102+
val resourceId = this.getStringResourceId()
103+
// 2. 使用标准 Context 的方式获取字符串
104+
return context.getString(resourceId)
73105
}

app/src/main/res/values-zh-rCN/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<string name="unstable">测试版</string>
2121

2222

23+
<string name="enable_notification_hint">请启用通知以显示安装进度</string>
2324
<string name="discuss">讨论 &amp; 反馈 &amp; 更新</string>
2425
<string name="qq_channel">QQ频道</string>
2526
<string name="qq_group_official">QQ群组(官方)</string>
@@ -126,6 +127,7 @@
126127
<string name="installer_install_failed">安装失败</string>
127128
<string name="installer_install_success">安装成功</string>
128129

130+
<string name="installer_unknown_error">未知错误</string>
129131
<string name="installer_channel_name">安装通知</string>
130132
<string name="installer_progress_channel_name">安装过程通知</string>
131133
<string name="installer_background_channel_name">后台通知</string>
@@ -172,4 +174,5 @@
172174
<string name="old_version_prefix">当前</string>
173175
<string name="new_version_prefix">新</string>
174176
<string name="install_choice">安装包选择</string>
177+
<string name="installer_error_reason">错误原因</string>
175178
</resources>

app/src/main/res/values-zh-rTW/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<string name="preview">預覽版</string>
2020
<string name="unstable">測試版</string>
2121

22+
<string name="enable_notification_hint">請啟用通知以顯示安裝進度</string>
2223
<string name="discuss">討論 &amp; 回饋 &amp; 更新</string>
2324
<string name="qq_channel">QQ頻道</string>
2425
<string name="qq_group_official">QQ群组(官方)</string>
@@ -125,6 +126,7 @@
125126
<string name="installer_install_failed">安裝失敗</string>
126127
<string name="installer_install_success">安裝成功</string>
127128

129+
<string name="installer_unknown_error">未知錯誤</string>
128130
<string name="installer_channel_name">安裝通知</string>
129131
<string name="installer_progress_channel_name">安裝過程通知</string>
130132
<string name="installer_background_channel_name">後臺通知</string>
@@ -171,4 +173,5 @@
171173
<string name="old_version_prefix">當前</string>
172174
<string name="new_version_prefix">新</string>
173175
<string name="install_choice">安裝包選擇</string>
176+
<string name="installer_error_reason">錯誤原因</string>
174177
</resources>

app/src/main/res/values/strings.xml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<string name="preview">Preview</string>
2222
<string name="unstable">Unstable</string>
2323

24+
<string name="enable_notification_hint">Please enable Notification Permission.</string>
2425

2526
<string name="discuss">Chat &amp; Feedback &amp; Update</string>
2627
<string name="qq_channel">QQ channel</string>
@@ -127,6 +128,8 @@
127128
<string name="installer_installing">Installing…</string>
128129
<string name="installer_install_failed">Install failed</string>
129130
<string name="installer_install_success">Install complete</string>
131+
<string name="installer_error_reason">Error Reason</string>
132+
<string name="installer_unknown_error">Unknown Error</string>
130133

131134
<string name="installer_channel_name">Install Notification</string>
132135
<string name="installer_progress_channel_name">Install Process Notification</string>
@@ -137,8 +140,8 @@
137140
<string name="installer_version">Version: %s (%s)</string>
138141
<string name="installer_version2">%s (%s)</string>
139142
<string name="installer_package_name">Package: %s</string>
140-
<string name="installer_package_min_sdk" translatable="false">MinSdk: %s</string>
141-
<string name="installer_package_target_sdk" translatable="false">TargetSdk: %s</string>
143+
<string name="installer_package_min_sdk" translatable="false">MinSDK: %s</string>
144+
<string name="installer_package_target_sdk" translatable="false">TargetSDK: %s</string>
142145
<string name="installer_file_path">Path: %s</string>
143146

144147
<string name="exception_install_failed_unknown">Unknown error</string>

0 commit comments

Comments
 (0)