Merge branch 'develop' into feature/ons/add_members_fab
This commit is contained in:
commit
dff7f24187
|
@ -29,6 +29,7 @@
|
|||
<w>signout</w>
|
||||
<w>signup</w>
|
||||
<w>ssss</w>
|
||||
<w>sygnal</w>
|
||||
<w>threepid</w>
|
||||
<w>unwedging</w>
|
||||
</words>
|
||||
|
|
|
@ -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 🗣:
|
||||
-
|
||||
|
|
|
@ -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,
|
||||
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
|
||||
*/
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/"
|
||||
}
|
||||
|
|
|
@ -113,8 +113,4 @@ constructor(trustPinned: Array<TrustManager>, acceptedTlsVersions: List<TlsVersi
|
|||
}
|
||||
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.SessionId
|
||||
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.configureWith
|
||||
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
|
||||
|
@ -41,10 +42,23 @@ internal class DefaultPushersService @Inject constructor(
|
|||
@SessionDatabase private val monarchy: Monarchy,
|
||||
@SessionId private val sessionId: String,
|
||||
private val getPusherTask: GetPushersTask,
|
||||
private val pushGatewayNotifyTask: PushGatewayNotifyTask,
|
||||
private val removePusherTask: RemovePusherTask,
|
||||
private val taskExecutor: TaskExecutor
|
||||
) : 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() {
|
||||
getPusherTask
|
||||
.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.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
|
||||
}
|
||||
|
|
|
@ -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.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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,9 +94,11 @@
|
|||
</activity>
|
||||
<activity android:name=".features.media.ImageMediaViewerActivity" />
|
||||
|
||||
<!-- Add tools:ignore="Instantiatable" for the error reported only by Buildkite :/ -->
|
||||
<activity
|
||||
android:name=".features.media.VectorAttachmentViewerActivity"
|
||||
android:theme="@style/AppTheme.Transparent" />
|
||||
android:theme="@style/AppTheme.Transparent"
|
||||
tools:ignore="Instantiatable" />
|
||||
|
||||
<activity android:name=".features.media.BigImageViewerActivity" />
|
||||
<activity
|
||||
|
@ -252,6 +254,10 @@
|
|||
android:name=".features.call.service.CallHeadsUpActionReceiver"
|
||||
android:exported="false" />
|
||||
|
||||
<receiver
|
||||
android:name=".features.settings.troubleshoot.TestNotificationReceiver"
|
||||
android:exported="false" />
|
||||
|
||||
<!-- Exported false, should only be accessible from this app!! -->
|
||||
<receiver
|
||||
android:name=".features.notifications.NotificationBroadcastReceiver"
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* 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.core.epoxy
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.text.TextUtils
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import androidx.core.view.isVisible
|
||||
import com.airbnb.epoxy.EpoxyAttribute
|
||||
import com.airbnb.epoxy.EpoxyModelClass
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.copyOnLongClick
|
||||
|
||||
@EpoxyModelClass(layout = R.layout.item_expandable_textview)
|
||||
abstract class ExpandableTextItem : VectorEpoxyModel<ExpandableTextItem.Holder>() {
|
||||
|
||||
@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<TextView>(R.id.expandableContent)
|
||||
val arrow by bind<ImageView>(R.id.expandableArrow)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<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 {
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Intent>) {
|
||||
fun startNotificationSettingsIntent(context: Context, activityResultLauncher: ActivityResultLauncher<Intent>) {
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -91,19 +91,19 @@ open class LoginActivity : VectorBaseActivity(), ToolbarConfigurable, UnlockedAc
|
|||
addFirstFragment()
|
||||
}
|
||||
|
||||
// Get config extra
|
||||
val loginConfig = intent.getParcelableExtra<LoginConfig?>(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<LoginConfig?>(EXTRA_CONFIG)
|
||||
if (isFirstCreation()) {
|
||||
// TODO Check this
|
||||
loginViewModel.handle(LoginAction.InitWith(loginConfig))
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun addFirstFragment() {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -77,6 +77,16 @@ class NotificationTroubleshootRecyclerViewAdapter(val tests: ArrayList<Troublesh
|
|||
statusIconImage.visibility = View.VISIBLE
|
||||
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 -> {
|
||||
progressBar.visibility = View.VISIBLE
|
||||
statusIconImage.visibility = View.INVISIBLE
|
||||
|
|
|
@ -23,13 +23,19 @@ import androidx.fragment.app.Fragment
|
|||
import kotlin.properties.Delegates
|
||||
|
||||
class NotificationTroubleshootTestManager(val fragment: Fragment) {
|
||||
private val testList = ArrayList<TroubleshootTest>()
|
||||
|
||||
val testListSize: Int
|
||||
get() = testList.size
|
||||
|
||||
val testList = ArrayList<TroubleshootTest>()
|
||||
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<Intent>) {
|
||||
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() }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
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) {
|
||||
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M12,8l-6,6 1.41,1.41L12,10.83l4.59,4.58L18,14z"/>
|
||||
</vector>
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
|
||||
</vector>
|
|
@ -1,11 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/jitsi_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
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
|
||||
android:id="@+id/jitsi_progress_layout"
|
||||
|
@ -13,7 +14,8 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
android:orientation="vertical"
|
||||
tools:ignore="UselessParent">
|
||||
|
||||
<ProgressBar
|
||||
android:layout_width="40dp"
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/expandableContent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
app:layout_constraintBottom_toTopOf="@+id/expandableArrow"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:maxLines="2"
|
||||
tools:text="@sample/matrix.json/data/roomTopic" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/expandableArrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:autoLink="web"
|
||||
android:fontFamily="sans-serif"
|
||||
android:gravity="center"
|
||||
android:src="@drawable/ic_expand_more"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="normal"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/expandableContent"
|
||||
app:tint="?riotx_text_secondary"
|
||||
tools:ignore="MissingPrefix" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -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>
|
|
@ -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" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/roomProfileTopicView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="40dp"
|
||||
android:layout_marginEnd="40dp"
|
||||
android:autoLink="web"
|
||||
android:fontFamily="sans-serif"
|
||||
android:gravity="center"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="normal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/roomProfileAliasView"
|
||||
tools:text="@sample/matrix.json/data/roomTopic" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -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_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_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>
|
||||
|
|
Loading…
Reference in New Issue