diff --git a/.idea/dictionaries/bmarty.xml b/.idea/dictionaries/bmarty.xml
index 5c27da044e..d13e40248f 100644
--- a/.idea/dictionaries/bmarty.xml
+++ b/.idea/dictionaries/bmarty.xml
@@ -29,6 +29,7 @@
signout
signup
ssss
+ sygnal
threepid
unwedging
diff --git a/CHANGES.md b/CHANGES.md
index 187d9ad8f4..534194c2a9 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -11,17 +11,23 @@ Improvements 🙌:
- Small optimisation of scrolling experience in timeline (#2114)
- Allow user to reset cross signing if he has no way to recover (#2052)
- Create home shortcut for any room (#1525)
+ - Can't confirm email due to killing by Android (#2021)
- Add a menu item to open the setting in room list and in room (#2171)
- Add a menu item in the timeline as a shortcut to invite user (#2171)
- Drawer: move settings access and add sign out action (#2171)
- Filter room member (and banned users) by name (#2184)
- Implement "Jump to read receipt" and "Mention" actions on the room member profile screen
- Add FAB to room members list (#2226)
+ - Add Sygnal API implementation to test is Push are correctly received
+ - Add PushGateway API implementation to test if Push are correctly received
+ - Cross signing: shouldn't offer to verify with other session when there is not. (#2227)
Bugfix 🐛:
- Improve support for image/audio/video/file selection with intent changes (#1376)
- Fix Splash layout on small screens
+ - Invalid popup when pressing back (#1635)
- Simplifies draft management and should fix bunch of draft issues (#952, #683)
+ - Very long topic cannot be fully visible (#1957)
Translations 🗣:
-
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushGatewayFailure.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushGatewayFailure.kt
new file mode 100644
index 0000000000..b15147161f
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushGatewayFailure.kt
@@ -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()
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt
index 0e33be400c..3993422b1d 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/pushers/PushersService.kt
@@ -66,6 +66,21 @@ interface PushersService {
append: Boolean,
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): Cancelable
+
/**
* Remove the http pusher
*/
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt
index 37886a7d77..c77ac70124 100755
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt
@@ -50,7 +50,7 @@ import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibility
import org.matrix.android.sdk.api.session.room.model.RoomHistoryVisibilityContent
-import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
+import org.matrix.android.sdk.api.session.room.model.RoomMemberContent
import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
import org.matrix.android.sdk.internal.crypto.actions.MegolmSessionDataImporter
import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter
@@ -953,7 +953,7 @@ internal class DefaultCryptoService @Inject constructor(
roomEncryptorsStore.get(roomId) ?: /* No encrypting in this room */ return
event.stateKey?.let { userId ->
- val roomMember: RoomMemberSummary? = event.content.toModel()
+ val roomMember: RoomMemberContent? = event.content.toModel()
val membership = roomMember?.membership
if (membership == Membership.JOIN) {
// make sure we are tracking the deviceList for this user.
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/SendToDeviceBody.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/SendToDeviceBody.kt
index e252abfb04..868f7aa943 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/SendToDeviceBody.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/model/rest/SendToDeviceBody.kt
@@ -16,6 +16,9 @@
package org.matrix.android.sdk.internal.crypto.model.rest
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
internal data class SendToDeviceBody(
/**
* `Any` should implement [SendToDeviceObject], but we cannot use interface here because of Json serialization
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConstants.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConstants.kt
index 1154d1967c..a14c86efb6 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConstants.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/NetworkConstants.kt
@@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.network
internal object NetworkConstants {
-
+ // Homeserver
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_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_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/"
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/TLSSocketFactory.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/TLSSocketFactory.kt
index a5c711a3f0..c4dbce6240 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/TLSSocketFactory.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/network/ssl/TLSSocketFactory.kt
@@ -113,8 +113,4 @@ constructor(trustPinned: Array, acceptedTlsVersions: List): Cancelable {
+ return pushGatewayNotifyTask
+ .configureWith(PushGatewayNotifyTask.Params(url, appId, pushkey, eventId)) {
+ this.callback = callback
+ }
+ .executeBy(taskExecutor)
+ }
+
override fun refreshPushers() {
getPusherTask
.configureWith()
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt
index a6042b27a2..4030c63514 100644
--- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/PushersModule.kt
@@ -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.DefaultPushRuleService
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.SetRoomNotificationStateTask
import retrofit2.Retrofit
@@ -86,4 +88,7 @@ internal abstract class PushersModule {
@Binds
abstract fun bindProcessEventForPushTask(task: DefaultProcessEventForPushTask): ProcessEventForPushTask
+
+ @Binds
+ abstract fun bindPushGatewayNotifyTask(task: DefaultPushGatewayNotifyTask): PushGatewayNotifyTask
}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayAPI.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayAPI.kt
new file mode 100644
index 0000000000..d95587fc22
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayAPI.kt
@@ -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
+}
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayDevice.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayDevice.kt
new file mode 100644
index 0000000000..5490fed7a7
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayDevice.kt
@@ -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
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayNotification.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayNotification.kt
new file mode 100644
index 0000000000..b9c8a35afc
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayNotification.kt
@@ -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
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayNotifyBody.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayNotifyBody.kt
new file mode 100644
index 0000000000..751414d2fe
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayNotifyBody.kt
@@ -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
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayNotifyResponse.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayNotifyResponse.kt
new file mode 100644
index 0000000000..50401ef6e9
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayNotifyResponse.kt
@@ -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
+)
diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayNotifyTask.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayNotifyTask.kt
new file mode 100644
index 0000000000..df6f46fa81
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/pushers/gateway/PushGatewayNotifyTask.kt
@@ -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 {
+ 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(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
+ }
+ }
+}
diff --git a/tools/tests/test_push.sh b/tools/tests/test_push.sh
new file mode 100755
index 0000000000..08348497ae
--- /dev/null
+++ b/tools/tests/test_push.sh
@@ -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"}]}
+
diff --git a/tools/tests/test_push_unsafe.sh b/tools/tests/test_push_unsafe.sh
new file mode 100755
index 0000000000..77c198197f
--- /dev/null
+++ b/tools/tests/test_push_unsafe.sh
@@ -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"}]}
+
diff --git a/vector/src/fdroid/java/im/vector/app/push/fcm/NotificationTroubleshootTestManagerFactory.kt b/vector/src/fdroid/java/im/vector/app/push/fcm/NotificationTroubleshootTestManagerFactory.kt
index 65b8609446..6236aad65c 100644
--- a/vector/src/fdroid/java/im/vector/app/push/fcm/NotificationTroubleshootTestManagerFactory.kt
+++ b/vector/src/fdroid/java/im/vector/app/push/fcm/NotificationTroubleshootTestManagerFactory.kt
@@ -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.TestAccountSettings
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.TestSystemSettings
import javax.inject.Inject
-class NotificationTroubleshootTestManagerFactory @Inject constructor(private val testSystemSettings: TestSystemSettings,
- private val testAccountSettings: TestAccountSettings,
- private val testDeviceSettings: TestDeviceSettings,
- private val testPushRulesSettings: TestPushRulesSettings,
- private val testAutoStartBoot: TestAutoStartBoot,
- private val testBackgroundRestrictions: TestBackgroundRestrictions,
- private val testBatteryOptimization: TestBatteryOptimization) {
+class NotificationTroubleshootTestManagerFactory @Inject constructor(
+ private val testSystemSettings: TestSystemSettings,
+ private val testAccountSettings: TestAccountSettings,
+ private val testDeviceSettings: TestDeviceSettings,
+ private val testPushRulesSettings: TestPushRulesSettings,
+ private val testAutoStartBoot: TestAutoStartBoot,
+ private val testBackgroundRestrictions: TestBackgroundRestrictions,
+ private val testBatteryOptimization: TestBatteryOptimization,
+ private val testNotification: TestNotification
+) {
fun create(fragment: Fragment): NotificationTroubleshootTestManager {
val mgr = NotificationTroubleshootTestManager(fragment)
@@ -43,6 +47,7 @@ class NotificationTroubleshootTestManagerFactory @Inject constructor(private val
mgr.addTest(testAutoStartBoot)
mgr.addTest(testBackgroundRestrictions)
mgr.addTest(testBatteryOptimization)
+ mgr.addTest(testNotification)
return mgr
}
}
diff --git a/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPushFromPushGateway.kt b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPushFromPushGateway.kt
new file mode 100644
index 0000000000..da93d54075
--- /dev/null
+++ b/vector/src/gplay/java/im/vector/app/gplay/features/settings/troubleshoot/TestPushFromPushGateway.kt
@@ -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) {
+ val fcmToken = FcmHelper.getFcmToken(context) ?: run {
+ status = TestStatus.FAILED
+ return
+ }
+ action = pushersManager.testPush(fcmToken, object : MatrixCallback {
+ 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()
+ }
+}
diff --git a/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt b/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt
index 8fdb65c8d0..cfd241d4f9 100755
--- a/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt
+++ b/vector/src/gplay/java/im/vector/app/gplay/push/fcm/VectorFirebaseMessagingService.kt
@@ -19,10 +19,12 @@
package im.vector.app.gplay.push.fcm
+import android.content.Intent
import android.os.Handler
import android.os.Looper
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ProcessLifecycleOwner
+import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
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.NotifiableMessageEvent
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.settings.VectorPreferences
import im.vector.app.push.fcm.FcmHelper
@@ -60,11 +63,13 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
override fun onCreate() {
super.onCreate()
- notificationDrawerManager = vectorComponent().notificationDrawerManager()
- notifiableEventResolver = vectorComponent().notifiableEventResolver()
- pusherManager = vectorComponent().pusherManager()
- activeSessionHolder = vectorComponent().activeSessionHolder()
- vectorPreferences = vectorComponent().vectorPreferences()
+ with(vectorComponent()) {
+ notificationDrawerManager = notificationDrawerManager()
+ notifiableEventResolver = notifiableEventResolver()
+ pusherManager = pusherManager()
+ activeSessionHolder = activeSessionHolder()
+ vectorPreferences = vectorPreferences()
+ }
}
/**
@@ -73,6 +78,13 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
* @param message the message
*/
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()) {
Timber.i("Notification are disabled for this device")
return
diff --git a/vector/src/gplay/java/im/vector/app/push/fcm/NotificationTroubleshootTestManagerFactory.kt b/vector/src/gplay/java/im/vector/app/push/fcm/NotificationTroubleshootTestManagerFactory.kt
index b2dad09483..e96c603e60 100644
--- a/vector/src/gplay/java/im/vector/app/push/fcm/NotificationTroubleshootTestManagerFactory.kt
+++ b/vector/src/gplay/java/im/vector/app/push/fcm/NotificationTroubleshootTestManagerFactory.kt
@@ -19,20 +19,26 @@ import androidx.fragment.app.Fragment
import im.vector.app.features.settings.troubleshoot.NotificationTroubleshootTestManager
import im.vector.app.features.settings.troubleshoot.TestAccountSettings
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.TestSystemSettings
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.TestPushFromPushGateway
import im.vector.app.gplay.features.settings.troubleshoot.TestTokenRegistration
import javax.inject.Inject
-class NotificationTroubleshootTestManagerFactory @Inject constructor(private val testSystemSettings: TestSystemSettings,
- private val testAccountSettings: TestAccountSettings,
- private val testDeviceSettings: TestDeviceSettings,
- private val testBingRulesSettings: TestPushRulesSettings,
- private val testPlayServices: TestPlayServices,
- private val testFirebaseToken: TestFirebaseToken,
- private val testTokenRegistration: TestTokenRegistration) {
+class NotificationTroubleshootTestManagerFactory @Inject constructor(
+ private val testSystemSettings: TestSystemSettings,
+ private val testAccountSettings: TestAccountSettings,
+ private val testDeviceSettings: TestDeviceSettings,
+ private val testBingRulesSettings: TestPushRulesSettings,
+ private val testPlayServices: TestPlayServices,
+ private val testFirebaseToken: TestFirebaseToken,
+ private val testTokenRegistration: TestTokenRegistration,
+ private val testPushFromPushGateway: TestPushFromPushGateway,
+ private val testNotification: TestNotification
+) {
fun create(fragment: Fragment): NotificationTroubleshootTestManager {
val mgr = NotificationTroubleshootTestManager(fragment)
@@ -43,6 +49,8 @@ class NotificationTroubleshootTestManagerFactory @Inject constructor(private val
mgr.addTest(testPlayServices)
mgr.addTest(testFirebaseToken)
mgr.addTest(testTokenRegistration)
+ mgr.addTest(testPushFromPushGateway)
+ mgr.addTest(testNotification)
return mgr
}
}
diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml
index 3f178a3d20..23e483f50b 100644
--- a/vector/src/main/AndroidManifest.xml
+++ b/vector/src/main/AndroidManifest.xml
@@ -94,9 +94,11 @@
+
+ android:theme="@style/AppTheme.Transparent"
+ tools:ignore="Instantiatable" />
+
+
() {
+
+ @EpoxyAttribute
+ lateinit var content: String
+
+ @EpoxyAttribute
+ var maxLines: Int = 3
+
+ private var isExpanded = false
+ private var expandedLines = 0
+
+ override fun bind(holder: Holder) {
+ super.bind(holder)
+ holder.content.text = content
+ holder.content.copyOnLongClick()
+
+ holder.content.doOnPreDraw {
+ if (holder.content.lineCount > maxLines) {
+ expandedLines = holder.content.lineCount
+ holder.content.maxLines = maxLines
+
+ holder.view.setOnClickListener {
+ if (isExpanded) {
+ collapse(holder)
+ } else {
+ expand(holder)
+ }
+ }
+ holder.arrow.isVisible = true
+ } else {
+ holder.arrow.isVisible = false
+ }
+ }
+ }
+
+ private fun expand(holder: Holder) {
+ ObjectAnimator
+ .ofInt(holder.content, "maxLines", expandedLines)
+ .setDuration(200)
+ .start()
+
+ holder.content.ellipsize = null
+ holder.arrow.setImageResource(R.drawable.ic_expand_less)
+ holder.arrow.contentDescription = holder.view.context.getString(R.string.merged_events_collapse)
+ isExpanded = true
+ }
+
+ private fun collapse(holder: Holder) {
+ ObjectAnimator
+ .ofInt(holder.content, "maxLines", maxLines)
+ .setDuration(200)
+ .start()
+
+ holder.content.ellipsize = TextUtils.TruncateAt.END
+ holder.arrow.setImageResource(R.drawable.ic_expand_more)
+ holder.arrow.contentDescription = holder.view.context.getString(R.string.merged_events_expand)
+ isExpanded = false
+ }
+
+ class Holder : VectorEpoxyHolder() {
+ val content by bind(R.id.expandableContent)
+ val arrow by bind(R.id.expandableArrow)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt b/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt
index 43395b97f7..1e7d662793 100644
--- a/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt
+++ b/vector/src/main/java/im/vector/app/core/error/ErrorFormatter.kt
@@ -24,7 +24,6 @@ import org.matrix.android.sdk.api.failure.isInvalidPassword
import org.matrix.android.sdk.api.session.identity.IdentityServiceError
import java.net.HttpURLConnection
import java.net.SocketTimeoutException
-import java.net.UnknownHostException
import javax.inject.Inject
import javax.net.ssl.SSLException
import javax.net.ssl.SSLPeerUnverifiedException
@@ -45,15 +44,12 @@ class DefaultErrorFormatter @Inject constructor(
when (throwable.ioException) {
is SocketTimeoutException ->
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 ->
stringProvider.getString(R.string.login_error_ssl_peer_unverified)
is SSLException ->
stringProvider.getString(R.string.login_error_ssl_other)
else ->
+ // TODO Check network state, airplane mode, etc.
stringProvider.getString(R.string.error_no_network)
}
}
diff --git a/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt b/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt
index f0d27182e7..5fe30141d9 100644
--- a/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt
+++ b/vector/src/main/java/im/vector/app/core/pushers/PushersManager.kt
@@ -22,6 +22,7 @@ import im.vector.app.core.resources.AppNameProvider
import im.vector.app.core.resources.LocaleProvider
import im.vector.app.core.resources.StringProvider
import org.matrix.android.sdk.api.MatrixCallback
+import org.matrix.android.sdk.api.util.Cancelable
import java.util.UUID
import javax.inject.Inject
import kotlin.math.abs
@@ -34,6 +35,17 @@ class PushersManager @Inject constructor(
private val stringProvider: StringProvider,
private val appNameProvider: AppNameProvider
) {
+ fun testPush(pushKey: String, callback: MatrixCallback): 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 {
val currentSession = activeSessionHolder.getActiveSession()
@@ -56,4 +68,8 @@ class PushersManager @Inject constructor(
val currentSession = activeSessionHolder.getSafeActiveSession() ?: return
currentSession.removeHttpPusher(pushKey, stringProvider.getString(R.string.pusher_app_id), callback)
}
+
+ companion object {
+ const val TEST_EVENT_ID = "\$THIS_IS_A_FAKE_EVENT_ID"
+ }
}
diff --git a/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt b/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt
index 5e722ae209..d228adab12 100644
--- a/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt
+++ b/vector/src/main/java/im/vector/app/core/utils/SystemUtils.kt
@@ -30,7 +30,6 @@ import android.provider.Settings
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.StringRes
-import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.getSystemService
import androidx.fragment.app.Fragment
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.
* In android O will directly opens the notification settings, in lower version it will show the App settings
*/
-fun startNotificationSettingsIntent(activity: AppCompatActivity, activityResultLauncher: ActivityResultLauncher) {
+fun startNotificationSettingsIntent(context: Context, activityResultLauncher: ActivityResultLauncher) {
val intent = Intent()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
- intent.putExtra(Settings.EXTRA_APP_PACKAGE, activity.packageName)
+ intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
} else {
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
- intent.putExtra("app_package", activity.packageName)
- intent.putExtra("app_uid", activity.applicationInfo?.uid)
+ intent.putExtra("app_package", context.packageName)
+ intent.putExtra("app_uid", context.applicationInfo?.uid)
}
activityResultLauncher.launch(intent)
}
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt
index 3c00478ab0..e65fb6042b 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt
@@ -76,7 +76,8 @@ data class VerificationBottomSheetViewState(
val userWantsToCancel: Boolean = false,
val userThinkItsNotHim: Boolean = false,
val quadSContainsSecrets: Boolean = true,
- val quadSHasBeenReset: Boolean = false
+ val quadSHasBeenReset: Boolean = false,
+ val hasAnyOtherSession: Boolean = false
) : MvRxState
class VerificationBottomSheetViewModel @AssistedInject constructor(
@@ -119,6 +120,12 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
session.cryptoService().verificationService().getExistingTransaction(args.otherUserId, it) as? QrCodeVerificationTransaction
}
+ val hasAnyOtherSession = session.cryptoService()
+ .getCryptoDeviceInfo(session.myUserId)
+ .any {
+ it.deviceId != session.sessionParams.deviceId
+ }
+
setState {
copy(
otherUserMxItem = userItem?.toMatrixItem(),
@@ -130,7 +137,8 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
roomId = args.roomId,
isMe = args.otherUserId == session.myUserId,
currentDeviceCanCrossSign = session.cryptoService().crossSigningService().canCrossSign(),
- quadSContainsSecrets = session.sharedSecretStorageService.isRecoverySetup()
+ quadSContainsSecrets = session.sharedSecretStorageService.isRecoverySetup(),
+ hasAnyOtherSession = hasAnyOtherSession
)
}
diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestController.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestController.kt
index 3f4c3120e7..6e98d924e7 100644
--- a/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestController.kt
+++ b/vector/src/main/java/im/vector/app/features/crypto/verification/request/VerificationRequestController.kt
@@ -52,25 +52,32 @@ class VerificationRequestController @Inject constructor(
val matrixItem = viewState?.otherUserMxItem ?: return
if (state.selfVerificationMode) {
- bottomSheetVerificationNoticeItem {
- id("notice")
- notice(stringProvider.getString(R.string.verification_open_other_to_verify))
- }
+ if (state.hasAnyOtherSession) {
+ bottomSheetVerificationNoticeItem {
+ id("notice")
+ notice(stringProvider.getString(R.string.verification_open_other_to_verify))
+ }
- bottomSheetSelfWaitItem {
- id("waiting")
- }
+ bottomSheetSelfWaitItem {
+ id("waiting")
+ }
- dividerItem {
- id("sep")
+ dividerItem {
+ id("sep")
+ }
}
if (state.quadSContainsSecrets) {
+ val subtitle = if (state.hasAnyOtherSession) {
+ stringProvider.getString(R.string.verification_use_passphrase)
+ } else {
+ null
+ }
bottomSheetVerificationActionItem {
id("passphrase")
title(stringProvider.getString(R.string.verification_cannot_access_other_session))
titleColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
- subTitle(stringProvider.getString(R.string.verification_use_passphrase))
+ subTitle(subtitle)
iconRes(R.drawable.ic_arrow_right)
iconColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
listener { listener?.onClickRecoverFromPassphrase() }
@@ -122,13 +129,13 @@ class VerificationRequestController @Inject constructor(
listener { listener?.onClickOnVerificationStart() }
}
}
- is Loading -> {
+ is Loading -> {
bottomSheetVerificationWaitingItem {
id("waiting")
title(stringProvider.getString(R.string.verification_request_waiting_for, matrixItem.getBestName()))
}
}
- is Success -> {
+ is Success -> {
if (!pr.invoke().isReady) {
if (state.isMe) {
bottomSheetVerificationWaitingItem {
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginAction.kt b/vector/src/main/java/im/vector/app/features/login/LoginAction.kt
index 9067984852..9788060ad3 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginAction.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginAction.kt
@@ -27,7 +27,7 @@ sealed class LoginAction : VectorViewModelAction {
data class UpdateSignMode(val signMode: SignMode) : LoginAction()
data class LoginWithToken(val loginToken: String) : LoginAction()
data class WebLoginSuccess(val credentials: Credentials) : LoginAction()
- data class InitWith(val loginConfig: LoginConfig) : LoginAction()
+ data class InitWith(val loginConfig: LoginConfig?) : LoginAction()
data class ResetPassword(val email: String, val newPassword: String) : LoginAction()
object ResetPasswordMailConfirmed : LoginAction()
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
index 0eb4ef3e32..01e835b4e3 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginActivity.kt
@@ -91,19 +91,19 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable, UnlockedAc
addFirstFragment()
}
- // Get config extra
- val loginConfig = intent.getParcelableExtra(EXTRA_CONFIG)
- if (loginConfig != null && isFirstCreation()) {
- // TODO Check this
- loginViewModel.handle(LoginAction.InitWith(loginConfig))
- }
-
loginViewModel
.subscribe(this) {
updateWithState(it)
}
loginViewModel.observeViewEvents { handleLoginViewEvents(it) }
+
+ // Get config extra
+ val loginConfig = intent.getParcelableExtra(EXTRA_CONFIG)
+ if (isFirstCreation()) {
+ // TODO Check this
+ loginViewModel.handle(LoginAction.InitWith(loginConfig))
+ }
}
protected open fun addFirstFragment() {
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginServerUrlFormFragment.kt b/vector/src/main/java/im/vector/app/features/login/LoginServerUrlFormFragment.kt
index 4b9f528254..af959fecd4 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginServerUrlFormFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginServerUrlFormFragment.kt
@@ -28,6 +28,8 @@ import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.utils.ensureProtocol
import im.vector.app.core.utils.openUrlInChromeCustomTab
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
/**
@@ -115,7 +117,13 @@ class LoginServerUrlFormFragment @Inject constructor() : AbstractLoginFragment()
}
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) {
diff --git a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
index f986227961..81d6a78123 100644
--- a/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
+++ b/vector/src/main/java/im/vector/app/features/login/LoginViewModel.kt
@@ -417,6 +417,18 @@ class LoginViewModel @AssistedInject constructor(
private fun handleInitWith(action: LoginAction.InitWith) {
loginConfig = action.loginConfig
+
+ // If there is a pending email validation continue on this step
+ try {
+ if (registrationWizard?.isRegistrationStarted == true) {
+ currentThreePid?.let {
+ handle(LoginAction.PostViewEvent(LoginViewEvents.OnSendEmailSuccess(it)))
+ }
+ }
+ } catch (e: Throwable) {
+ // NOOP. API is designed to use wizards in a login/registration flow,
+ // but we need to check the state anyway.
+ }
}
private fun handleResetPassword(action: LoginAction.ResetPassword) {
@@ -672,6 +684,7 @@ class LoginViewModel @AssistedInject constructor(
private fun onSessionCreated(session: Session) {
activeSessionHolder.setActiveSession(session)
+ authenticationService.reset()
session.configureAndStart(applicationContext)
setState {
copy(
@@ -740,7 +753,7 @@ class LoginViewModel @AssistedInject constructor(
override fun onSuccess(data: LoginFlowResult) {
when (data) {
- is LoginFlowResult.Success -> {
+ is LoginFlowResult.Success -> {
val loginMode = when {
// SSO login is taken first
data.supportedLoginTypes.contains(LoginFlowTypes.SSO) -> LoginMode.Sso
diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt
index 26f00fcef9..7147968d49 100644
--- a/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt
+++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationDrawerManager.kt
@@ -590,6 +590,10 @@ class NotificationDrawerManager @Inject constructor(private val context: Context
}
}
+ fun displayDiagnosticNotification() {
+ notificationUtils.displayDiagnosticNotification()
+ }
+
companion object {
private const val SUMMARY_NOTIFICATION_ID = 0
private const val ROOM_MESSAGES_NOTIFICATION_ID = 1
diff --git a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
index 9d89168bb8..44eb278c64 100755
--- a/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
+++ b/vector/src/main/java/im/vector/app/features/notifications/NotificationUtils.kt
@@ -27,8 +27,10 @@ import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
+import android.graphics.Canvas
import android.net.Uri
import android.os.Build
+import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
@@ -36,6 +38,7 @@ import androidx.core.app.RemoteInput
import androidx.core.app.TaskStackBuilder
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
+import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.IconCompat
import androidx.fragment.app.Fragment
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.room.detail.RoomDetailActivity
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.troubleshoot.TestNotificationReceiver
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@@ -61,7 +64,6 @@ import kotlin.random.Random
@Singleton
class NotificationUtils @Inject constructor(private val context: Context,
private val stringProvider: StringProvider,
- private val pinLocker: PinLocker,
private val vectorPreferences: VectorPreferences) {
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_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"
+ const val DIAGNOSTIC_ACTION = "${BuildConfig.APPLICATION_ID}.NotificationActions.DIAGNOSTIC"
+ const val PUSH_ACTION = "${BuildConfig.APPLICATION_ID}.PUSH"
/* ==========================================================================================
* 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
*/
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt
index 160ebd56be..7dc744da31 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileController.kt
@@ -19,6 +19,7 @@ package im.vector.app.features.roomprofile
import com.airbnb.epoxy.TypedEpoxyController
import im.vector.app.R
+import im.vector.app.core.epoxy.expandableTextItem
import im.vector.app.core.epoxy.profiles.buildProfileAction
import im.vector.app.core.epoxy.profiles.buildProfileSection
import im.vector.app.core.resources.ColorProvider
@@ -57,6 +58,20 @@ class RoomProfileController @Inject constructor(
return
}
val roomSummary = data.roomSummary() ?: return
+
+ // Topic
+ roomSummary
+ .topic
+ .takeIf { it.isNotEmpty() }
+ ?.let {
+ buildProfileSection(stringProvider.getString(R.string.room_settings_topic))
+ expandableTextItem {
+ id("topic")
+ content(it)
+ maxLines(2)
+ }
+ }
+
// Security
buildProfileSection(stringProvider.getString(R.string.room_profile_section_security))
val learnMoreSubtitle = if (roomSummary.isEncrypted) {
diff --git a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
index 399c1ecf32..c2f25c08d3 100644
--- a/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/roomprofile/RoomProfileFragment.kt
@@ -129,7 +129,6 @@ class RoomProfileFragment @Inject constructor(
private fun setupLongClicks() {
roomProfileNameView.copyOnLongClick()
roomProfileAliasView.copyOnLongClick()
- roomProfileTopicView.copyOnLongClick()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
@@ -187,7 +186,6 @@ class RoomProfileFragment @Inject constructor(
roomProfileNameView.text = it.displayName
matrixProfileToolbarTitleView.text = it.displayName
roomProfileAliasView.setTextOrHide(it.canonicalAlias)
- roomProfileTopicView.setTextOrHide(it.topic)
val matrixItem = it.toMatrixItem()
avatarRenderer.render(matrixItem, roomProfileAvatarView)
avatarRenderer.render(matrixItem, matrixProfileToolbarAvatarImageView)
diff --git a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt
index b989ae44d0..9dc6dc1751 100644
--- a/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/VectorSettingsNotificationsTroubleshootFragment.kt
@@ -16,12 +16,16 @@
package im.vector.app.features.settings
import android.app.Activity
+import android.content.BroadcastReceiver
import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
+import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
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.platform.VectorBaseActivity
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.settings.troubleshoot.NotificationTroubleshootTestManager
import im.vector.app.features.settings.troubleshoot.TroubleshootTest
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
class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
@@ -45,12 +52,16 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
@BindView(R.id.troubleshoot_test_recycler_view)
lateinit var mRecyclerView: RecyclerView
+
@BindView(R.id.troubleshoot_bottom_view)
lateinit var mBottomView: ViewGroup
+
@BindView(R.id.toubleshoot_summ_description)
lateinit var mSummaryDescription: TextView
+
@BindView(R.id.troubleshoot_summ_button)
lateinit var mSummaryButton: Button
+
@BindView(R.id.troubleshoot_run_button)
lateinit var mRunButton: Button
@@ -82,8 +93,7 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
}
private fun startUI() {
- mSummaryDescription.text = getString(R.string.settings_troubleshoot_diagnostic_running_status,
- 0, 0)
+ mSummaryDescription.text = getString(R.string.settings_troubleshoot_diagnostic_running_status, 0, 0)
testManager = testManagerFactory.create(this)
testManager?.statusListener = { troubleshootTestManager ->
if (isAdded) {
@@ -94,10 +104,10 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
mSummaryButton.visibility = View.GONE
mRunButton.visibility = View.VISIBLE
}
- TroubleshootTest.TestStatus.RUNNING -> {
- // Forces int type because it's breaking lint
- val size: Int = troubleshootTestManager.testList.size
- val currentTestIndex: Int = troubleshootTestManager.currentTestIndex
+ TroubleshootTest.TestStatus.RUNNING,
+ TroubleshootTest.TestStatus.WAITING_FOR_USER -> {
+ val size = troubleshootTestManager.testListSize
+ val currentTestIndex = troubleshootTestManager.currentTestIndex
mSummaryDescription.text = getString(
R.string.settings_troubleshoot_diagnostic_running_status,
currentTestIndex,
@@ -108,15 +118,7 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
}
TroubleshootTest.TestStatus.FAILED -> {
// check if there are quick fixes
- var hasQuickFix = false
- testManager?.testList?.let {
- for (test in it) {
- if (test.status == TroubleshootTest.TestStatus.FAILED && test.quickFix != null) {
- hasQuickFix = true
- break
- }
- }
- }
+ val hasQuickFix = testManager?.hasQuickFix().orFalse()
if (hasQuickFix) {
mSummaryDescription.text = getString(R.string.settings_troubleshoot_diagnostic_failure_status_with_quickfix)
} else {
@@ -161,6 +163,39 @@ class VectorSettingsNotificationsTroubleshootFragment @Inject constructor(
override fun onResume() {
super.onResume()
(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) {
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootRecyclerViewAdapter.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootRecyclerViewAdapter.kt
index 1ccd7b6735..633d9d05fe 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootRecyclerViewAdapter.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootRecyclerViewAdapter.kt
@@ -77,6 +77,16 @@ class NotificationTroubleshootRecyclerViewAdapter(val tests: ArrayList {
+ 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 -> {
progressBar.visibility = View.VISIBLE
statusIconImage.visibility = View.INVISIBLE
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootTestManager.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootTestManager.kt
index e977dc5963..7e7ca57243 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootTestManager.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/NotificationTroubleshootTestManager.kt
@@ -23,13 +23,19 @@ import androidx.fragment.app.Fragment
import kotlin.properties.Delegates
class NotificationTroubleshootTestManager(val fragment: Fragment) {
+ private val testList = ArrayList()
+
+ val testListSize: Int
+ get() = testList.size
- val testList = ArrayList()
var isCancelled = false
+ private set
var currentTestIndex by Delegates.observable(0) { _, _, _ ->
statusListener?.invoke(this)
}
+ private set
+
val adapter = NotificationTroubleshootRecyclerViewAdapter(testList)
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) { _, _, _ ->
statusListener?.invoke(this)
}
+ private set
fun addTest(test: TroubleshootTest) {
testList.add(test)
@@ -79,19 +86,31 @@ class NotificationTroubleshootTestManager(val fragment: Fragment) {
}
fun retry(activityResultLauncher: ActivityResultLauncher) {
- for (test in testList) {
- test.cancel()
- test.description = null
- test.quickFix = null
- test.status = TroubleshootTest.TestStatus.NOT_STARTED
+ testList.forEach {
+ it.cancel()
+ it.description = null
+ it.quickFix = null
+ it.status = TroubleshootTest.TestStatus.NOT_STARTED
}
runDiagnostic(activityResultLauncher)
}
- fun cancel() {
- isCancelled = true
- for (test in testList) {
- test.cancel()
+ fun hasQuickFix(): Boolean {
+ return testList.any { test ->
+ test.status == TroubleshootTest.TestStatus.FAILED && test.quickFix != null
}
}
+
+ fun cancel() {
+ isCancelled = true
+ testList.forEach { it.cancel() }
+ }
+
+ fun onDiagnosticPushReceived() {
+ testList.forEach { it.onPushReceived() }
+ }
+
+ fun onDiagnosticNotificationClicked() {
+ testList.forEach { it.onNotificationClicked() }
+ }
}
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotification.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotification.kt
new file mode 100644
index 0000000000..6f25ecfe39
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotification.kt
@@ -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) {
+ // 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
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotificationReceiver.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotificationReceiver.kt
new file mode 100644
index 0000000000..7dec870d3d
--- /dev/null
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestNotificationReceiver.kt
@@ -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)
+ }
+}
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt
index 8e64514e7f..ee652288be 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TestSystemSettings.kt
@@ -15,9 +15,9 @@
*/
package im.vector.app.features.settings.troubleshoot
+import android.content.Context
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
-import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationManagerCompat
import im.vector.app.R
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.
*/
-class TestSystemSettings @Inject constructor(private val context: AppCompatActivity,
+class TestSystemSettings @Inject constructor(private val context: Context,
private val stringProvider: StringProvider)
: TroubleshootTest(R.string.settings_troubleshoot_test_system_settings_title) {
diff --git a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TroubleshootTest.kt b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TroubleshootTest.kt
index f894fcc0ef..76ba2378a0 100644
--- a/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TroubleshootTest.kt
+++ b/vector/src/main/java/im/vector/app/features/settings/troubleshoot/TroubleshootTest.kt
@@ -25,6 +25,7 @@ abstract class TroubleshootTest(@StringRes val titleResId: Int) {
enum class TestStatus {
NOT_STARTED,
RUNNING,
+ WAITING_FOR_USER,
FAILED,
SUCCESS
}
@@ -51,4 +52,10 @@ abstract class TroubleshootTest(@StringRes val titleResId: Int) {
open fun cancel() {
}
+
+ open fun onPushReceived() {
+ }
+
+ open fun onNotificationClicked() {
+ }
}
diff --git a/vector/src/main/res/drawable/ic_expand_less.xml b/vector/src/main/res/drawable/ic_expand_less.xml
new file mode 100644
index 0000000000..92bc86da08
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_expand_less.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/vector/src/main/res/drawable/ic_expand_more.xml b/vector/src/main/res/drawable/ic_expand_more.xml
new file mode 100644
index 0000000000..22c401f0c3
--- /dev/null
+++ b/vector/src/main/res/drawable/ic_expand_more.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/vector/src/main/res/layout/activity_jitsi.xml b/vector/src/main/res/layout/activity_jitsi.xml
index e0359d220d..de0c0271bc 100644
--- a/vector/src/main/res/layout/activity_jitsi.xml
+++ b/vector/src/main/res/layout/activity_jitsi.xml
@@ -1,11 +1,12 @@
-
+
+ android:orientation="vertical"
+ tools:ignore="UselessParent">
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vector/src/main/res/layout/view_avatar_selector.xml b/vector/src/main/res/layout/view_avatar_selector.xml
deleted file mode 100644
index c303347960..0000000000
--- a/vector/src/main/res/layout/view_avatar_selector.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/vector/src/main/res/layout/view_stub_room_profile_header.xml b/vector/src/main/res/layout/view_stub_room_profile_header.xml
index f7ae1c77a5..ecb7174f0e 100644
--- a/vector/src/main/res/layout/view_stub_room_profile_header.xml
+++ b/vector/src/main/res/layout/view_stub_room_profile_header.xml
@@ -54,27 +54,9 @@
android:textAppearance="@style/Vector.Toolbar.Title"
android:textSize="14sp"
android:textStyle="bold"
- app:layout_constraintBottom_toTopOf="@+id/roomProfileTopicView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/roomProfileNameView"
tools:text="@sample/matrix.json/data/roomAlias" />
-
-
diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
index d48162de29..e400f7fe64 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -749,6 +749,15 @@
FCM token successfully registered to HomeServer.
Failed to register FCM token to HomeServer:\n%1$s
+ Test Push
+ The application is waiting for the PUSH
+ The application is receiving PUSH
+ Failed to receive push. Solution could be to reinstall the application.
+ You are viewing the notification! Click me!
+ Notification Display
+ Please click on the notification. If you do not see the notification, please check the system settings.
+ The notification has been clicked!
+
Notifications Service
Notifications Service is running.
Notifications Service is not running.\nTry to restart the application.