Merge pull request #2203 from vector-im/feature/bma/pusher_data
Feature/bma/pusher data
This commit is contained in:
commit
6f709a1e7c
|
@ -29,6 +29,7 @@
|
||||||
<w>signout</w>
|
<w>signout</w>
|
||||||
<w>signup</w>
|
<w>signup</w>
|
||||||
<w>ssss</w>
|
<w>ssss</w>
|
||||||
|
<w>sygnal</w>
|
||||||
<w>threepid</w>
|
<w>threepid</w>
|
||||||
<w>unwedging</w>
|
<w>unwedging</w>
|
||||||
</words>
|
</words>
|
||||||
|
|
|
@ -16,6 +16,8 @@ Improvements 🙌:
|
||||||
- Drawer: move settings access and add sign out action (#2171)
|
- Drawer: move settings access and add sign out action (#2171)
|
||||||
- Filter room member (and banned users) by name (#2184)
|
- Filter room member (and banned users) by name (#2184)
|
||||||
- Implement "Jump to read receipt" and "Mention" actions on the room member profile screen
|
- Implement "Jump to read receipt" and "Mention" actions on the room member profile screen
|
||||||
|
- Add Sygnal API implementation to test is Push are correctly received
|
||||||
|
- Add PushGateway API implementation to test if Push are correctly received
|
||||||
|
|
||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
- Improve support for image/audio/video/file selection with intent changes (#1376)
|
- Improve support for image/audio/video/file selection with intent changes (#1376)
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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 PushGatewayFailure : Failure.FeatureFailure() {
|
||||||
|
object PusherRejected : PushGatewayFailure()
|
||||||
|
}
|
|
@ -66,6 +66,21 @@ interface PushersService {
|
||||||
append: Boolean,
|
append: Boolean,
|
||||||
withEventIdOnly: Boolean): UUID
|
withEventIdOnly: Boolean): UUID
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directly ask the push gateway to send a push to this device
|
||||||
|
* @param url the push gateway url (full path)
|
||||||
|
* @param appId the application id
|
||||||
|
* @param pushkey the FCM token
|
||||||
|
* @param eventId the eventId which will be sent in the Push message. Use a fake eventId.
|
||||||
|
* @param callback callback to know if the push gateway 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,
|
||||||
|
appId: String,
|
||||||
|
pushkey: String,
|
||||||
|
eventId: String,
|
||||||
|
callback: MatrixCallback<Unit>): Cancelable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the http pusher
|
* Remove the http pusher
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package org.matrix.android.sdk.internal.network
|
package org.matrix.android.sdk.internal.network
|
||||||
|
|
||||||
internal object NetworkConstants {
|
internal object NetworkConstants {
|
||||||
|
// Homeserver
|
||||||
private const val URI_API_PREFIX_PATH = "_matrix/client"
|
private const val URI_API_PREFIX_PATH = "_matrix/client"
|
||||||
const val URI_API_PREFIX_PATH_ = "$URI_API_PREFIX_PATH/"
|
const val URI_API_PREFIX_PATH_ = "$URI_API_PREFIX_PATH/"
|
||||||
const val URI_API_PREFIX_PATH_R0 = "$URI_API_PREFIX_PATH/r0/"
|
const val URI_API_PREFIX_PATH_R0 = "$URI_API_PREFIX_PATH/r0/"
|
||||||
|
@ -31,5 +31,9 @@ internal object NetworkConstants {
|
||||||
const val URI_IDENTITY_PREFIX_PATH = "_matrix/identity/v2"
|
const val URI_IDENTITY_PREFIX_PATH = "_matrix/identity/v2"
|
||||||
const val URI_IDENTITY_PATH_V2 = "$URI_IDENTITY_PREFIX_PATH/"
|
const val URI_IDENTITY_PATH_V2 = "$URI_IDENTITY_PREFIX_PATH/"
|
||||||
|
|
||||||
|
// Push Gateway
|
||||||
|
const val URI_PUSH_GATEWAY_PREFIX_PATH = "_matrix/push/v1/"
|
||||||
|
|
||||||
|
// Integration
|
||||||
const val URI_INTEGRATION_MANAGER_PATH = "_matrix/integrations/v1/"
|
const val URI_INTEGRATION_MANAGER_PATH = "_matrix/integrations/v1/"
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,8 +113,4 @@ constructor(trustPinned: Array<TrustManager>, acceptedTlsVersions: List<TlsVersi
|
||||||
}
|
}
|
||||||
return socket
|
return socket
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val LOG_TAG = TLSSocketFactory::class.java.simpleName
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.matrix.android.sdk.internal.database.query.where
|
||||||
import org.matrix.android.sdk.internal.di.SessionDatabase
|
import org.matrix.android.sdk.internal.di.SessionDatabase
|
||||||
import org.matrix.android.sdk.internal.di.SessionId
|
import org.matrix.android.sdk.internal.di.SessionId
|
||||||
import org.matrix.android.sdk.internal.di.WorkManagerProvider
|
import org.matrix.android.sdk.internal.di.WorkManagerProvider
|
||||||
|
import org.matrix.android.sdk.internal.session.pushers.gateway.PushGatewayNotifyTask
|
||||||
import org.matrix.android.sdk.internal.task.TaskExecutor
|
import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
import org.matrix.android.sdk.internal.task.configureWith
|
import org.matrix.android.sdk.internal.task.configureWith
|
||||||
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
||||||
|
@ -41,10 +42,23 @@ internal class DefaultPushersService @Inject constructor(
|
||||||
@SessionDatabase private val monarchy: Monarchy,
|
@SessionDatabase private val monarchy: Monarchy,
|
||||||
@SessionId private val sessionId: String,
|
@SessionId private val sessionId: String,
|
||||||
private val getPusherTask: GetPushersTask,
|
private val getPusherTask: GetPushersTask,
|
||||||
|
private val pushGatewayNotifyTask: PushGatewayNotifyTask,
|
||||||
private val removePusherTask: RemovePusherTask,
|
private val removePusherTask: RemovePusherTask,
|
||||||
private val taskExecutor: TaskExecutor
|
private val taskExecutor: TaskExecutor
|
||||||
) : PushersService {
|
) : PushersService {
|
||||||
|
|
||||||
|
override fun testPush(url: String,
|
||||||
|
appId: String,
|
||||||
|
pushkey: String,
|
||||||
|
eventId: String,
|
||||||
|
callback: MatrixCallback<Unit>): Cancelable {
|
||||||
|
return pushGatewayNotifyTask
|
||||||
|
.configureWith(PushGatewayNotifyTask.Params(url, appId, pushkey, eventId)) {
|
||||||
|
this.callback = callback
|
||||||
|
}
|
||||||
|
.executeBy(taskExecutor)
|
||||||
|
}
|
||||||
|
|
||||||
override fun refreshPushers() {
|
override fun refreshPushers() {
|
||||||
getPusherTask
|
getPusherTask
|
||||||
.configureWith()
|
.configureWith()
|
||||||
|
|
|
@ -25,6 +25,8 @@ import org.matrix.android.sdk.api.session.pushers.PushersService
|
||||||
import org.matrix.android.sdk.internal.session.notification.DefaultProcessEventForPushTask
|
import org.matrix.android.sdk.internal.session.notification.DefaultProcessEventForPushTask
|
||||||
import org.matrix.android.sdk.internal.session.notification.DefaultPushRuleService
|
import org.matrix.android.sdk.internal.session.notification.DefaultPushRuleService
|
||||||
import org.matrix.android.sdk.internal.session.notification.ProcessEventForPushTask
|
import org.matrix.android.sdk.internal.session.notification.ProcessEventForPushTask
|
||||||
|
import org.matrix.android.sdk.internal.session.pushers.gateway.DefaultPushGatewayNotifyTask
|
||||||
|
import org.matrix.android.sdk.internal.session.pushers.gateway.PushGatewayNotifyTask
|
||||||
import org.matrix.android.sdk.internal.session.room.notification.DefaultSetRoomNotificationStateTask
|
import org.matrix.android.sdk.internal.session.room.notification.DefaultSetRoomNotificationStateTask
|
||||||
import org.matrix.android.sdk.internal.session.room.notification.SetRoomNotificationStateTask
|
import org.matrix.android.sdk.internal.session.room.notification.SetRoomNotificationStateTask
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
|
@ -86,4 +88,7 @@ internal abstract class PushersModule {
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun bindProcessEventForPushTask(task: DefaultProcessEventForPushTask): ProcessEventForPushTask
|
abstract fun bindProcessEventForPushTask(task: DefaultProcessEventForPushTask): ProcessEventForPushTask
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun bindPushGatewayNotifyTask(task: DefaultPushGatewayNotifyTask): PushGatewayNotifyTask
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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.internal.session.pushers.gateway
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.internal.network.NetworkConstants
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.http.Body
|
||||||
|
import retrofit2.http.POST
|
||||||
|
|
||||||
|
internal interface PushGatewayAPI {
|
||||||
|
/**
|
||||||
|
* Ask the Push Gateway to send a push to the current device.
|
||||||
|
*
|
||||||
|
* Ref: https://matrix.org/docs/spec/push_gateway/r0.1.1#post-matrix-push-v1-notify
|
||||||
|
*/
|
||||||
|
@POST(NetworkConstants.URI_PUSH_GATEWAY_PREFIX_PATH + "notify")
|
||||||
|
fun notify(@Body body: PushGatewayNotifyBody): Call<PushGatewayNotifyResponse>
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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.internal.session.pushers.gateway
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
internal data class PushGatewayDevice(
|
||||||
|
/**
|
||||||
|
* Required. The app_id given when the pusher was created.
|
||||||
|
*/
|
||||||
|
@Json(name = "app_id")
|
||||||
|
val appId: String,
|
||||||
|
/**
|
||||||
|
* Required. The pushkey given when the pusher was created.
|
||||||
|
*/
|
||||||
|
@Json(name = "pushkey")
|
||||||
|
val pushKey: String
|
||||||
|
)
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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.internal.session.pushers.gateway
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
internal data class PushGatewayNotification(
|
||||||
|
@Json(name = "event_id")
|
||||||
|
val eventId: String,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Required. This is an array of devices that the notification should be sent to.
|
||||||
|
*/
|
||||||
|
@Json(name = "devices")
|
||||||
|
val devices: List<PushGatewayDevice>
|
||||||
|
)
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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.internal.session.pushers.gateway
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
internal data class PushGatewayNotifyBody(
|
||||||
|
/**
|
||||||
|
* Required. Information about the push notification
|
||||||
|
*/
|
||||||
|
@Json(name = "notification")
|
||||||
|
val notification: PushGatewayNotification
|
||||||
|
)
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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.internal.session.pushers.gateway
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
internal data class PushGatewayNotifyResponse(
|
||||||
|
@Json(name = "rejected")
|
||||||
|
val rejectedPushKeys: List<String>
|
||||||
|
)
|
|
@ -0,0 +1,68 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
*
|
||||||
|
* 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.internal.session.pushers.gateway
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import org.matrix.android.sdk.api.session.pushers.PushGatewayFailure
|
||||||
|
import org.matrix.android.sdk.internal.di.Unauthenticated
|
||||||
|
import org.matrix.android.sdk.internal.network.NetworkConstants
|
||||||
|
import org.matrix.android.sdk.internal.network.RetrofitFactory
|
||||||
|
import org.matrix.android.sdk.internal.network.executeRequest
|
||||||
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
internal interface PushGatewayNotifyTask : Task<PushGatewayNotifyTask.Params, Unit> {
|
||||||
|
data class Params(
|
||||||
|
val url: String,
|
||||||
|
val appId: String,
|
||||||
|
val pushKey: String,
|
||||||
|
val eventId: String
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class DefaultPushGatewayNotifyTask @Inject constructor(
|
||||||
|
private val retrofitFactory: RetrofitFactory,
|
||||||
|
@Unauthenticated private val unauthenticatedOkHttpClient: OkHttpClient
|
||||||
|
) : PushGatewayNotifyTask {
|
||||||
|
|
||||||
|
override suspend fun execute(params: PushGatewayNotifyTask.Params) {
|
||||||
|
val sygnalApi = retrofitFactory.create(
|
||||||
|
unauthenticatedOkHttpClient,
|
||||||
|
params.url.substringBefore(NetworkConstants.URI_PUSH_GATEWAY_PREFIX_PATH)
|
||||||
|
)
|
||||||
|
.create(PushGatewayAPI::class.java)
|
||||||
|
|
||||||
|
val response = executeRequest<PushGatewayNotifyResponse>(null) {
|
||||||
|
apiCall = sygnalApi.notify(
|
||||||
|
PushGatewayNotifyBody(
|
||||||
|
PushGatewayNotification(
|
||||||
|
eventId = params.eventId,
|
||||||
|
devices = listOf(
|
||||||
|
PushGatewayDevice(
|
||||||
|
params.appId,
|
||||||
|
params.pushKey
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.rejectedPushKeys.contains(params.pushKey)) {
|
||||||
|
throw PushGatewayFailure.PusherRejected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Some doc
|
||||||
|
# https://firebase.google.com/docs/cloud-messaging/android/first-message
|
||||||
|
# http://bulkpush.com/pushnotification/guidedetail/s-4/android-gcm-api-configuration
|
||||||
|
# http://www.feelzdroid.com/2016/02/android-google-cloud-messaging-push-notifications-gcm-tutorial.html
|
||||||
|
|
||||||
|
# Ask for parameter
|
||||||
|
read -p "Enter the server API key: " SERVER_KEY
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Check validity of API key, InvalidRegistration error is OK"
|
||||||
|
# https://developers.google.com/cloud-messaging/http
|
||||||
|
|
||||||
|
curl -H "Authorization: key=$SERVER_KEY" \
|
||||||
|
-H Content-Type:"application/json" \
|
||||||
|
-d "{\"registration_ids\":[\"ABC\"]}" \
|
||||||
|
-s \
|
||||||
|
https://fcm.googleapis.com/fcm/send \
|
||||||
|
| python -m json.tool
|
||||||
|
|
||||||
|
# should obtain something like this:
|
||||||
|
# {"multicast_id":5978845027639121780,"success":0,"failure":1,"canonical_ids":0,"results":[{"error":"InvalidRegistration"}]}
|
||||||
|
|
||||||
|
read -p "Enter the FCM token: " FCM_TOKEN
|
||||||
|
|
||||||
|
# content of the notification
|
||||||
|
DATA='{"event_id":"$THIS_IS_A_FAKE_EVENT_ID"}'
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
echo "Send a push, you should see success:1..."
|
||||||
|
|
||||||
|
curl -H "Authorization: key=$SERVER_KEY" \
|
||||||
|
-H Content-Type:"application/json" \
|
||||||
|
-d "{ \"data\" : $DATA, \"to\":\"$FCM_TOKEN\" }" \
|
||||||
|
-s \
|
||||||
|
https://fcm.googleapis.com/fcm/send \
|
||||||
|
| python -m json.tool
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
|
||||||
|
# should obtain something like this:
|
||||||
|
# {"multicast_id":7967233883611790812,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1472636210339069%84ac25d9f9fd7ecd"}]}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
# Copy and adaptation of ./test_push.sh, which takes 2 params: server key and fcm token.
|
||||||
|
# It's unsafe to use it because it takes server key as parameter, that will remain in the terminal history.
|
||||||
|
|
||||||
|
# Some doc
|
||||||
|
# https://firebase.google.com/docs/cloud-messaging/android/first-message
|
||||||
|
# http://bulkpush.com/pushnotification/guidedetail/s-4/android-gcm-api-configuration
|
||||||
|
# http://www.feelzdroid.com/2016/02/android-google-cloud-messaging-push-notifications-gcm-tutorial.html
|
||||||
|
|
||||||
|
if [[ "$#" -ne 2 ]]; then
|
||||||
|
echo "Usage: $0 SERVER_KEY FCM_TOKEN" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the command line parameters
|
||||||
|
SERVER_KEY=$1
|
||||||
|
FCM_TOKEN=$2
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Check validity of API key, InvalidRegistration error is OK"
|
||||||
|
# https://developers.google.com/cloud-messaging/http
|
||||||
|
|
||||||
|
curl -H "Authorization: key=$SERVER_KEY" \
|
||||||
|
-H Content-Type:"application/json" \
|
||||||
|
-d "{\"registration_ids\":[\"ABC\"]}" \
|
||||||
|
-s \
|
||||||
|
https://fcm.googleapis.com/fcm/send \
|
||||||
|
| python -m json.tool
|
||||||
|
|
||||||
|
# should obtain something like this:
|
||||||
|
# {"multicast_id":5978845027639121780,"success":0,"failure":1,"canonical_ids":0,"results":[{"error":"InvalidRegistration"}]}
|
||||||
|
|
||||||
|
# content of the notification
|
||||||
|
DATA='{"event_id":"$THIS_IS_A_FAKE_EVENT_ID"}'
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
echo "Send a push, you should see success:1..."
|
||||||
|
|
||||||
|
curl -H "Authorization: key=$SERVER_KEY" \
|
||||||
|
-H Content-Type:"application/json" \
|
||||||
|
-d "{ \"data\" : $DATA, \"to\":\"$FCM_TOKEN\" }" \
|
||||||
|
-s \
|
||||||
|
https://fcm.googleapis.com/fcm/send \
|
||||||
|
| python -m json.tool
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo
|
||||||
|
|
||||||
|
# should obtain something like this:
|
||||||
|
# {"multicast_id":7967233883611790812,"success":1,"failure":0,"canonical_ids":0,"results":[{"message_id":"0:1472636210339069%84ac25d9f9fd7ecd"}]}
|
||||||
|
|
|
@ -22,17 +22,21 @@ import im.vector.app.fdroid.features.settings.troubleshoot.TestBatteryOptimizati
|
||||||
import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager
|
import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager
|
||||||
import im.vector.app.features.settings.troubleshoot.TestAccountSettings
|
import im.vector.app.features.settings.troubleshoot.TestAccountSettings
|
||||||
import im.vector.app.features.settings.troubleshoot.TestDeviceSettings
|
import im.vector.app.features.settings.troubleshoot.TestDeviceSettings
|
||||||
|
import im.vector.app.features.settings.troubleshoot.TestNotification
|
||||||
import im.vector.app.features.settings.troubleshoot.TestPushRulesSettings
|
import im.vector.app.features.settings.troubleshoot.TestPushRulesSettings
|
||||||
import im.vector.app.features.settings.troubleshoot.TestSystemSettings
|
import im.vector.app.features.settings.troubleshoot.TestSystemSettings
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NotificationTroubleshootTestManagerFactory @Inject constructor(private val testSystemSettings: TestSystemSettings,
|
class NotificationTroubleshootTestManagerFactory @Inject constructor(
|
||||||
private val testAccountSettings: TestAccountSettings,
|
private val testSystemSettings: TestSystemSettings,
|
||||||
private val testDeviceSettings: TestDeviceSettings,
|
private val testAccountSettings: TestAccountSettings,
|
||||||
private val testPushRulesSettings: TestPushRulesSettings,
|
private val testDeviceSettings: TestDeviceSettings,
|
||||||
private val testAutoStartBoot: TestAutoStartBoot,
|
private val testPushRulesSettings: TestPushRulesSettings,
|
||||||
private val testBackgroundRestrictions: TestBackgroundRestrictions,
|
private val testAutoStartBoot: TestAutoStartBoot,
|
||||||
private val testBatteryOptimization: TestBatteryOptimization) {
|
private val testBackgroundRestrictions: TestBackgroundRestrictions,
|
||||||
|
private val testBatteryOptimization: TestBatteryOptimization,
|
||||||
|
private val testNotification: TestNotification
|
||||||
|
) {
|
||||||
|
|
||||||
fun create(fragment: Fragment): NotificationTroubleshootTestManager {
|
fun create(fragment: Fragment): NotificationTroubleshootTestManager {
|
||||||
val mgr = NotificationTroubleshootTestManager(fragment)
|
val mgr = NotificationTroubleshootTestManager(fragment)
|
||||||
|
@ -43,6 +47,7 @@ class NotificationTroubleshootTestManagerFactory @Inject constructor(private val
|
||||||
mgr.addTest(testAutoStartBoot)
|
mgr.addTest(testAutoStartBoot)
|
||||||
mgr.addTest(testBackgroundRestrictions)
|
mgr.addTest(testBackgroundRestrictions)
|
||||||
mgr.addTest(testBatteryOptimization)
|
mgr.addTest(testBatteryOptimization)
|
||||||
|
mgr.addTest(testNotification)
|
||||||
return mgr
|
return mgr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright 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 im.vector.app.gplay.features.settings.troubleshoot
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.error.ErrorFormatter
|
||||||
|
import im.vector.app.core.pushers.PushersManager
|
||||||
|
import im.vector.app.core.resources.StringProvider
|
||||||
|
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
|
||||||
|
import im.vector.app.push.fcm.FcmHelper
|
||||||
|
import org.matrix.android.sdk.api.MatrixCallback
|
||||||
|
import org.matrix.android.sdk.api.session.pushers.PushGatewayFailure
|
||||||
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test Push by asking the Push Gateway to send a Push back
|
||||||
|
*/
|
||||||
|
class TestPushFromPushGateway @Inject constructor(private val context: AppCompatActivity,
|
||||||
|
private val stringProvider: StringProvider,
|
||||||
|
private val errorFormatter: ErrorFormatter,
|
||||||
|
private val pushersManager: PushersManager)
|
||||||
|
: TroubleshootTest(R.string.settings_troubleshoot_test_push_loop_title) {
|
||||||
|
|
||||||
|
private var action: Cancelable? = null
|
||||||
|
|
||||||
|
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
|
val fcmToken = FcmHelper.getFcmToken(context) ?: run {
|
||||||
|
status = TestStatus.FAILED
|
||||||
|
return
|
||||||
|
}
|
||||||
|
action = pushersManager.testPush(fcmToken, object : MatrixCallback<Unit> {
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
description = if (failure is PushGatewayFailure.PusherRejected) {
|
||||||
|
stringProvider.getString(R.string.settings_troubleshoot_test_push_loop_failed)
|
||||||
|
} else {
|
||||||
|
errorFormatter.toHumanReadable(failure)
|
||||||
|
}
|
||||||
|
status = TestStatus.FAILED
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSuccess(data: Unit) {
|
||||||
|
// Wait for the push to be received
|
||||||
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_push_loop_waiting_for_push)
|
||||||
|
status = TestStatus.RUNNING
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPushReceived() {
|
||||||
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_push_loop_success)
|
||||||
|
status = TestStatus.SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun cancel() {
|
||||||
|
action?.cancel()
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,10 +19,12 @@
|
||||||
|
|
||||||
package im.vector.app.gplay.push.fcm
|
package im.vector.app.gplay.push.fcm
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.ProcessLifecycleOwner
|
import androidx.lifecycle.ProcessLifecycleOwner
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
import com.google.firebase.messaging.FirebaseMessagingService
|
import com.google.firebase.messaging.FirebaseMessagingService
|
||||||
import com.google.firebase.messaging.RemoteMessage
|
import com.google.firebase.messaging.RemoteMessage
|
||||||
import im.vector.app.BuildConfig
|
import im.vector.app.BuildConfig
|
||||||
|
@ -34,6 +36,7 @@ import im.vector.app.features.badge.BadgeProxy
|
||||||
import im.vector.app.features.notifications.NotifiableEventResolver
|
import im.vector.app.features.notifications.NotifiableEventResolver
|
||||||
import im.vector.app.features.notifications.NotifiableMessageEvent
|
import im.vector.app.features.notifications.NotifiableMessageEvent
|
||||||
import im.vector.app.features.notifications.NotificationDrawerManager
|
import im.vector.app.features.notifications.NotificationDrawerManager
|
||||||
|
import im.vector.app.features.notifications.NotificationUtils
|
||||||
import im.vector.app.features.notifications.SimpleNotifiableEvent
|
import im.vector.app.features.notifications.SimpleNotifiableEvent
|
||||||
import im.vector.app.features.settings.VectorPreferences
|
import im.vector.app.features.settings.VectorPreferences
|
||||||
import im.vector.app.push.fcm.FcmHelper
|
import im.vector.app.push.fcm.FcmHelper
|
||||||
|
@ -60,11 +63,13 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
notificationDrawerManager = vectorComponent().notificationDrawerManager()
|
with(vectorComponent()) {
|
||||||
notifiableEventResolver = vectorComponent().notifiableEventResolver()
|
notificationDrawerManager = notificationDrawerManager()
|
||||||
pusherManager = vectorComponent().pusherManager()
|
notifiableEventResolver = notifiableEventResolver()
|
||||||
activeSessionHolder = vectorComponent().activeSessionHolder()
|
pusherManager = pusherManager()
|
||||||
vectorPreferences = vectorComponent().vectorPreferences()
|
activeSessionHolder = activeSessionHolder()
|
||||||
|
vectorPreferences = vectorPreferences()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -73,6 +78,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) {
|
||||||
|
val intent = Intent(NotificationUtils.PUSH_ACTION)
|
||||||
|
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
|
||||||
|
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
|
||||||
|
|
|
@ -19,20 +19,26 @@ import androidx.fragment.app.Fragment
|
||||||
import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager
|
import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager
|
||||||
import im.vector.app.features.settings.troubleshoot.TestAccountSettings
|
import im.vector.app.features.settings.troubleshoot.TestAccountSettings
|
||||||
import im.vector.app.features.settings.troubleshoot.TestDeviceSettings
|
import im.vector.app.features.settings.troubleshoot.TestDeviceSettings
|
||||||
|
import im.vector.app.features.settings.troubleshoot.TestNotification
|
||||||
import im.vector.app.features.settings.troubleshoot.TestPushRulesSettings
|
import im.vector.app.features.settings.troubleshoot.TestPushRulesSettings
|
||||||
import im.vector.app.features.settings.troubleshoot.TestSystemSettings
|
import im.vector.app.features.settings.troubleshoot.TestSystemSettings
|
||||||
import im.vector.app.gplay.features.settings.troubleshoot.TestFirebaseToken
|
import im.vector.app.gplay.features.settings.troubleshoot.TestFirebaseToken
|
||||||
import im.vector.app.gplay.features.settings.troubleshoot.TestPlayServices
|
import im.vector.app.gplay.features.settings.troubleshoot.TestPlayServices
|
||||||
|
import im.vector.app.gplay.features.settings.troubleshoot.TestPushFromPushGateway
|
||||||
import im.vector.app.gplay.features.settings.troubleshoot.TestTokenRegistration
|
import im.vector.app.gplay.features.settings.troubleshoot.TestTokenRegistration
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class NotificationTroubleshootTestManagerFactory @Inject constructor(private val testSystemSettings: TestSystemSettings,
|
class NotificationTroubleshootTestManagerFactory @Inject constructor(
|
||||||
private val testAccountSettings: TestAccountSettings,
|
private val testSystemSettings: TestSystemSettings,
|
||||||
private val testDeviceSettings: TestDeviceSettings,
|
private val testAccountSettings: TestAccountSettings,
|
||||||
private val testBingRulesSettings: TestPushRulesSettings,
|
private val testDeviceSettings: TestDeviceSettings,
|
||||||
private val testPlayServices: TestPlayServices,
|
private val testBingRulesSettings: TestPushRulesSettings,
|
||||||
private val testFirebaseToken: TestFirebaseToken,
|
private val testPlayServices: TestPlayServices,
|
||||||
private val testTokenRegistration: TestTokenRegistration) {
|
private val testFirebaseToken: TestFirebaseToken,
|
||||||
|
private val testTokenRegistration: TestTokenRegistration,
|
||||||
|
private val testPushFromPushGateway: TestPushFromPushGateway,
|
||||||
|
private val testNotification: TestNotification
|
||||||
|
) {
|
||||||
|
|
||||||
fun create(fragment: Fragment): NotificationTroubleshootTestManager {
|
fun create(fragment: Fragment): NotificationTroubleshootTestManager {
|
||||||
val mgr = NotificationTroubleshootTestManager(fragment)
|
val mgr = NotificationTroubleshootTestManager(fragment)
|
||||||
|
@ -43,6 +49,8 @@ class NotificationTroubleshootTestManagerFactory @Inject constructor(private val
|
||||||
mgr.addTest(testPlayServices)
|
mgr.addTest(testPlayServices)
|
||||||
mgr.addTest(testFirebaseToken)
|
mgr.addTest(testFirebaseToken)
|
||||||
mgr.addTest(testTokenRegistration)
|
mgr.addTest(testTokenRegistration)
|
||||||
|
mgr.addTest(testPushFromPushGateway)
|
||||||
|
mgr.addTest(testNotification)
|
||||||
return mgr
|
return mgr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,6 +254,10 @@
|
||||||
android:name=".features.call.service.CallHeadsUpActionReceiver"
|
android:name=".features.call.service.CallHeadsUpActionReceiver"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".features.settings.troubleshoot.TestNotificationReceiver"
|
||||||
|
android:exported="false" />
|
||||||
|
|
||||||
<!-- Exported false, should only be accessible from this app!! -->
|
<!-- Exported false, should only be accessible from this app!! -->
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".features.notifications.NotificationBroadcastReceiver"
|
android:name=".features.notifications.NotificationBroadcastReceiver"
|
||||||
|
|
|
@ -24,7 +24,6 @@ import org.matrix.android.sdk.api.failure.isInvalidPassword
|
||||||
import org.matrix.android.sdk.api.session.identity.IdentityServiceError
|
import org.matrix.android.sdk.api.session.identity.IdentityServiceError
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.net.SocketTimeoutException
|
import java.net.SocketTimeoutException
|
||||||
import java.net.UnknownHostException
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.net.ssl.SSLException
|
import javax.net.ssl.SSLException
|
||||||
import javax.net.ssl.SSLPeerUnverifiedException
|
import javax.net.ssl.SSLPeerUnverifiedException
|
||||||
|
@ -45,15 +44,12 @@ class DefaultErrorFormatter @Inject constructor(
|
||||||
when (throwable.ioException) {
|
when (throwable.ioException) {
|
||||||
is SocketTimeoutException ->
|
is SocketTimeoutException ->
|
||||||
stringProvider.getString(R.string.error_network_timeout)
|
stringProvider.getString(R.string.error_network_timeout)
|
||||||
is UnknownHostException ->
|
|
||||||
// Invalid homeserver?
|
|
||||||
// TODO Check network state, airplane mode, etc.
|
|
||||||
stringProvider.getString(R.string.login_error_unknown_host)
|
|
||||||
is SSLPeerUnverifiedException ->
|
is SSLPeerUnverifiedException ->
|
||||||
stringProvider.getString(R.string.login_error_ssl_peer_unverified)
|
stringProvider.getString(R.string.login_error_ssl_peer_unverified)
|
||||||
is SSLException ->
|
is SSLException ->
|
||||||
stringProvider.getString(R.string.login_error_ssl_other)
|
stringProvider.getString(R.string.login_error_ssl_other)
|
||||||
else ->
|
else ->
|
||||||
|
// TODO Check network state, airplane mode, etc.
|
||||||
stringProvider.getString(R.string.error_no_network)
|
stringProvider.getString(R.string.error_no_network)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import im.vector.app.core.resources.AppNameProvider
|
||||||
import im.vector.app.core.resources.LocaleProvider
|
import im.vector.app.core.resources.LocaleProvider
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
import org.matrix.android.sdk.api.MatrixCallback
|
import org.matrix.android.sdk.api.MatrixCallback
|
||||||
|
import org.matrix.android.sdk.api.util.Cancelable
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
@ -34,6 +35,17 @@ class PushersManager @Inject constructor(
|
||||||
private val stringProvider: StringProvider,
|
private val stringProvider: StringProvider,
|
||||||
private val appNameProvider: AppNameProvider
|
private val appNameProvider: AppNameProvider
|
||||||
) {
|
) {
|
||||||
|
fun testPush(pushKey: String, callback: MatrixCallback<Unit>): Cancelable {
|
||||||
|
val currentSession = activeSessionHolder.getActiveSession()
|
||||||
|
|
||||||
|
return currentSession.testPush(
|
||||||
|
stringProvider.getString(R.string.pusher_http_url),
|
||||||
|
stringProvider.getString(R.string.pusher_app_id),
|
||||||
|
pushKey,
|
||||||
|
TEST_EVENT_ID,
|
||||||
|
callback
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun registerPusherWithFcmKey(pushKey: String): UUID {
|
fun registerPusherWithFcmKey(pushKey: String): UUID {
|
||||||
val currentSession = activeSessionHolder.getActiveSession()
|
val currentSession = activeSessionHolder.getActiveSession()
|
||||||
|
@ -56,4 +68,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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,6 @@ import android.provider.Settings
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
@ -97,15 +96,15 @@ fun copyToClipboard(context: Context, text: CharSequence, showToast: Boolean = t
|
||||||
* Shows notification settings for the current app.
|
* Shows notification settings for the current app.
|
||||||
* In android O will directly opens the notification settings, in lower version it will show the App settings
|
* In android O will directly opens the notification settings, in lower version it will show the App settings
|
||||||
*/
|
*/
|
||||||
fun startNotificationSettingsIntent(activity: AppCompatActivity, activityResultLauncher: ActivityResultLauncher<Intent>) {
|
fun startNotificationSettingsIntent(context: Context, activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
val intent = Intent()
|
val intent = Intent()
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
||||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, activity.packageName)
|
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
|
||||||
} else {
|
} else {
|
||||||
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
|
||||||
intent.putExtra("app_package", activity.packageName)
|
intent.putExtra("app_package", context.packageName)
|
||||||
intent.putExtra("app_uid", activity.applicationInfo?.uid)
|
intent.putExtra("app_uid", context.applicationInfo?.uid)
|
||||||
}
|
}
|
||||||
activityResultLauncher.launch(intent)
|
activityResultLauncher.launch(intent)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@ import im.vector.app.core.extensions.hideKeyboard
|
||||||
import im.vector.app.core.utils.ensureProtocol
|
import im.vector.app.core.utils.ensureProtocol
|
||||||
import im.vector.app.core.utils.openUrlInChromeCustomTab
|
import im.vector.app.core.utils.openUrlInChromeCustomTab
|
||||||
import kotlinx.android.synthetic.main.fragment_login_server_url_form.*
|
import kotlinx.android.synthetic.main.fragment_login_server_url_form.*
|
||||||
|
import org.matrix.android.sdk.api.failure.Failure
|
||||||
|
import java.net.UnknownHostException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -115,7 +117,13 @@ class LoginServerUrlFormFragment @Inject constructor() : AbstractLoginFragment()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(throwable: Throwable) {
|
override fun onError(throwable: Throwable) {
|
||||||
loginServerUrlFormHomeServerUrlTil.error = errorFormatter.toHumanReadable(throwable)
|
loginServerUrlFormHomeServerUrlTil.error = if (throwable is Failure.NetworkConnection
|
||||||
|
&& throwable.ioException is UnknownHostException) {
|
||||||
|
// Invalid homeserver?
|
||||||
|
getString(R.string.login_error_homeserver_not_found)
|
||||||
|
} else {
|
||||||
|
errorFormatter.toHumanReadable(throwable)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateWithState(state: LoginViewState) {
|
override fun updateWithState(state: LoginViewState) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,8 +50,8 @@ 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 im.vector.app.features.settings.troubleshoot.TestNotificationReceiver
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
@ -61,7 +64,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 {
|
||||||
|
@ -89,6 +91,8 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
||||||
const val DISMISS_SUMMARY_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DISMISS_SUMMARY_ACTION"
|
const val DISMISS_SUMMARY_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DISMISS_SUMMARY_ACTION"
|
||||||
const val DISMISS_ROOM_NOTIF_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DISMISS_ROOM_NOTIF_ACTION"
|
const val DISMISS_ROOM_NOTIF_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DISMISS_ROOM_NOTIF_ACTION"
|
||||||
private const val TAP_TO_VIEW_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.TAP_TO_VIEW_ACTION"
|
private const val TAP_TO_VIEW_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.TAP_TO_VIEW_ACTION"
|
||||||
|
const val DIAGNOSTIC_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DIAGNOSTIC"
|
||||||
|
const val PUSH_ACTION = "${BuildConfig.APPLICATION_ID}.PUSH"
|
||||||
|
|
||||||
/* ==========================================================================================
|
/* ==========================================================================================
|
||||||
* IDs for channels
|
* IDs for channels
|
||||||
|
@ -845,6 +849,43 @@ class NotificationUtils @Inject constructor(private val context: Context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun displayDiagnosticNotification() {
|
||||||
|
val testActionIntent = Intent(context, TestNotificationReceiver::class.java)
|
||||||
|
testActionIntent.action = DIAGNOSTIC_ACTION
|
||||||
|
val testPendingIntent = PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
0,
|
||||||
|
testActionIntent,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
.setContentIntent(testPendingIntent)
|
||||||
|
.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
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -16,12 +16,16 @@
|
||||||
package im.vector.app.features.settings
|
package im.vector.app.features.settings
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
@ -32,10 +36,13 @@ import im.vector.app.core.extensions.cleanup
|
||||||
import im.vector.app.core.extensions.registerStartForActivityResult
|
import im.vector.app.core.extensions.registerStartForActivityResult
|
||||||
import im.vector.app.core.platform.VectorBaseActivity
|
import im.vector.app.core.platform.VectorBaseActivity
|
||||||
import im.vector.app.core.platform.VectorBaseFragment
|
import im.vector.app.core.platform.VectorBaseFragment
|
||||||
|
import im.vector.app.features.notifications.NotificationUtils
|
||||||
import im.vector.app.features.rageshake.BugReporter
|
import im.vector.app.features.rageshake.BugReporter
|
||||||
import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager
|
import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager
|
||||||
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
|
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
|
||||||
import im.vector.app.push.fcm.NotificationTroubleshootTestManagerFactory
|
import im.vector.app.push.fcm.NotificationTroubleshootTestManagerFactory
|
||||||
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
|
class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
|
||||||
|
@ -45,12 +52,16 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
|
||||||
|
|
||||||
@BindView(R.id.troubleshoot_test_recycler_view)
|
@BindView(R.id.troubleshoot_test_recycler_view)
|
||||||
lateinit var mRecyclerView: RecyclerView
|
lateinit var mRecyclerView: RecyclerView
|
||||||
|
|
||||||
@BindView(R.id.troubleshoot_bottom_view)
|
@BindView(R.id.troubleshoot_bottom_view)
|
||||||
lateinit var mBottomView: ViewGroup
|
lateinit var mBottomView: ViewGroup
|
||||||
|
|
||||||
@BindView(R.id.toubleshoot_summ_description)
|
@BindView(R.id.toubleshoot_summ_description)
|
||||||
lateinit var mSummaryDescription: TextView
|
lateinit var mSummaryDescription: TextView
|
||||||
|
|
||||||
@BindView(R.id.troubleshoot_summ_button)
|
@BindView(R.id.troubleshoot_summ_button)
|
||||||
lateinit var mSummaryButton: Button
|
lateinit var mSummaryButton: Button
|
||||||
|
|
||||||
@BindView(R.id.troubleshoot_run_button)
|
@BindView(R.id.troubleshoot_run_button)
|
||||||
lateinit var mRunButton: Button
|
lateinit var mRunButton: Button
|
||||||
|
|
||||||
|
@ -82,8 +93,7 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startUI() {
|
private fun startUI() {
|
||||||
mSummaryDescription.text = getString(R.string.settings_troubleshoot_diagnostic_running_status,
|
mSummaryDescription.text = getString(R.string.settings_troubleshoot_diagnostic_running_status, 0, 0)
|
||||||
0, 0)
|
|
||||||
testManager = testManagerFactory.create(this)
|
testManager = testManagerFactory.create(this)
|
||||||
testManager?.statusListener = { troubleshootTestManager ->
|
testManager?.statusListener = { troubleshootTestManager ->
|
||||||
if (isAdded) {
|
if (isAdded) {
|
||||||
|
@ -94,10 +104,10 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
|
||||||
mSummaryButton.visibility = View.GONE
|
mSummaryButton.visibility = View.GONE
|
||||||
mRunButton.visibility = View.VISIBLE
|
mRunButton.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
TroubleshootTest.TestStatus.RUNNING -> {
|
TroubleshootTest.TestStatus.RUNNING,
|
||||||
// Forces int type because it's breaking lint
|
TroubleshootTest.TestStatus.WAITING_FOR_USER -> {
|
||||||
val size: Int = troubleshootTestManager.testList.size
|
val size = troubleshootTestManager.testListSize
|
||||||
val currentTestIndex: Int = troubleshootTestManager.currentTestIndex
|
val currentTestIndex = troubleshootTestManager.currentTestIndex
|
||||||
mSummaryDescription.text = getString(
|
mSummaryDescription.text = getString(
|
||||||
R.string.settings_troubleshoot_diagnostic_running_status,
|
R.string.settings_troubleshoot_diagnostic_running_status,
|
||||||
currentTestIndex,
|
currentTestIndex,
|
||||||
|
@ -108,15 +118,7 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
TroubleshootTest.TestStatus.FAILED -> {
|
TroubleshootTest.TestStatus.FAILED -> {
|
||||||
// check if there are quick fixes
|
// check if there are quick fixes
|
||||||
var hasQuickFix = false
|
val hasQuickFix = testManager?.hasQuickFix().orFalse()
|
||||||
testManager?.testList?.let {
|
|
||||||
for (test in it) {
|
|
||||||
if (test.status == TroubleshootTest.TestStatus.FAILED && test.quickFix != null) {
|
|
||||||
hasQuickFix = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasQuickFix) {
|
if (hasQuickFix) {
|
||||||
mSummaryDescription.text = getString(R.string.settings_troubleshoot_diagnostic_failure_status_with_quickfix)
|
mSummaryDescription.text = getString(R.string.settings_troubleshoot_diagnostic_failure_status_with_quickfix)
|
||||||
} else {
|
} else {
|
||||||
|
@ -161,6 +163,39 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_notification_troubleshoot)
|
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_notification_troubleshoot)
|
||||||
|
|
||||||
|
tryOrNull("Unable to register the receiver") {
|
||||||
|
LocalBroadcastManager.getInstance(requireContext())
|
||||||
|
.registerReceiver(broadcastReceiverPush, IntentFilter(NotificationUtils.PUSH_ACTION))
|
||||||
|
}
|
||||||
|
tryOrNull("Unable to register the receiver") {
|
||||||
|
LocalBroadcastManager.getInstance(requireContext())
|
||||||
|
.registerReceiver(broadcastReceiverNotification, IntentFilter(NotificationUtils.DIAGNOSTIC_ACTION))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
tryOrNull {
|
||||||
|
LocalBroadcastManager.getInstance(requireContext())
|
||||||
|
.unregisterReceiver(broadcastReceiverPush)
|
||||||
|
}
|
||||||
|
tryOrNull {
|
||||||
|
LocalBroadcastManager.getInstance(requireContext())
|
||||||
|
.unregisterReceiver(broadcastReceiverNotification)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val broadcastReceiverPush = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
testManager?.onDiagnosticPushReceived()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val broadcastReceiverNotification = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
testManager?.onDiagnosticNotificationClicked()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAttach(context: Context) {
|
override fun onAttach(context: Context) {
|
||||||
|
|
|
@ -77,6 +77,16 @@ class NotificationTroubleshootRecyclerViewAdapter(val tests: ArrayList<Troublesh
|
||||||
statusIconImage.visibility = View.VISIBLE
|
statusIconImage.visibility = View.VISIBLE
|
||||||
statusIconImage.setImageResource(R.drawable.unit_test)
|
statusIconImage.setImageResource(R.drawable.unit_test)
|
||||||
}
|
}
|
||||||
|
TroubleshootTest.TestStatus.WAITING_FOR_USER -> {
|
||||||
|
progressBar.visibility = View.INVISIBLE
|
||||||
|
statusIconImage.visibility = View.VISIBLE
|
||||||
|
val infoColor = ContextCompat.getColor(context, R.color.vector_info_color)
|
||||||
|
val drawable = ContextCompat.getDrawable(itemView.context, R.drawable.ic_notification_privacy_warning)?.apply {
|
||||||
|
ThemeUtils.tintDrawableWithColor(this, infoColor)
|
||||||
|
}
|
||||||
|
statusIconImage.setImageDrawable(drawable)
|
||||||
|
descriptionText.setTextColor(infoColor)
|
||||||
|
}
|
||||||
TroubleshootTest.TestStatus.RUNNING -> {
|
TroubleshootTest.TestStatus.RUNNING -> {
|
||||||
progressBar.visibility = View.VISIBLE
|
progressBar.visibility = View.VISIBLE
|
||||||
statusIconImage.visibility = View.INVISIBLE
|
statusIconImage.visibility = View.INVISIBLE
|
||||||
|
|
|
@ -23,13 +23,19 @@ import androidx.fragment.app.Fragment
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
class NotificationTroubleshootTestManager(val fragment: Fragment) {
|
class NotificationTroubleshootTestManager(val fragment: Fragment) {
|
||||||
|
private val testList = ArrayList<TroubleshootTest>()
|
||||||
|
|
||||||
|
val testListSize: Int
|
||||||
|
get() = testList.size
|
||||||
|
|
||||||
val testList = ArrayList<TroubleshootTest>()
|
|
||||||
var isCancelled = false
|
var isCancelled = false
|
||||||
|
private set
|
||||||
|
|
||||||
var currentTestIndex by Delegates.observable(0) { _, _, _ ->
|
var currentTestIndex by Delegates.observable(0) { _, _, _ ->
|
||||||
statusListener?.invoke(this)
|
statusListener?.invoke(this)
|
||||||
}
|
}
|
||||||
|
private set
|
||||||
|
|
||||||
val adapter = NotificationTroubleshootRecyclerViewAdapter(testList)
|
val adapter = NotificationTroubleshootRecyclerViewAdapter(testList)
|
||||||
|
|
||||||
var statusListener: ((NotificationTroubleshootTestManager) -> Unit)? = null
|
var statusListener: ((NotificationTroubleshootTestManager) -> Unit)? = null
|
||||||
|
@ -37,6 +43,7 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) {
|
||||||
var diagStatus: TroubleshootTest.TestStatus by Delegates.observable(TroubleshootTest.TestStatus.NOT_STARTED) { _, _, _ ->
|
var diagStatus: TroubleshootTest.TestStatus by Delegates.observable(TroubleshootTest.TestStatus.NOT_STARTED) { _, _, _ ->
|
||||||
statusListener?.invoke(this)
|
statusListener?.invoke(this)
|
||||||
}
|
}
|
||||||
|
private set
|
||||||
|
|
||||||
fun addTest(test: TroubleshootTest) {
|
fun addTest(test: TroubleshootTest) {
|
||||||
testList.add(test)
|
testList.add(test)
|
||||||
|
@ -79,19 +86,31 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun retry(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
fun retry(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
for (test in testList) {
|
testList.forEach {
|
||||||
test.cancel()
|
it.cancel()
|
||||||
test.description = null
|
it.description = null
|
||||||
test.quickFix = null
|
it.quickFix = null
|
||||||
test.status = TroubleshootTest.TestStatus.NOT_STARTED
|
it.status = TroubleshootTest.TestStatus.NOT_STARTED
|
||||||
}
|
}
|
||||||
runDiagnostic(activityResultLauncher)
|
runDiagnostic(activityResultLauncher)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancel() {
|
fun hasQuickFix(): Boolean {
|
||||||
isCancelled = true
|
return testList.any { test ->
|
||||||
for (test in testList) {
|
test.status == TroubleshootTest.TestStatus.FAILED && test.quickFix != null
|
||||||
test.cancel()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cancel() {
|
||||||
|
isCancelled = true
|
||||||
|
testList.forEach { it.cancel() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDiagnosticPushReceived() {
|
||||||
|
testList.forEach { it.onPushReceived() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onDiagnosticNotificationClicked() {
|
||||||
|
testList.forEach { it.onNotificationClicked() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2018 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.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
|
||||||
|
import im.vector.app.features.notifications.NotificationUtils
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if notifications can be displayed and clicked by the user
|
||||||
|
*/
|
||||||
|
class TestNotification @Inject constructor(private val context: Context,
|
||||||
|
private val notificationUtils: NotificationUtils,
|
||||||
|
private val stringProvider: StringProvider)
|
||||||
|
: TroubleshootTest(R.string.settings_troubleshoot_test_notification_title) {
|
||||||
|
|
||||||
|
override fun perform(activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status = TestStatus.WAITING_FOR_USER
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNotificationClicked() {
|
||||||
|
description = stringProvider.getString(R.string.settings_troubleshoot_test_notification_notification_clicked)
|
||||||
|
quickFix = null
|
||||||
|
status = TestStatus.SUCCESS
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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 im.vector.app.features.settings.troubleshoot
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager
|
||||||
|
|
||||||
|
class TestNotificationReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
// Internal broadcast to any one interested
|
||||||
|
LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,9 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package im.vector.app.features.settings.troubleshoot
|
package im.vector.app.features.settings.troubleshoot
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.core.app.NotificationManagerCompat
|
import androidx.core.app.NotificationManagerCompat
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.resources.StringProvider
|
import im.vector.app.core.resources.StringProvider
|
||||||
|
@ -27,7 +27,7 @@ import javax.inject.Inject
|
||||||
/**
|
/**
|
||||||
* Checks if notifications are enable in the system settings for this app.
|
* Checks if notifications are enable in the system settings for this app.
|
||||||
*/
|
*/
|
||||||
class TestSystemSettings @Inject constructor(private val context: AppCompatActivity,
|
class TestSystemSettings @Inject constructor(private val context: Context,
|
||||||
private val stringProvider: StringProvider)
|
private val stringProvider: StringProvider)
|
||||||
: TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) {
|
: TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) {
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ abstract class TroubleshootTest(@StringRes val titleResId: Int) {
|
||||||
enum class TestStatus {
|
enum class TestStatus {
|
||||||
NOT_STARTED,
|
NOT_STARTED,
|
||||||
RUNNING,
|
RUNNING,
|
||||||
|
WAITING_FOR_USER,
|
||||||
FAILED,
|
FAILED,
|
||||||
SUCCESS
|
SUCCESS
|
||||||
}
|
}
|
||||||
|
@ -51,4 +52,10 @@ abstract class TroubleshootTest(@StringRes val titleResId: Int) {
|
||||||
|
|
||||||
open fun cancel() {
|
open fun cancel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun onPushReceived() {
|
||||||
|
}
|
||||||
|
|
||||||
|
open fun onNotificationClicked() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/jitsi_layout"
|
android:id="@+id/jitsi_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@android:color/black">
|
android:background="@android:color/black">
|
||||||
|
|
||||||
<!-- Note: A org.jitsi.meet.sdk.JitsiMeetView will be added here -->
|
<!-- Note: A org.jitsi.meet.sdk.JitsiMeetView will be added here and so add tools:ignore="UselessParent" -->
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/jitsi_progress_layout"
|
android:id="@+id/jitsi_progress_layout"
|
||||||
|
@ -13,7 +14,8 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical"
|
||||||
|
tools:ignore="UselessParent">
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:background="@drawable/bg_attachment_type_selector"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:paddingTop="16dp"
|
|
||||||
android:paddingBottom="16dp">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_margin="16dp"
|
|
||||||
android:baselineAligned="false"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:weightSum="2">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/avatarCameraButton"
|
|
||||||
style="@style/AttachmentTypeSelectorButton"
|
|
||||||
android:contentDescription="@string/attachment_type_camera"
|
|
||||||
android:src="@drawable/ic_attachment_camera_white_24dp"
|
|
||||||
tools:background="@color/riotx_accent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/AttachmentTypeSelectorLabel"
|
|
||||||
android:importantForAccessibility="no"
|
|
||||||
android:text="@string/attachment_type_camera" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/avatarGalleryButton"
|
|
||||||
style="@style/AttachmentTypeSelectorButton"
|
|
||||||
android:contentDescription="@string/attachment_type_gallery"
|
|
||||||
android:src="@drawable/ic_attachment_gallery_white_24dp"
|
|
||||||
tools:background="@color/riotx_accent" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/AttachmentTypeSelectorLabel"
|
|
||||||
android:importantForAccessibility="no"
|
|
||||||
android:text="@string/attachment_type_gallery" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
|
@ -749,6 +749,15 @@
|
||||||
<string name="settings_troubleshoot_test_token_registration_success">FCM token successfully registered to HomeServer.</string>
|
<string name="settings_troubleshoot_test_token_registration_success">FCM token successfully registered to HomeServer.</string>
|
||||||
<string name="settings_troubleshoot_test_token_registration_failed">Failed to register FCM token to HomeServer:\n%1$s</string>
|
<string name="settings_troubleshoot_test_token_registration_failed">Failed to register FCM token to HomeServer:\n%1$s</string>
|
||||||
|
|
||||||
|
<string name="settings_troubleshoot_test_push_loop_title">Test Push</string>
|
||||||
|
<string name="settings_troubleshoot_test_push_loop_waiting_for_push">The application is waiting for the PUSH</string>
|
||||||
|
<string name="settings_troubleshoot_test_push_loop_success">The application is receiving PUSH</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 viewing the notification! Click me!</string>
|
||||||
|
<string name="settings_troubleshoot_test_notification_title">Notification Display</string>
|
||||||
|
<string name="settings_troubleshoot_test_notification_notice">Please click on the notification. If you do not see the notification, please check the system settings.</string>
|
||||||
|
<string name="settings_troubleshoot_test_notification_notification_clicked">The notification has been clicked!</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>
|
||||||
<string name="settings_troubleshoot_test_foreground_service_started_failed">Notifications Service is not running.\nTry to restart the application.</string>
|
<string name="settings_troubleshoot_test_foreground_service_started_failed">Notifications Service is not running.\nTry to restart the application.</string>
|
||||||
|
|
Loading…
Reference in New Issue