Push test: better error handling

This commit is contained in:
Benoit Marty 2020-10-02 15:23:42 +02:00 committed by Benoit Marty
parent 7d53dfeca4
commit 02f1dab9b2
11 changed files with 92 additions and 8 deletions

View file

@ -71,12 +71,14 @@ interface PushersService {
* @param url the Sygnal url (full path) * @param url the Sygnal url (full path)
* @param appId the application id * @param appId the application id
* @param pushkey the FCM token * @param pushkey the FCM token
* @param callback callback to know if Sygnal has accepted the request. In this case, the app should receive a Push with the provided data (TODO) * @param eventId the eventId which will be sent in the Push message. Use a fake eventId.
* * @param callback callback to know if Sygnal has accepted the request. In this case, the app should receive a Push with the provided eventId.
* In case of error, PusherRejected failure can happen. In this case it means that the pushkey is not valid.
*/ */
fun testPush(url: String, fun testPush(url: String,
appId: String, appId: String,
pushkey: String, pushkey: String,
eventId: String,
callback: MatrixCallback<Unit>) callback: MatrixCallback<Unit>)
/** /**

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020 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 org.matrix.android.sdk.api.session.pushers
import org.matrix.android.sdk.api.failure.Failure
sealed class SygnalFailure : Failure.FeatureFailure() {
object PusherRejected : SygnalFailure()
}

View file

@ -50,9 +50,10 @@ internal class DefaultPushersService @Inject constructor(
override fun testPush(url: String, override fun testPush(url: String,
appId: String, appId: String,
pushkey: String, pushkey: String,
eventId: String,
callback: MatrixCallback<Unit>) { callback: MatrixCallback<Unit>) {
sygnalNotifyTask sygnalNotifyTask
.configureWith(SygnalNotifyTask.Params(url, appId, pushkey)) { .configureWith(SygnalNotifyTask.Params(url, appId, pushkey, eventId)) {
this.callback = callback this.callback = callback
} }
.executeBy(taskExecutor) .executeBy(taskExecutor)

View file

@ -21,6 +21,9 @@ import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class SygnalNotification( internal data class SygnalNotification(
@Json(name = "event_id")
val eventId: String,
/** /**
* Required. This is an array of devices that the notification should be sent to. * Required. This is an array of devices that the notification should be sent to.
*/ */

View file

@ -16,6 +16,7 @@
package org.matrix.android.sdk.internal.session.pushers.sygnal package org.matrix.android.sdk.internal.session.pushers.sygnal
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import org.matrix.android.sdk.api.session.pushers.SygnalFailure
import org.matrix.android.sdk.internal.di.Unauthenticated import org.matrix.android.sdk.internal.di.Unauthenticated
import org.matrix.android.sdk.internal.network.NetworkConstants import org.matrix.android.sdk.internal.network.NetworkConstants
import org.matrix.android.sdk.internal.network.RetrofitFactory import org.matrix.android.sdk.internal.network.RetrofitFactory
@ -27,7 +28,8 @@ internal interface SygnalNotifyTask : Task<SygnalNotifyTask.Params, Unit> {
data class Params( data class Params(
val url: String, val url: String,
val appId: String, val appId: String,
val pushKey: String val pushKey: String,
val eventId: String
) )
} }
@ -47,6 +49,7 @@ internal class DefaultSygnalNotifyTask @Inject constructor(
apiCall = sygnalApi.notify( apiCall = sygnalApi.notify(
SygnalNotifyBody( SygnalNotifyBody(
SygnalNotification( SygnalNotification(
eventId = params.eventId,
devices = listOf( devices = listOf(
SygnalDevice( SygnalDevice(
params.appId, params.appId,
@ -59,7 +62,7 @@ internal class DefaultSygnalNotifyTask @Inject constructor(
} }
if (response.rejectedPushKey.contains(params.pushKey)) { if (response.rejectedPushKey.contains(params.pushKey)) {
throw IllegalStateException("Failure") throw SygnalFailure.PusherRejected
} }
} }
} }

View file

@ -17,11 +17,13 @@ package im.vector.app.gplay.features.settings.troubleshoot
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.PushersManager
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.features.settings.troubleshoot.TroubleshootTest import im.vector.app.features.settings.troubleshoot.TroubleshootTest
import im.vector.app.push.fcm.FcmHelper import im.vector.app.push.fcm.FcmHelper
import org.matrix.android.sdk.api.MatrixCallback import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.pushers.SygnalFailure
import javax.inject.Inject import javax.inject.Inject
/** /**
@ -29,6 +31,7 @@ import javax.inject.Inject
*/ */
class TestPushFromSygnal @Inject constructor(private val context: AppCompatActivity, class TestPushFromSygnal @Inject constructor(private val context: AppCompatActivity,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val errorFormatter: ErrorFormatter,
private val pushersManager: PushersManager) private val pushersManager: PushersManager)
: TroubleshootTest(R.string.settings_troubleshoot_test_push_loop_title) { : TroubleshootTest(R.string.settings_troubleshoot_test_push_loop_title) {
@ -39,7 +42,11 @@ class TestPushFromSygnal @Inject constructor(private val context: AppCompatActiv
} }
pushersManager.testPush(fcmToken, object : MatrixCallback<Unit> { pushersManager.testPush(fcmToken, object : MatrixCallback<Unit> {
override fun onFailure(failure: Throwable) { override fun onFailure(failure: Throwable) {
description = stringProvider.getString(R.string.settings_troubleshoot_test_push_loop_failed) description = if (failure is SygnalFailure.PusherRejected) {
stringProvider.getString(R.string.settings_troubleshoot_test_push_loop_failed)
} else {
errorFormatter.toHumanReadable(failure)
}
status = TestStatus.FAILED status = TestStatus.FAILED
} }

View file

@ -75,6 +75,13 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
* @param message the message * @param message the message
*/ */
override fun onMessageReceived(message: RemoteMessage) { override fun onMessageReceived(message: RemoteMessage) {
// Diagnostic Push
if (message.data["event_id"] == PushersManager.TEST_EVENT_ID) {
// Display the notification right now
notificationDrawerManager.displayDiagnosticNotification()
return
}
if (!vectorPreferences.areNotificationEnabledForDevice()) { if (!vectorPreferences.areNotificationEnabledForDevice()) {
Timber.i("Notification are disabled for this device") Timber.i("Notification are disabled for this device")
return return

View file

@ -41,6 +41,7 @@ class PushersManager @Inject constructor(
stringProvider.getString(R.string.pusher_http_url), stringProvider.getString(R.string.pusher_http_url),
stringProvider.getString(R.string.pusher_app_id), stringProvider.getString(R.string.pusher_app_id),
pushKey, pushKey,
TEST_EVENT_ID,
callback callback
) )
} }
@ -66,4 +67,8 @@ class PushersManager @Inject constructor(
val currentSession = activeSessionHolder.getSafeActiveSession() ?: return val currentSession = activeSessionHolder.getSafeActiveSession() ?: return
currentSession.removeHttpPusher(pushKey, stringProvider.getString(R.string.pusher_app_id), callback) currentSession.removeHttpPusher(pushKey, stringProvider.getString(R.string.pusher_app_id), callback)
} }
companion object {
const val TEST_EVENT_ID = "\$THIS_IS_A_FAKE_EVENT_ID"
}
} }

View file

@ -590,6 +590,10 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
} }
} }
fun displayDiagnosticNotification() {
notificationUtils.displayDiagnosticNotification()
}
companion object { companion object {
private const val SUMMARY_NOTIFICATION_ID = 0 private const val SUMMARY_NOTIFICATION_ID = 0
private const val ROOM_MESSAGES_NOTIFICATION_ID = 1 private const val ROOM_MESSAGES_NOTIFICATION_ID = 1

View file

@ -27,8 +27,10 @@ import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Canvas
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
@ -36,6 +38,7 @@ import androidx.core.app.RemoteInput
import androidx.core.app.TaskStackBuilder import androidx.core.app.TaskStackBuilder
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.IconCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import im.vector.app.BuildConfig import im.vector.app.BuildConfig
@ -47,7 +50,6 @@ import im.vector.app.features.call.service.CallHeadsUpActionReceiver
import im.vector.app.features.home.HomeActivity import im.vector.app.features.home.HomeActivity
import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailActivity
import im.vector.app.features.home.room.detail.RoomDetailArgs import im.vector.app.features.home.room.detail.RoomDetailArgs
import im.vector.app.features.pin.PinLocker
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -61,7 +63,6 @@ import kotlin.random.Random
@Singleton @Singleton
class NotificationUtils @Inject constructor(private val context: Context, class NotificationUtils @Inject constructor(private val context: Context,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val pinLocker: PinLocker,
private val vectorPreferences: VectorPreferences) { private val vectorPreferences: VectorPreferences) {
companion object { companion object {
@ -845,6 +846,33 @@ class NotificationUtils @Inject constructor(private val context: Context,
} }
} }
fun displayDiagnosticNotification() {
notificationManager.notify(
"DIAGNOSTIC",
888,
NotificationCompat.Builder(context, NOISY_NOTIFICATION_CHANNEL_ID)
.setContentTitle(stringProvider.getString(R.string.app_name))
.setContentText(stringProvider.getString(R.string.settings_troubleshoot_test_push_notification_content))
.setSmallIcon(R.drawable.ic_status_bar)
.setLargeIcon(getBitmap(context, R.drawable.element_logo_green))
.setColor(ContextCompat.getColor(context, R.color.notification_accent_color))
.setPriority(NotificationCompat.PRIORITY_MAX)
.setCategory(NotificationCompat.CATEGORY_STATUS)
.setAutoCancel(true)
.build()
)
}
private fun getBitmap(context: Context, @DrawableRes drawableRes: Int): Bitmap? {
val drawable = ResourcesCompat.getDrawable(context.resources, drawableRes, null) ?: return null
val canvas = Canvas()
val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888)
canvas.setBitmap(bitmap)
drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
drawable.draw(canvas)
return bitmap
}
/** /**
* Return true it the user has enabled the do not disturb mode * Return true it the user has enabled the do not disturb mode
*/ */

View file

@ -752,6 +752,7 @@
<string name="settings_troubleshoot_test_push_loop_title">Test Push</string> <string name="settings_troubleshoot_test_push_loop_title">Test Push</string>
<string name="settings_troubleshoot_test_push_loop_success">The application is receiving PUSH, you should see a notification.</string> <string name="settings_troubleshoot_test_push_loop_success">The application is receiving PUSH, you should see a notification.</string>
<string name="settings_troubleshoot_test_push_loop_failed">Failed to receive push. Solution could be to reinstall the application.</string> <string name="settings_troubleshoot_test_push_loop_failed">Failed to receive push. Solution could be to reinstall the application.</string>
<string name="settings_troubleshoot_test_push_notification_content">You are receiving PUSH!</string>
<string name="settings_troubleshoot_test_foreground_service_started_title">Notifications Service</string> <string name="settings_troubleshoot_test_foreground_service_started_title">Notifications Service</string>
<string name="settings_troubleshoot_test_foreground_service_startedt_success">Notifications Service is running.</string> <string name="settings_troubleshoot_test_foreground_service_startedt_success">Notifications Service is running.</string>