Manage new Android 13 notification permission.
This commit is contained in:
parent
3952f732dd
commit
652069d520
|
@ -635,6 +635,8 @@
|
|||
<string name="permissions_rationale_msg_record_audio">${app_name} needs permission to access your microphone to perform audio calls.</string>
|
||||
<!-- Note to translators: the translation MUST contain the string "${app_name}", which will be replaced by the application name -->
|
||||
<string name="permissions_rationale_msg_camera_and_audio">${app_name} needs permission to access your camera and your microphone to perform video calls.\n\nPlease allow access on the next pop-ups to be able to make the call.</string>
|
||||
<!-- Note to translators: the translation MUST contain the string "${app_name}", which will be replaced by the application name -->
|
||||
<string name="permissions_rationale_msg_notification">${app_name} needs permission to display notifications. Notifications can display your messages, your invitations, etc.\n\nPlease allow access on the next pop-ups to be able to view notification.</string>
|
||||
|
||||
<string name="permissions_denied_qr_code">To scan a QR code, you need to allow camera access.</string>
|
||||
<string name="permissions_denied_add_contact">Allow permission to access your contacts.</string>
|
||||
|
@ -854,7 +856,9 @@
|
|||
<string name="settings_troubleshoot_test_system_settings_title">System Settings.</string>
|
||||
<string name="settings_troubleshoot_test_system_settings_success">Notifications are enabled in the system settings.</string>
|
||||
<string name="settings_troubleshoot_test_system_settings_failed">Notifications are disabled in the system settings.\nPlease check system settings.</string>
|
||||
<string name="settings_troubleshoot_test_system_settings_permission_failed">${app_name} needs the permission to show notifications.\nPlease grant the permission.</string>
|
||||
<string name="open_settings">Open Settings</string>
|
||||
<string name="grant_permission">Grant Permission</string>
|
||||
|
||||
<string name="settings_troubleshoot_test_account_settings_title">Account Settings.</string>
|
||||
<string name="settings_troubleshoot_test_account_settings_success">Notifications are enabled for your account.</string>
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
## From https://developer.android.com/develop/ui/views/notifications/notification-permission#test
|
||||
|
||||
PACKAGE_NAME=im.vector.app.debug
|
||||
|
||||
# App is newly installed on a device that runs Android 13 or higher:
|
||||
|
||||
adb shell pm revoke ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS
|
||||
adb shell pm clear-permission-flags ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-set
|
||||
adb shell pm clear-permission-flags ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-fixed
|
||||
|
||||
# The user keeps notifications enabled when the app is installed on a device that runs 12L or lower,
|
||||
# then the device upgrades to Android 13 or higher:
|
||||
|
||||
# adb shell pm grant ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS
|
||||
# adb shell pm set-permission-flags ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-set
|
||||
# adb shell pm clear-permission-flags ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-fixed
|
||||
|
||||
# The user manually disables notifications when the app is installed on a device that runs 12L or lower,
|
||||
# then the device upgrades to Android 13 or higher:
|
||||
|
||||
# adb shell pm revoke ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS
|
||||
# adb shell pm set-permission-flags ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-set
|
||||
# adb shell pm clear-permission-flags ${PACKAGE_NAME} android.permission.POST_NOTIFICATIONS user-fixed
|
|
@ -22,6 +22,7 @@ import android.os.Build
|
|||
import android.widget.Toast
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import im.vector.app.core.platform.VectorBaseActivity
|
||||
import im.vector.app.core.utils.checkPermissions
|
||||
|
@ -46,7 +47,15 @@ class DebugPermissionActivity : VectorBaseActivity<ActivityDebugPermissionBindin
|
|||
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.READ_CONTACTS
|
||||
)
|
||||
) + getAndroid13Permissions()
|
||||
|
||||
private fun getAndroid13Permissions(): List<String> {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
listOf(Manifest.permission.POST_NOTIFICATIONS)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
private var lastPermissions = emptyList<String>()
|
||||
|
||||
|
@ -77,6 +86,14 @@ class DebugPermissionActivity : VectorBaseActivity<ActivityDebugPermissionBindin
|
|||
lastPermissions = listOf(Manifest.permission.READ_CONTACTS)
|
||||
checkPerm()
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
views.notification.setOnClickListener {
|
||||
lastPermissions = listOf(Manifest.permission.POST_NOTIFICATIONS)
|
||||
checkPerm()
|
||||
}
|
||||
} else {
|
||||
views.notification.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkPerm() {
|
||||
|
|
|
@ -30,43 +30,43 @@
|
|||
android:id="@+id/camera"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="CAMERA"
|
||||
android:textAllCaps="false" />
|
||||
android:text="CAMERA" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/audio"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="RECORD_AUDIO"
|
||||
android:textAllCaps="false" />
|
||||
android:text="RECORD_AUDIO" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/camera_audio"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="CAMERA + RECORD_AUDIO"
|
||||
android:textAllCaps="false" />
|
||||
android:text="CAMERA + RECORD_AUDIO" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/write"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="WRITE_EXTERNAL_STORAGE"
|
||||
android:textAllCaps="false" />
|
||||
android:text="WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/read"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="READ_EXTERNAL_STORAGE"
|
||||
android:textAllCaps="false" />
|
||||
android:text="READ_EXTERNAL_STORAGE" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/contact"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="READ_CONTACTS"
|
||||
android:textAllCaps="false" />
|
||||
android:text="READ_CONTACTS" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/notification"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="POST_NOTIFICATIONS" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package im.vector.app.fdroid.features.settings.troubleshoot
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
|
@ -32,7 +30,7 @@ class TestAutoStartBoot @Inject constructor(
|
|||
) :
|
||||
TroubleshootTest(R.string.settings_troubleshoot_test_service_boot_title) {
|
||||
|
||||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
override fun perform(testParameters: TestParameters) {
|
||||
if (vectorPreferences.autoStartOnBoot()) {
|
||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_service_boot_success)
|
||||
status = TestStatus.SUCCESS
|
||||
|
@ -42,7 +40,7 @@ class TestAutoStartBoot @Inject constructor(
|
|||
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_service_boot_quickfix) {
|
||||
override fun doFix() {
|
||||
vectorPreferences.setAutoStartOnBoot(true)
|
||||
manager?.retry(activityResultLauncher)
|
||||
manager?.retry(testParameters)
|
||||
}
|
||||
}
|
||||
status = TestStatus.FAILED
|
||||
|
|
|
@ -15,9 +15,7 @@
|
|||
*/
|
||||
package im.vector.app.fdroid.features.settings.troubleshoot
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.ConnectivityManager
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.net.ConnectivityManagerCompat
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
|
@ -32,7 +30,7 @@ class TestBackgroundRestrictions @Inject constructor(
|
|||
) :
|
||||
TroubleshootTest(R.string.settings_troubleshoot_test_bg_restricted_title) {
|
||||
|
||||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
override fun perform(testParameters: TestParameters) {
|
||||
context.getSystemService<ConnectivityManager>()!!.apply {
|
||||
// Checks if the device is on a metered network
|
||||
if (isActiveNetworkMetered) {
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package im.vector.app.fdroid.features.settings.troubleshoot
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
|
@ -30,7 +28,7 @@ class TestBatteryOptimization @Inject constructor(
|
|||
private val stringProvider: StringProvider
|
||||
) : TroubleshootTest(R.string.settings_troubleshoot_test_battery_title) {
|
||||
|
||||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
override fun perform(testParameters: TestParameters) {
|
||||
if (context.isIgnoringBatteryOptimizations()) {
|
||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_success)
|
||||
status = TestStatus.SUCCESS
|
||||
|
@ -39,7 +37,7 @@ class TestBatteryOptimization @Inject constructor(
|
|||
description = stringProvider.getString(R.string.settings_troubleshoot_test_battery_failed)
|
||||
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_battery_quickfix) {
|
||||
override fun doFix() {
|
||||
requestDisablingBatteryOptimization(context, activityResultLauncher)
|
||||
requestDisablingBatteryOptimization(context, testParameters.activityResultLauncher)
|
||||
}
|
||||
}
|
||||
status = TestStatus.FAILED
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package im.vector.app.gplay.features.settings.troubleshoot
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.google.firebase.messaging.FirebaseMessaging
|
||||
import im.vector.app.R
|
||||
|
@ -36,7 +34,7 @@ class TestFirebaseToken @Inject constructor(
|
|||
private val fcmHelper: FcmHelper,
|
||||
) : TroubleshootTest(R.string.settings_troubleshoot_test_fcm_title) {
|
||||
|
||||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
override fun perform(testParameters: TestParameters) {
|
||||
status = TestStatus.RUNNING
|
||||
try {
|
||||
FirebaseMessaging.getInstance().token
|
||||
|
@ -53,7 +51,7 @@ class TestFirebaseToken @Inject constructor(
|
|||
"ACCOUNT_MISSING" -> {
|
||||
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_fcm_failed_account_missing_quick_fix) {
|
||||
override fun doFix() {
|
||||
startAddGoogleAccountIntent(context, activityResultLauncher)
|
||||
startAddGoogleAccountIntent(context, testParameters.activityResultLauncher)
|
||||
}
|
||||
}
|
||||
stringProvider.getString(R.string.settings_troubleshoot_test_fcm_failed_account_missing, errorMsg)
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package im.vector.app.gplay.features.settings.troubleshoot
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.google.android.gms.common.ConnectionResult
|
||||
import com.google.android.gms.common.GoogleApiAvailability
|
||||
|
@ -35,7 +33,7 @@ class TestPlayServices @Inject constructor(
|
|||
) :
|
||||
TroubleshootTest(R.string.settings_troubleshoot_test_play_services_title) {
|
||||
|
||||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
override fun perform(testParameters: TestParameters) {
|
||||
val apiAvailability = GoogleApiAvailability.getInstance()
|
||||
val resultCode = apiAvailability.isGooglePlayServicesAvailable(context)
|
||||
if (resultCode == ConnectionResult.SUCCESS) {
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package im.vector.app.gplay.features.settings.troubleshoot
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.work.WorkInfo
|
||||
|
@ -42,7 +40,7 @@ class TestTokenRegistration @Inject constructor(
|
|||
) :
|
||||
TroubleshootTest(R.string.settings_troubleshoot_test_token_registration_title) {
|
||||
|
||||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
override fun perform(testParameters: TestParameters) {
|
||||
// Check if we have a registered pusher for this token
|
||||
val fcmToken = fcmHelper.getFcmToken() ?: run {
|
||||
status = TestStatus.FAILED
|
||||
|
@ -66,9 +64,9 @@ class TestTokenRegistration @Inject constructor(
|
|||
WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo ->
|
||||
if (workInfo != null) {
|
||||
if (workInfo.state == WorkInfo.State.SUCCEEDED) {
|
||||
manager?.retry(activityResultLauncher)
|
||||
manager?.retry(testParameters)
|
||||
} else if (workInfo.state == WorkInfo.State.FAILED) {
|
||||
manager?.retry(activityResultLauncher)
|
||||
manager?.retry(testParameters)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<!-- https://developer.android.com/develop/ui/views/notifications/notification-permission#exemptions -->
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<!-- Call feature -->
|
||||
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
|
||||
<!-- Commented because Google PlayStore does not like we add permission if we are not requiring it. And it was added for future use -->
|
||||
|
|
|
@ -48,6 +48,7 @@ import im.vector.app.core.platform.VectorMenuProvider
|
|||
import im.vector.app.core.pushers.FcmHelper
|
||||
import im.vector.app.core.pushers.PushersManager
|
||||
import im.vector.app.core.pushers.UnifiedPushHelper
|
||||
import im.vector.app.core.utils.registerForPermissionsResult
|
||||
import im.vector.app.core.utils.startSharePlainTextIntent
|
||||
import im.vector.app.databinding.ActivityHomeBinding
|
||||
import im.vector.app.features.MainActivity
|
||||
|
@ -143,6 +144,7 @@ class HomeActivity :
|
|||
@Inject lateinit var fcmHelper: FcmHelper
|
||||
@Inject lateinit var nightlyProxy: NightlyProxy
|
||||
@Inject lateinit var disclaimerDialog: DisclaimerDialog
|
||||
@Inject lateinit var notificationPermissionManager: NotificationPermissionManager
|
||||
|
||||
private var isNewAppLayoutEnabled: Boolean = false // delete once old app layout is removed
|
||||
|
||||
|
@ -172,6 +174,10 @@ class HomeActivity :
|
|||
}
|
||||
}
|
||||
|
||||
private val postPermissionLauncher = registerForPermissionsResult { _, _ ->
|
||||
// Nothing to do with the result.
|
||||
}
|
||||
|
||||
private val fragmentLifecycleCallbacks = object : FragmentManager.FragmentLifecycleCallbacks() {
|
||||
override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
|
||||
if (f is MatrixToBottomSheet) {
|
||||
|
@ -273,6 +279,7 @@ class HomeActivity :
|
|||
}
|
||||
is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it)
|
||||
HomeActivityViewEvents.ShowAnalyticsOptIn -> handleShowAnalyticsOptIn()
|
||||
HomeActivityViewEvents.ShowNotificationDialog -> handleShowNotificationDialog()
|
||||
HomeActivityViewEvents.ShowReleaseNotes -> handleShowReleaseNotes()
|
||||
HomeActivityViewEvents.NotifyUserForThreadsMigration -> handleNotifyUserForThreadsMigration()
|
||||
is HomeActivityViewEvents.MigrateThreads -> migrateThreadsIfNeeded(it.checkSession)
|
||||
|
@ -288,6 +295,10 @@ class HomeActivity :
|
|||
homeActivityViewModel.handle(HomeActivityViewActions.ViewStarted)
|
||||
}
|
||||
|
||||
private fun handleShowNotificationDialog() {
|
||||
notificationPermissionManager.eventuallyRequestPermission(this, postPermissionLauncher)
|
||||
}
|
||||
|
||||
private fun handleShowReleaseNotes() {
|
||||
startActivity(Intent(this, ReleaseNotesActivity::class.java))
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ sealed interface HomeActivityViewEvents : VectorViewEvents {
|
|||
data class OnCrossSignedInvalidated(val userItem: MatrixItem.UserItem) : HomeActivityViewEvents
|
||||
object PromptToEnableSessionPush : HomeActivityViewEvents
|
||||
object ShowAnalyticsOptIn : HomeActivityViewEvents
|
||||
object ShowNotificationDialog : HomeActivityViewEvents
|
||||
object ShowReleaseNotes : HomeActivityViewEvents
|
||||
object NotifyUserForThreadsMigration : HomeActivityViewEvents
|
||||
data class MigrateThreads(val checkSession: Boolean) : HomeActivityViewEvents
|
||||
|
|
|
@ -143,6 +143,8 @@ class HomeActivityViewModel @AssistedInject constructor(
|
|||
.onEach { didAskUser ->
|
||||
if (!didAskUser) {
|
||||
_viewEvents.post(HomeActivityViewEvents.ShowAnalyticsOptIn)
|
||||
} else {
|
||||
_viewEvents.post(HomeActivityViewEvents.ShowNotificationDialog)
|
||||
}
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
|
@ -162,6 +164,8 @@ class HomeActivityViewModel @AssistedInject constructor(
|
|||
// do nothing
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_viewEvents.post(HomeActivityViewEvents.ShowNotificationDialog)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package im.vector.app.features.home
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.content.ContextCompat
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.utils.checkPermissions
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
import org.matrix.android.sdk.api.util.BuildVersionSdkIntProvider
|
||||
import javax.inject.Inject
|
||||
|
||||
class NotificationPermissionManager @Inject constructor(
|
||||
private val sdkIntProvider: BuildVersionSdkIntProvider,
|
||||
private val vectorPreferences: VectorPreferences,
|
||||
) {
|
||||
|
||||
fun isPermissionGranted(activity: Activity): Boolean {
|
||||
return if (sdkIntProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) {
|
||||
ContextCompat.checkSelfPermission(
|
||||
activity,
|
||||
Manifest.permission.POST_NOTIFICATIONS
|
||||
) == PackageManager.PERMISSION_GRANTED
|
||||
} else {
|
||||
// No notification permission management before API 33.
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fun eventuallyRequestPermission(
|
||||
activity: Activity,
|
||||
requestPermissionLauncher: ActivityResultLauncher<Array<String>>,
|
||||
showRationale: Boolean = true,
|
||||
ignorePreference: Boolean = false,
|
||||
) {
|
||||
if (!sdkIntProvider.isAtLeast(Build.VERSION_CODES.TIRAMISU)) return
|
||||
if (!vectorPreferences.areNotificationEnabledForDevice() && !ignorePreference) return
|
||||
checkPermissions(
|
||||
listOf(Manifest.permission.POST_NOTIFICATIONS),
|
||||
activity,
|
||||
activityResultLauncher = requestPermissionLauncher,
|
||||
if (showRationale) R.string.permissions_rationale_msg_notification else 0
|
||||
)
|
||||
}
|
||||
|
||||
@RequiresApi(33)
|
||||
fun askPermission(requestPermissionLauncher: ActivityResultLauncher<Array<String>>) {
|
||||
requestPermissionLauncher.launch(
|
||||
arrayOf(Manifest.permission.POST_NOTIFICATIONS)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -43,9 +43,12 @@ import im.vector.app.core.pushers.UnifiedPushHelper
|
|||
import im.vector.app.core.services.GuardServiceStarter
|
||||
import im.vector.app.core.utils.combineLatest
|
||||
import im.vector.app.core.utils.isIgnoringBatteryOptimizations
|
||||
import im.vector.app.core.utils.registerForPermissionsResult
|
||||
import im.vector.app.core.utils.requestDisablingBatteryOptimization
|
||||
import im.vector.app.core.utils.startNotificationSettingsIntent
|
||||
import im.vector.app.features.VectorFeatures
|
||||
import im.vector.app.features.analytics.plan.MobileScreen
|
||||
import im.vector.app.features.home.NotificationPermissionManager
|
||||
import im.vector.app.features.notifications.NotificationUtils
|
||||
import im.vector.app.features.settings.BackgroundSyncMode
|
||||
import im.vector.app.features.settings.BackgroundSyncModeChooserDialog
|
||||
|
@ -76,12 +79,24 @@ class VectorSettingsNotificationPreferenceFragment :
|
|||
@Inject lateinit var vectorPreferences: VectorPreferences
|
||||
@Inject lateinit var guardServiceStarter: GuardServiceStarter
|
||||
@Inject lateinit var vectorFeatures: VectorFeatures
|
||||
@Inject lateinit var notificationPermissionManager: NotificationPermissionManager
|
||||
|
||||
override var titleRes: Int = R.string.settings_notifications
|
||||
override val preferenceXmlRes = R.xml.vector_settings_notifications
|
||||
|
||||
private var interactionListener: VectorSettingsFragmentInteractionListener? = null
|
||||
|
||||
private val notificationStartForActivityResult = registerStartForActivityResult { _ ->
|
||||
// No op
|
||||
}
|
||||
|
||||
private val postPermissionLauncher = registerForPermissionsResult { _, deniedPermanently ->
|
||||
if (deniedPermanently) {
|
||||
// Open System setting, to give a chance to the user to enable notification. Sometimes the permission dialog is not displayed
|
||||
startNotificationSettingsIntent(requireContext(), notificationStartForActivityResult)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
analyticsScreenName = MobileScreen.ScreenName.SettingsNotifications
|
||||
|
@ -118,6 +133,12 @@ class VectorSettingsNotificationPreferenceFragment :
|
|||
findPreference<VectorPreference>(VectorPreferences.SETTINGS_NOTIFICATION_METHOD_KEY)
|
||||
?.summary = unifiedPushHelper.getCurrentDistributorName()
|
||||
}
|
||||
notificationPermissionManager.eventuallyRequestPermission(
|
||||
requireActivity(),
|
||||
postPermissionLauncher,
|
||||
showRationale = false,
|
||||
ignorePreference = true
|
||||
)
|
||||
} else {
|
||||
unifiedPushHelper.unregister(pushersManager)
|
||||
session.pushersService().refreshPushers()
|
||||
|
|
|
@ -34,6 +34,8 @@ import im.vector.app.R
|
|||
import im.vector.app.core.extensions.cleanup
|
||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.core.utils.registerForPermissionsResult
|
||||
import im.vector.app.core.utils.startNotificationSettingsIntent
|
||||
import im.vector.app.databinding.FragmentSettingsNotificationsTroubleshootBinding
|
||||
import im.vector.app.features.notifications.NotificationActionIds
|
||||
import im.vector.app.features.push.NotificationTroubleshootTestManagerFactory
|
||||
|
@ -76,7 +78,7 @@ class VectorSettingsNotificationsTroubleshootFragment :
|
|||
}
|
||||
|
||||
views.troubleshootRunButton.debouncedClicks {
|
||||
testManager?.retry(testStartForActivityResult)
|
||||
testManager?.retry(TroubleshootTest.TestParameters(testStartForActivityResult, testStartForPermissionResult))
|
||||
}
|
||||
startUI()
|
||||
}
|
||||
|
@ -125,7 +127,7 @@ class VectorSettingsNotificationsTroubleshootFragment :
|
|||
}
|
||||
}
|
||||
views.troubleshootTestRecyclerView.adapter = testManager?.adapter
|
||||
testManager?.runDiagnostic(testStartForActivityResult)
|
||||
testManager?.runDiagnostic(TroubleshootTest.TestParameters(testStartForActivityResult, testStartForPermissionResult))
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
@ -139,8 +141,17 @@ class VectorSettingsNotificationsTroubleshootFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private val testStartForPermissionResult = registerForPermissionsResult { allGranted, deniedPermanently ->
|
||||
if (allGranted) {
|
||||
retry()
|
||||
} else if (deniedPermanently) {
|
||||
// Open System setting
|
||||
startNotificationSettingsIntent(requireContext(), testStartForActivityResult)
|
||||
}
|
||||
}
|
||||
|
||||
private fun retry() {
|
||||
testManager?.retry(testStartForActivityResult)
|
||||
testManager?.retry(TroubleshootTest.TestParameters(testStartForActivityResult, testStartForPermissionResult))
|
||||
}
|
||||
|
||||
override fun onDetach() {
|
||||
|
|
|
@ -15,10 +15,8 @@
|
|||
*/
|
||||
package im.vector.app.features.settings.troubleshoot
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.fragment.app.Fragment
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
|
@ -50,7 +48,7 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) {
|
|||
test.manager = this
|
||||
}
|
||||
|
||||
fun runDiagnostic(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
fun runDiagnostic(testParameters: TroubleshootTest.TestParameters) {
|
||||
if (isCancelled) return
|
||||
currentTestIndex = 0
|
||||
val handler = Handler(Looper.getMainLooper())
|
||||
|
@ -69,7 +67,7 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) {
|
|||
// Cosmetic: Start with a small delay for UI/UX reason (better animation effect) for non async tests
|
||||
handler.postDelayed({
|
||||
if (fragment.isAdded) {
|
||||
troubleshootTest.perform(activityResultLauncher)
|
||||
troubleshootTest.perform(testParameters)
|
||||
}
|
||||
}, 600)
|
||||
} else {
|
||||
|
@ -81,18 +79,18 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) {
|
|||
}
|
||||
}
|
||||
if (fragment.isAdded) {
|
||||
testList.firstOrNull()?.perform(activityResultLauncher)
|
||||
testList.firstOrNull()?.perform(testParameters)
|
||||
}
|
||||
}
|
||||
|
||||
fun retry(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
fun retry(testParameters: TroubleshootTest.TestParameters) {
|
||||
testList.forEach {
|
||||
it.cancel()
|
||||
it.description = null
|
||||
it.quickFix = null
|
||||
it.status = TroubleshootTest.TestStatus.NOT_STARTED
|
||||
}
|
||||
runDiagnostic(activityResultLauncher)
|
||||
runDiagnostic(testParameters)
|
||||
}
|
||||
|
||||
fun hasQuickFix(): Boolean {
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package im.vector.app.features.settings.troubleshoot
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.di.ActiveSessionHolder
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
|
@ -38,7 +36,7 @@ class TestAccountSettings @Inject constructor(
|
|||
) :
|
||||
TroubleshootTest(R.string.settings_troubleshoot_test_account_settings_title) {
|
||||
|
||||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
override fun perform(testParameters: TestParameters) {
|
||||
val session = activeSessionHolder.getSafeActiveSession() ?: return
|
||||
val defaultRule = session.pushRuleService().getPushRules().getAllRules()
|
||||
.find { it.ruleId == RuleIds.RULE_ID_DISABLE_ALL }
|
||||
|
@ -59,7 +57,7 @@ class TestAccountSettings @Inject constructor(
|
|||
session.pushRuleService().updatePushRuleEnableStatus(RuleKind.OVERRIDE, defaultRule, !defaultRule.enabled)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
manager?.retry(activityResultLauncher)
|
||||
manager?.retry(testParameters)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package im.vector.app.features.settings.troubleshoot
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.pushers.FcmHelper
|
||||
import im.vector.app.core.pushers.UnifiedPushHelper
|
||||
|
@ -30,7 +28,7 @@ class TestAvailableUnifiedPushDistributors @Inject constructor(
|
|||
private val fcmHelper: FcmHelper,
|
||||
) : TroubleshootTest(R.string.settings_troubleshoot_test_distributors_title) {
|
||||
|
||||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
override fun perform(testParameters: TestParameters) {
|
||||
val distributors = unifiedPushHelper.getExternalDistributors()
|
||||
description = if (distributors.isEmpty()) {
|
||||
stringProvider.getString(
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package im.vector.app.features.settings.troubleshoot
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.pushers.UnifiedPushHelper
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
|
@ -28,7 +26,7 @@ class TestCurrentUnifiedPushDistributor @Inject constructor(
|
|||
private val stringProvider: StringProvider,
|
||||
) : TroubleshootTest(R.string.settings_troubleshoot_test_current_distributor_title) {
|
||||
|
||||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
override fun perform(testParameters: TestParameters) {
|
||||
description = stringProvider.getString(
|
||||
R.string.settings_troubleshoot_test_current_distributor,
|
||||
unifiedPushHelper.getCurrentDistributorName()
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package im.vector.app.features.settings.troubleshoot
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.features.settings.VectorPreferences
|
||||
|
@ -31,7 +29,7 @@ class TestDeviceSettings @Inject constructor(
|
|||
) :
|
||||
TroubleshootTest(R.string.settings_troubleshoot_test_device_settings_title) {
|
||||
|
||||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
override fun perform(testParameters: TestParameters) {
|
||||
if (vectorPreferences.areNotificationEnabledForDevice()) {
|
||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_device_settings_success)
|
||||
quickFix = null
|
||||
|
@ -40,7 +38,7 @@ class TestDeviceSettings @Inject constructor(
|
|||
quickFix = object : TroubleshootQuickFix(R.string.settings_troubleshoot_test_device_settings_quickfix) {
|
||||
override fun doFix() {
|
||||
vectorPreferences.setNotificationEnabledForDevice(true)
|
||||
manager?.retry(activityResultLauncher)
|
||||
manager?.retry(testParameters)
|
||||
}
|
||||
}
|
||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_device_settings_failed)
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package im.vector.app.features.settings.troubleshoot
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.work.WorkInfo
|
||||
|
@ -38,7 +36,7 @@ class TestEndpointAsTokenRegistration @Inject constructor(
|
|||
private val unifiedPushHelper: UnifiedPushHelper,
|
||||
) : TroubleshootTest(R.string.settings_troubleshoot_test_endpoint_registration_title) {
|
||||
|
||||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
override fun perform(testParameters: TestParameters) {
|
||||
// Check if we have a registered pusher for this token
|
||||
val endpoint = unifiedPushHelper.getEndpointOrToken() ?: run {
|
||||
status = TestStatus.FAILED
|
||||
|
@ -66,9 +64,9 @@ class TestEndpointAsTokenRegistration @Inject constructor(
|
|||
WorkManager.getInstance(context).getWorkInfoByIdLiveData(workId).observe(context, Observer { workInfo ->
|
||||
if (workInfo != null) {
|
||||
if (workInfo.state == WorkInfo.State.SUCCEEDED) {
|
||||
manager?.retry(activityResultLauncher)
|
||||
manager?.retry(testParameters)
|
||||
} else if (workInfo.state == WorkInfo.State.FAILED) {
|
||||
manager?.retry(activityResultLauncher)
|
||||
manager?.retry(testParameters)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
package im.vector.app.features.settings.troubleshoot
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.utils.startNotificationSettingsIntent
|
||||
|
@ -34,14 +32,14 @@ class TestNotification @Inject constructor(
|
|||
) :
|
||||
TroubleshootTest(R.string.settings_troubleshoot_test_notification_title) {
|
||||
|
||||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
override fun perform(testParameters: TestParameters) {
|
||||
// Display the notification right now
|
||||
notificationUtils.displayDiagnosticNotification()
|
||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_notification_notice)
|
||||
|
||||
quickFix = object : TroubleshootQuickFix(R.string.open_settings) {
|
||||
override fun doFix() {
|
||||
startNotificationSettingsIntent(context, activityResultLauncher)
|
||||
startNotificationSettingsIntent(context, testParameters.activityResultLauncher)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package im.vector.app.features.settings.troubleshoot
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.di.ActiveSessionHolder
|
||||
import im.vector.app.core.error.ErrorFormatter
|
||||
|
@ -43,7 +41,7 @@ class TestPushFromPushGateway @Inject constructor(
|
|||
private var action: Job? = null
|
||||
private var pushReceived: Boolean = false
|
||||
|
||||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
override fun perform(testParameters: TestParameters) {
|
||||
pushReceived = false
|
||||
action = activeSessionHolder.getActiveSession().coroutineScope.launch {
|
||||
val result = runCatching { pushersManager.testPush() }
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package im.vector.app.features.settings.troubleshoot
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.di.ActiveSessionHolder
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
|
@ -39,7 +37,7 @@ class TestPushRulesSettings @Inject constructor(
|
|||
RuleIds.RULE_ID_ALL_OTHER_MESSAGES_ROOMS
|
||||
)
|
||||
|
||||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
override fun perform(testParameters: TestParameters) {
|
||||
val session = activeSessionHolder.getSafeActiveSession() ?: return
|
||||
val pushRules = session.pushRuleService().getPushRules().getAllRules()
|
||||
var oneOrMoreRuleIsOff = false
|
||||
|
|
|
@ -15,34 +15,44 @@
|
|||
*/
|
||||
package im.vector.app.features.settings.troubleshoot
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
import im.vector.app.core.utils.startNotificationSettingsIntent
|
||||
import im.vector.app.features.home.NotificationPermissionManager
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Checks if notifications are enable in the system settings for this app.
|
||||
* On Android 13, it will check for the notification permission.
|
||||
*/
|
||||
class TestSystemSettings @Inject constructor(
|
||||
private val context: FragmentActivity,
|
||||
private val stringProvider: StringProvider
|
||||
) :
|
||||
TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) {
|
||||
private val stringProvider: StringProvider,
|
||||
private val notificationPermissionManager: NotificationPermissionManager,
|
||||
) : TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) {
|
||||
|
||||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
override fun perform(testParameters: TestParameters) {
|
||||
if (NotificationManagerCompat.from(context).areNotificationsEnabled()) {
|
||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_success)
|
||||
quickFix = null
|
||||
status = TestStatus.SUCCESS
|
||||
} else {
|
||||
if (notificationPermissionManager.isPermissionGranted(context)) {
|
||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_failed)
|
||||
quickFix = object : TroubleshootQuickFix(R.string.open_settings) {
|
||||
override fun doFix() {
|
||||
startNotificationSettingsIntent(context, activityResultLauncher)
|
||||
startNotificationSettingsIntent(context, testParameters.activityResultLauncher)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// In this case, we can ask for user permission
|
||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_system_settings_permission_failed)
|
||||
quickFix = object : TroubleshootQuickFix(R.string.grant_permission) {
|
||||
override fun doFix() {
|
||||
notificationPermissionManager.askPermission(testParameters.permissionResultLauncher)
|
||||
}
|
||||
}
|
||||
}
|
||||
status = TestStatus.FAILED
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package im.vector.app.features.settings.troubleshoot
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.pushers.UnifiedPushHelper
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
|
@ -28,7 +26,7 @@ class TestUnifiedPushEndpoint @Inject constructor(
|
|||
private val unifiedPushHelper: UnifiedPushHelper,
|
||||
) : TroubleshootTest(R.string.settings_troubleshoot_test_current_endpoint_title) {
|
||||
|
||||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
override fun perform(testParameters: TestParameters) {
|
||||
val endpoint = unifiedPushHelper.getPrivacyFriendlyUpEndpoint()
|
||||
if (endpoint != null) {
|
||||
description = stringProvider.getString(R.string.settings_troubleshoot_test_current_endpoint_success, endpoint)
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package im.vector.app.features.settings.troubleshoot
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.pushers.UnifiedPushHelper
|
||||
import im.vector.app.core.resources.StringProvider
|
||||
|
@ -28,7 +26,7 @@ class TestUnifiedPushGateway @Inject constructor(
|
|||
private val stringProvider: StringProvider
|
||||
) : TroubleshootTest(R.string.settings_troubleshoot_test_current_gateway_title) {
|
||||
|
||||
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
override fun perform(testParameters: TestParameters) {
|
||||
description = stringProvider.getString(
|
||||
R.string.settings_troubleshoot_test_current_gateway,
|
||||
unifiedPushHelper.getPushGateway()
|
||||
|
|
|
@ -22,6 +22,11 @@ import kotlin.properties.Delegates
|
|||
|
||||
abstract class TroubleshootTest(@StringRes val titleResId: Int) {
|
||||
|
||||
data class TestParameters(
|
||||
val activityResultLauncher: ActivityResultLauncher<Intent>,
|
||||
val permissionResultLauncher: ActivityResultLauncher<Array<String>>
|
||||
)
|
||||
|
||||
enum class TestStatus {
|
||||
NOT_STARTED,
|
||||
RUNNING,
|
||||
|
@ -40,7 +45,7 @@ abstract class TroubleshootTest(@StringRes val titleResId: Int) {
|
|||
|
||||
var manager: NotificationTroubleshootTestManager? = null
|
||||
|
||||
abstract fun perform(activityResultLauncher: ActivityResultLauncher<Intent>)
|
||||
abstract fun perform(testParameters: TestParameters)
|
||||
|
||||
fun isFinished(): Boolean = (status == TestStatus.FAILED || status == TestStatus.SUCCESS)
|
||||
|
||||
|
|
Loading…
Reference in New Issue