Skip to content

Commit be961a9

Browse files
committed
[atomic_x]Update atomic_x 4.0.2
1 parent 3717794 commit be961a9

41 files changed

Lines changed: 3557 additions & 931 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

atomic-x/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 4.0.2
2+
* Optimized input box styling and voice recording animations.
3+
* Enhanced the visual design of read receipts.
4+
* Image messages now support pinch-to-zoom gestures.
5+
* Resolved compatibility issues with Huawei Nova phones when the default system language is set to Traditional Chinese, including permission dialog displays.
6+
17
## 4.0.1
28
* Added floating button features to the chat page: "Jump to Latest Position," "X Unread Messages," and "Someone @-ed Me".
39
* Text message links now support opening in a browser.

atomic-x/android/src/main/kotlin/io/trtc/tuikit/atomicx/permission/PermissionHandler.kt

Lines changed: 68 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package io.trtc.tuikit.atomicx.permission
22

33
import android.Manifest
44
import android.app.Activity
5+
import android.content.Context
56
import android.content.Intent
67
import android.content.pm.PackageManager
78
import android.net.Uri
@@ -24,6 +25,8 @@ class PermissionHandler(
2425
companion object {
2526
private const val PERMISSION_REQUEST_CODE = 9527
2627
private const val OVERLAY_PERMISSION_REQUEST_CODE = 9528
28+
private const val PREFS_NAME = "atomic_x_permissions"
29+
private const val KEY_EVER_REQUESTED_PREFIX = "ever_requested_"
2730

2831
// Permission identifiers from Dart
2932
private const val PERMISSION_CAMERA = "camera"
@@ -38,9 +41,39 @@ class PermissionHandler(
3841
private var activity: Activity? = null
3942
private var pendingResult: MethodChannel.Result? = null
4043
private var requestedPermissionTypes: List<String>? = null
41-
private var requestedAndroidPermissions: List<String>? = null
4244
private var pendingOverlayPermissionTypes: List<String>? = null
4345

46+
private val prefs by lazy {
47+
pluginBinding.applicationContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
48+
}
49+
50+
/**
51+
* Check if a permission type has ever been requested via [requestPermissions].
52+
* Used by [checkPermissionStatus] to avoid false-positive
53+
* `permanentlyDenied` on devices (e.g. Huawei) where
54+
* `shouldShowRequestPermissionRationale` returns `false` even before
55+
* the permission has ever been requested.
56+
*/
57+
private fun hasEverBeenRequested(permissionType: String): Boolean {
58+
return prefs.getBoolean("$KEY_EVER_REQUESTED_PREFIX$permissionType", false)
59+
}
60+
61+
/**
62+
* Mark permission types as having been requested at least once.
63+
*
64+
* Called in [onRequestPermissionsResult] — i.e. only after the system
65+
* permission dialog has returned a result and the user has made a choice.
66+
* This ensures that if the user kills the app before interacting with
67+
* the dialog, the permission is NOT marked as "ever requested".
68+
*/
69+
private fun markAsRequested(permissionTypes: List<String>) {
70+
val editor = prefs.edit()
71+
for (type in permissionTypes) {
72+
editor.putBoolean("$KEY_EVER_REQUESTED_PREFIX$type", true)
73+
}
74+
editor.apply()
75+
}
76+
4477
fun setActivity(activity: Activity?) {
4578
this.activity = activity
4679
}
@@ -139,17 +172,13 @@ class PermissionHandler(
139172
}
140173

141174
/**
142-
* Core logic for resolving a permission type's status.
175+
* Core logic for resolving a permission type's raw status.
143176
*
144-
* @param permissionType The permission type identifier from Dart.
145-
* @param checkRequestedPermissions When `true`, only reports `permanentlyDenied`
146-
* for permissions tracked in [requestedAndroidPermissions] (used after a
147-
* request flow to avoid false positives). When `false`, reports
148-
* `permanentlyDenied` purely based on `!isGranted && !shouldShowRationale`
149-
* (used for standalone check calls; the Dart layer cross-validates with a
150-
* subsequent request() to filter out false positives).
177+
* Reports `permanentlyDenied` purely based on `!isGranted && !shouldShowRationale`.
178+
* Callers (e.g. [checkPermissionStatus]) are responsible for filtering out
179+
* false positives via [hasEverBeenRequested].
151180
*/
152-
private fun resolvePermissionStatus(permissionType: String, checkRequestedPermissions: Boolean): String {
181+
private fun resolvePermissionStatus(permissionType: String): String {
153182
// Handle overlay permissions separately
154183
if (isOverlayPermission(permissionType)) {
155184
return if (canDrawOverlays()) "granted" else "denied"
@@ -184,13 +213,11 @@ class PermissionHandler(
184213
return "denied"
185214
}
186215

187-
// Check if any permission is permanently denied.
188-
// When checkRequestedPermissions is true, only report permanentlyDenied for
189-
// permissions that have been requested in the current session.
216+
// Check if any permission is permanently denied
190217
val anyPermanentlyDenied = androidPermissions.any { permission ->
191218
val isGranted = ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
192219
val shouldShow = ActivityCompat.shouldShowRequestPermissionRationale(currentActivity, permission)
193-
!isGranted && !shouldShow && (!checkRequestedPermissions || requestedAndroidPermissions?.contains(permission) == true)
220+
!isGranted && !shouldShow
194221
}
195222

196223
if (anyPermanentlyDenied) {
@@ -200,34 +227,6 @@ class PermissionHandler(
200227
return "denied"
201228
}
202229

203-
/**
204-
* Get the status of a permission type after a requestPermissions flow.
205-
*
206-
* Uses [requestedAndroidPermissions] to avoid false positives:
207-
* `shouldShowRequestPermissionRationale` returns false for both "never asked"
208-
* and "permanently denied". By checking against `requestedAndroidPermissions`,
209-
* we only report `permanentlyDenied` for permissions that have actually been
210-
* requested in the current session.
211-
*/
212-
private fun getStatusAfterRequest(permissionType: String): String {
213-
return resolvePermissionStatus(permissionType, checkRequestedPermissions = true)
214-
}
215-
216-
/**
217-
* Check the status of a permission type (used for standalone check calls).
218-
*
219-
* Unlike [getPermissionTypeStatus], this method does NOT rely on
220-
* [requestedAndroidPermissions]. It reports `permanentlyDenied` purely based
221-
* on `!isGranted && !shouldShowRationale`, which may produce false positives
222-
* when the permission has never been requested.
223-
*
224-
* The Dart layer uses this to get a preliminary signal, then cross-validates
225-
* with a subsequent `request()` call to filter out false positives.
226-
*/
227-
private fun checkPermissionStatusInternal(permissionType: String): String {
228-
return resolvePermissionStatus(permissionType, checkRequestedPermissions = false)
229-
}
230-
231230
fun requestPermissions(permissionTypes: List<String>, result: MethodChannel.Result) {
232231
val currentActivity = activity
233232
if (currentActivity == null) {
@@ -259,7 +258,6 @@ class PermissionHandler(
259258
}
260259

261260
requestedPermissionTypes = regularPermissions
262-
requestedAndroidPermissions = androidPermissions
263261
pendingResult = result
264262

265263
if (androidPermissions.isNotEmpty()) {
@@ -342,7 +340,7 @@ class PermissionHandler(
342340

343341
// Add regular permissions status
344342
for (permissionType in regularPermissions) {
345-
resultMap[permissionType] = getStatusAfterRequest(permissionType)
343+
resultMap[permissionType] = checkPermissionStatus(permissionType)
346344
}
347345

348346
// Add overlay permissions status
@@ -368,8 +366,26 @@ class PermissionHandler(
368366
}
369367
}
370368

369+
/**
370+
* Check the status of a permission type (used for standalone check calls).
371+
*
372+
* Only reports `permanentlyDenied` when the permission has been requested
373+
* at least once before (persisted via SharedPreferences). This avoids
374+
* false positives on devices like Huawei where
375+
* `shouldShowRequestPermissionRationale` returns `false` even for
376+
* never-requested permissions.
377+
*
378+
* The Dart layer uses this to get a preliminary signal, then cross-validates
379+
* with a subsequent `request()` call to filter out false positives.
380+
*/
371381
fun checkPermissionStatus(permissionType: String): String {
372-
return checkPermissionStatusInternal(permissionType)
382+
val status = resolvePermissionStatus(permissionType)
383+
// If resolve says permanentlyDenied but the permission has never been
384+
// requested, it is a false positive — report as denied instead.
385+
if (status == "permanentlyDenied" && !hasEverBeenRequested(permissionType)) {
386+
return "denied"
387+
}
388+
return status
373389
}
374390

375391
override fun onRequestPermissionsResult(
@@ -391,14 +407,20 @@ class PermissionHandler(
391407
return true
392408
}
393409

410+
// The system dialog has returned a result — the user made a choice.
411+
// Persist that these permission types have been requested at least once,
412+
// so that future checkPermissionStatus() calls can accurately
413+
// distinguish "never asked" from "permanently denied".
414+
markAsRequested(permissionTypes)
415+
394416
// If we have pending overlay permissions, request them now
395417
if (!overlayPermissions.isNullOrEmpty()) {
396418
requestOverlayPermissionAfterRegular(permissionTypes, overlayPermissions, result)
397419
return true
398420
}
399421

400422
// Build result map based on permission types
401-
val resultMap = permissionTypes.associateWith { getStatusAfterRequest(it) }
423+
val resultMap = permissionTypes.associateWith { checkPermissionStatus(it) }
402424

403425
result.success(resultMap)
404426
clearPendingRequest()
@@ -457,7 +479,6 @@ class PermissionHandler(
457479
private fun clearPendingRequest() {
458480
pendingResult = null
459481
requestedPermissionTypes = null
460-
requestedAndroidPermissions = null
461482
pendingOverlayPermissionTypes = null
462483
}
463484
}

atomic-x/chat_assets/icon/add.svg

Lines changed: 6 additions & 3 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 9 additions & 8 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading
Lines changed: 2 additions & 2 deletions
Loading

atomic-x/chat_assets/icon/info.svg

Lines changed: 0 additions & 5 deletions
This file was deleted.
Lines changed: 8 additions & 0 deletions
Loading
File renamed without changes.

0 commit comments

Comments
 (0)