mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 11:59:12 +03:00
Support for lab auto rageshake UISI
This commit is contained in:
parent
6a34b999f2
commit
be119ea161
11 changed files with 430 additions and 17 deletions
|
@ -36,6 +36,7 @@
|
||||||
<w>ssss</w>
|
<w>ssss</w>
|
||||||
<w>sygnal</w>
|
<w>sygnal</w>
|
||||||
<w>threepid</w>
|
<w>threepid</w>
|
||||||
|
<w>uisi</w>
|
||||||
<w>unpublish</w>
|
<w>unpublish</w>
|
||||||
<w>unwedging</w>
|
<w>unwedging</w>
|
||||||
<w>vctr</w>
|
<w>vctr</w>
|
||||||
|
|
204
vector/src/main/java/im/vector/app/AutoRageShaker.kt
Normal file
204
vector/src/main/java/im/vector/app/AutoRageShaker.kt
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
|
import im.vector.app.features.rageshake.BugReporter
|
||||||
|
import im.vector.app.features.rageshake.ReportType
|
||||||
|
import io.reactivex.disposables.Disposable
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.matrix.android.sdk.api.session.Session
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.toContent
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
const val AUTO_RS_REQUEST = "im.vector.auto_rs_request"
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
class AutoRageShaker @Inject constructor(
|
||||||
|
private val sessionDataSource: ActiveSessionDataSource,
|
||||||
|
private val activeSessionHolder: ActiveSessionHolder,
|
||||||
|
private val bugReporter: BugReporter,
|
||||||
|
private val context: Context
|
||||||
|
) : Session.Listener {
|
||||||
|
|
||||||
|
private lateinit var activeSessionDisposable: Disposable
|
||||||
|
private val activeSessionIds = mutableSetOf<String>()
|
||||||
|
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
|
||||||
|
private val uisiDetectors = mutableMapOf<String, UISIDetector>()
|
||||||
|
private var currentActiveSessionId: String? = null
|
||||||
|
|
||||||
|
fun initialize() {
|
||||||
|
observeActiveSession()
|
||||||
|
}
|
||||||
|
|
||||||
|
var _enabled = false
|
||||||
|
fun enable(enabled: Boolean) {
|
||||||
|
uisiDetectors.forEach { it.value.enabled = enabled }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun observeActiveSession() {
|
||||||
|
activeSessionDisposable = sessionDataSource.observe()
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.subscribe {
|
||||||
|
it.orNull()?.let { session ->
|
||||||
|
onSessionActive(session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun decryptionErrorDetected(target: E2EMessageDetected) {
|
||||||
|
if (target.source == UISIEventSource.INITIAL_SYNC) return
|
||||||
|
if (activeSessionHolder.getSafeActiveSession()?.sessionId != currentActiveSessionId) return
|
||||||
|
coroutineScope.launch {
|
||||||
|
bugReporter.sendBugReport(
|
||||||
|
context = context,
|
||||||
|
reportType = ReportType.AUTO_UISI,
|
||||||
|
withDevicesLogs = true,
|
||||||
|
withCrashLogs = true,
|
||||||
|
withKeyRequestHistory = true,
|
||||||
|
withScreenshot = false,
|
||||||
|
theBugDescription = "UISI detected",
|
||||||
|
serverVersion = "",
|
||||||
|
canContact = false,
|
||||||
|
customFields = mapOf("auto-uisi" to buildString {
|
||||||
|
append("\neventId: ${target.eventId}")
|
||||||
|
append("\nroomId: ${target.roomId}")
|
||||||
|
append("\nsenderKey: ${target.senderKey}")
|
||||||
|
append("\nsource: ${target.source}")
|
||||||
|
append("\ndeviceId: ${target.senderDeviceId}")
|
||||||
|
append("\nuserId: ${target.senderUserId}")
|
||||||
|
append("\nsessionId: ${target.sessionId}")
|
||||||
|
}),
|
||||||
|
listener = object : BugReporter.IMXBugReportListener {
|
||||||
|
override fun onUploadCancelled() {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUploadFailed(reason: String?) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onProgress(progress: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onUploadSucceed(reportUrl: String?) {
|
||||||
|
Timber.w("## VALR Report URL is $reportUrl")
|
||||||
|
// we need to send the toDevice message to the sender
|
||||||
|
|
||||||
|
coroutineScope.launch {
|
||||||
|
try {
|
||||||
|
activeSessionHolder.getSafeActiveSession()?.sendToDevice(
|
||||||
|
eventType = AUTO_RS_REQUEST,
|
||||||
|
userId = target.senderUserId,
|
||||||
|
deviceId = target.senderDeviceId,
|
||||||
|
content = mapOf(
|
||||||
|
"event_id" to target.eventId,
|
||||||
|
"room_id" to target.roomId,
|
||||||
|
"session_id" to target.sessionId,
|
||||||
|
"device_id" to target.senderDeviceId,
|
||||||
|
"user_id" to target.senderUserId,
|
||||||
|
"sender_key" to target.senderKey,
|
||||||
|
"matching_issue" to reportUrl
|
||||||
|
).toContent()
|
||||||
|
)
|
||||||
|
} catch (failure: Throwable) {
|
||||||
|
Timber.w("## VALR : failed to send auto-uisi to device")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remoteAutoUISIRequest(event: Event) {
|
||||||
|
if (event.type != AUTO_RS_REQUEST) return
|
||||||
|
if (activeSessionHolder.getSafeActiveSession()?.sessionId != currentActiveSessionId) return
|
||||||
|
|
||||||
|
coroutineScope.launch {
|
||||||
|
val eventId = event.content?.get("event_id")
|
||||||
|
val roomId = event.content?.get("room_id")
|
||||||
|
val sessionId = event.content?.get("session_id")
|
||||||
|
val deviceId = event.content?.get("device_id")
|
||||||
|
val userId = event.content?.get("user_id")
|
||||||
|
val senderKey = event.content?.get("sender_key")
|
||||||
|
val matchingIssue = event.content?.get("matching_issue")?.toString() ?: ""
|
||||||
|
|
||||||
|
bugReporter.sendBugReport(
|
||||||
|
context = context,
|
||||||
|
reportType = ReportType.AUTO_UISI_SENDER,
|
||||||
|
withDevicesLogs = true,
|
||||||
|
withCrashLogs = true,
|
||||||
|
withKeyRequestHistory = true,
|
||||||
|
withScreenshot = false,
|
||||||
|
theBugDescription = "UISI detected $matchingIssue",
|
||||||
|
serverVersion = "",
|
||||||
|
canContact = false,
|
||||||
|
customFields = mapOf<String, String>(
|
||||||
|
"auto-uisi" to buildString {
|
||||||
|
append("\neventId: $eventId")
|
||||||
|
append("\nroomId: $roomId")
|
||||||
|
append("\nsenderKey: $senderKey")
|
||||||
|
append("\ndeviceId: $deviceId")
|
||||||
|
append("\nuserId: $userId")
|
||||||
|
append("\nsessionId: $sessionId")
|
||||||
|
},
|
||||||
|
"matching_issue" to matchingIssue
|
||||||
|
),
|
||||||
|
listener = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val detector = UISIDetector().apply {
|
||||||
|
callback = object : UISIDetector.UISIDetectorCallback {
|
||||||
|
override val reciprocateToDeviceEventType: String
|
||||||
|
get() = AUTO_RS_REQUEST
|
||||||
|
|
||||||
|
override fun uisiDetected(source: E2EMessageDetected) {
|
||||||
|
decryptionErrorDetected(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun uisiReciprocateRequest(source: Event) {
|
||||||
|
remoteAutoUISIRequest(source)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onSessionActive(session: Session) {
|
||||||
|
val sessionId = session.sessionId
|
||||||
|
if (sessionId == currentActiveSessionId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.currentActiveSessionId = sessionId
|
||||||
|
this.detector.enabled = _enabled
|
||||||
|
activeSessionIds.add(sessionId)
|
||||||
|
session.addListener(this)
|
||||||
|
session.addEventStreamListener(detector)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSessionStopped(session: Session) {
|
||||||
|
uisiDetectors.get(session.sessionId)?.let {
|
||||||
|
session.removeEventStreamListener(it)
|
||||||
|
}
|
||||||
|
activeSessionIds.remove(session.sessionId)
|
||||||
|
}
|
||||||
|
}
|
153
vector/src/main/java/im/vector/app/UISIDetector.kt
Normal file
153
vector/src/main/java/im/vector/app/UISIDetector.kt
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 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
|
||||||
|
|
||||||
|
import org.matrix.android.sdk.api.session.LiveEventListener
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.Event
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.toModel
|
||||||
|
import org.matrix.android.sdk.api.util.JsonDict
|
||||||
|
import org.matrix.android.sdk.internal.crypto.model.event.EncryptedEventContent
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.util.Timer
|
||||||
|
import java.util.TimerTask
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
enum class UISIEventSource {
|
||||||
|
INITIAL_SYNC,
|
||||||
|
INCREMENTAL_SYNC,
|
||||||
|
PAGINATION
|
||||||
|
}
|
||||||
|
|
||||||
|
data class E2EMessageDetected(
|
||||||
|
val eventId: String,
|
||||||
|
val roomId: String,
|
||||||
|
val senderUserId: String,
|
||||||
|
val senderDeviceId: String,
|
||||||
|
val senderKey: String,
|
||||||
|
val sessionId: String,
|
||||||
|
val source: UISIEventSource) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromEvent(event: Event, roomId: String, source: UISIEventSource): E2EMessageDetected {
|
||||||
|
val encryptedContent = event.content.toModel<EncryptedEventContent>()
|
||||||
|
|
||||||
|
return E2EMessageDetected(
|
||||||
|
eventId = event.eventId ?: "",
|
||||||
|
roomId = roomId,
|
||||||
|
senderUserId = event.senderId ?: "",
|
||||||
|
senderDeviceId = encryptedContent?.deviceId ?: "",
|
||||||
|
senderKey = encryptedContent?.senderKey ?: "",
|
||||||
|
sessionId = encryptedContent?.sessionId ?: "",
|
||||||
|
source = source
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UISIDetector : LiveEventListener {
|
||||||
|
|
||||||
|
interface UISIDetectorCallback {
|
||||||
|
val reciprocateToDeviceEventType: String
|
||||||
|
fun uisiDetected(source: E2EMessageDetected)
|
||||||
|
fun uisiReciprocateRequest(source: Event)
|
||||||
|
}
|
||||||
|
|
||||||
|
var callback: UISIDetectorCallback? = null
|
||||||
|
|
||||||
|
private val trackedEvents = mutableListOf<Pair<E2EMessageDetected, TimerTask>>()
|
||||||
|
private val executor = Executors.newSingleThreadExecutor()
|
||||||
|
private val timer = Timer()
|
||||||
|
private val timeoutMillis = 30_000L
|
||||||
|
var enabled = false
|
||||||
|
|
||||||
|
override fun onLiveEvent(roomId: String, event: Event) {
|
||||||
|
if (!event.isEncrypted()) return
|
||||||
|
executor.execute {
|
||||||
|
handleEventReceived(E2EMessageDetected.fromEvent(event, roomId, UISIEventSource.INCREMENTAL_SYNC))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPaginatedEvent(roomId: String, event: Event) {
|
||||||
|
if (!event.isEncrypted()) return
|
||||||
|
executor.execute {
|
||||||
|
handleEventReceived(E2EMessageDetected.fromEvent(event, roomId, UISIEventSource.PAGINATION))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onEventDecrypted(eventId: String, roomId: String, clearEvent: JsonDict) {
|
||||||
|
executor.execute {
|
||||||
|
unTrack(eventId, roomId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onLiveToDeviceEvent(event: Event) {
|
||||||
|
if (event.type == callback?.reciprocateToDeviceEventType) {
|
||||||
|
callback?.uisiReciprocateRequest(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onEventDecryptionError(eventId: String, roomId: String, throwable: Throwable) {
|
||||||
|
executor.execute {
|
||||||
|
unTrack(eventId, roomId)?.let {
|
||||||
|
triggerUISI(it)
|
||||||
|
}
|
||||||
|
// if (throwable is MXCryptoError.OlmError) {
|
||||||
|
// if (throwable.olmException.message == "UNKNOWN_MESSAGE_INDEX") {
|
||||||
|
// unTrack(eventId, roomId)?.let {
|
||||||
|
// triggerUISI(it)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleEventReceived(detectorEvent: E2EMessageDetected) {
|
||||||
|
if (trackedEvents.any { it.first == detectorEvent }) {
|
||||||
|
Timber.w("## UISIDetector: Event ${detectorEvent.eventId} is already tracked")
|
||||||
|
} else {
|
||||||
|
// track it and start timer
|
||||||
|
val timeoutTask = object : TimerTask() {
|
||||||
|
override fun run() {
|
||||||
|
executor.execute {
|
||||||
|
unTrack(detectorEvent.eventId, detectorEvent.roomId)
|
||||||
|
Timber.v("## UISIDetector: Timeout on ${detectorEvent.eventId} ")
|
||||||
|
triggerUISI(detectorEvent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trackedEvents.add(detectorEvent to timeoutTask)
|
||||||
|
timer.schedule(timeoutTask, timeoutMillis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun triggerUISI(source: E2EMessageDetected) {
|
||||||
|
Timber.i("## UISIDetector: Unable To Decrypt $source")
|
||||||
|
callback?.uisiDetected(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun unTrack(eventId: String, roomId: String): E2EMessageDetected? {
|
||||||
|
val index = trackedEvents.indexOfFirst { it.first.eventId == eventId && it.first.roomId == roomId }
|
||||||
|
return if (index != -1) {
|
||||||
|
trackedEvents.removeAt(index).let {
|
||||||
|
it.second.cancel()
|
||||||
|
it.first
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -96,6 +96,7 @@ class VectorApplication :
|
||||||
@Inject lateinit var pinLocker: PinLocker
|
@Inject lateinit var pinLocker: PinLocker
|
||||||
@Inject lateinit var callManager: WebRtcCallManager
|
@Inject lateinit var callManager: WebRtcCallManager
|
||||||
@Inject lateinit var invitesAcceptor: InvitesAcceptor
|
@Inject lateinit var invitesAcceptor: InvitesAcceptor
|
||||||
|
@Inject lateinit var autoRageShaker: AutoRageShaker
|
||||||
@Inject lateinit var vectorFileLogger: VectorFileLogger
|
@Inject lateinit var vectorFileLogger: VectorFileLogger
|
||||||
@Inject lateinit var vectorAnalytics: VectorAnalytics
|
@Inject lateinit var vectorAnalytics: VectorAnalytics
|
||||||
|
|
||||||
|
@ -117,6 +118,7 @@ class VectorApplication :
|
||||||
appContext = this
|
appContext = this
|
||||||
vectorAnalytics.init()
|
vectorAnalytics.init()
|
||||||
invitesAcceptor.initialize()
|
invitesAcceptor.initialize()
|
||||||
|
autoRageShaker.initialize()
|
||||||
vectorUncaughtExceptionHandler.activate(this)
|
vectorUncaughtExceptionHandler.activate(this)
|
||||||
|
|
||||||
// Remove Log handler statically added by Jitsi
|
// Remove Log handler statically added by Jitsi
|
||||||
|
|
|
@ -156,6 +156,7 @@ class BugReportActivity : VectorBaseActivity<ActivityBugReportBinding>() {
|
||||||
views.bugReportEditText.text.toString(),
|
views.bugReportEditText.text.toString(),
|
||||||
state.serverVersion,
|
state.serverVersion,
|
||||||
views.bugReportButtonContactMe.isChecked,
|
views.bugReportButtonContactMe.isChecked,
|
||||||
|
null,
|
||||||
object : BugReporter.IMXBugReportListener {
|
object : BugReporter.IMXBugReportListener {
|
||||||
override fun onUploadFailed(reason: String?) {
|
override fun onUploadFailed(reason: String?) {
|
||||||
try {
|
try {
|
||||||
|
@ -198,7 +199,7 @@ class BugReportActivity : VectorBaseActivity<ActivityBugReportBinding>() {
|
||||||
views.bugReportProgressTextView.text = getString(R.string.send_bug_report_progress, myProgress.toString())
|
views.bugReportProgressTextView.text = getString(R.string.send_bug_report_progress, myProgress.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onUploadSucceed() {
|
override fun onUploadSucceed(reportUrl: String?) {
|
||||||
try {
|
try {
|
||||||
when (reportType) {
|
when (reportType) {
|
||||||
ReportType.BUG_REPORT -> {
|
ReportType.BUG_REPORT -> {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import android.os.Build
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
import com.squareup.moshi.Types
|
||||||
import im.vector.app.BuildConfig
|
import im.vector.app.BuildConfig
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
import im.vector.app.core.di.ActiveSessionHolder
|
import im.vector.app.core.di.ActiveSessionHolder
|
||||||
|
@ -49,7 +50,9 @@ import okhttp3.Response
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import org.matrix.android.sdk.api.Matrix
|
import org.matrix.android.sdk.api.Matrix
|
||||||
|
import org.matrix.android.sdk.api.util.JsonDict
|
||||||
import org.matrix.android.sdk.api.util.MimeTypes
|
import org.matrix.android.sdk.api.util.MimeTypes
|
||||||
|
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
@ -93,6 +96,9 @@ class BugReporter @Inject constructor(
|
||||||
// boolean to cancel the bug report
|
// boolean to cancel the bug report
|
||||||
private val mIsCancelled = false
|
private val mIsCancelled = false
|
||||||
|
|
||||||
|
val adapter = MoshiProvider.providesMoshi()
|
||||||
|
.adapter<JsonDict>(Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current Screenshot
|
* Get current Screenshot
|
||||||
*
|
*
|
||||||
|
@ -141,7 +147,7 @@ class BugReporter @Inject constructor(
|
||||||
/**
|
/**
|
||||||
* The bug report upload succeeded.
|
* The bug report upload succeeded.
|
||||||
*/
|
*/
|
||||||
fun onUploadSucceed()
|
fun onUploadSucceed(reportUrl: String?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -166,12 +172,14 @@ class BugReporter @Inject constructor(
|
||||||
theBugDescription: String,
|
theBugDescription: String,
|
||||||
serverVersion: String,
|
serverVersion: String,
|
||||||
canContact: Boolean = false,
|
canContact: Boolean = false,
|
||||||
|
customFields: Map<String, String>? = null,
|
||||||
listener: IMXBugReportListener?) {
|
listener: IMXBugReportListener?) {
|
||||||
// enumerate files to delete
|
// enumerate files to delete
|
||||||
val mBugReportFiles: MutableList<File> = ArrayList()
|
val mBugReportFiles: MutableList<File> = ArrayList()
|
||||||
|
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
var serverError: String? = null
|
var serverError: String? = null
|
||||||
|
var reportURL: String? = null
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
var bugDescription = theBugDescription
|
var bugDescription = theBugDescription
|
||||||
val crashCallStack = getCrashDescription(context)
|
val crashCallStack = getCrashDescription(context)
|
||||||
|
@ -247,9 +255,11 @@ class BugReporter @Inject constructor(
|
||||||
|
|
||||||
if (!mIsCancelled) {
|
if (!mIsCancelled) {
|
||||||
val text = when (reportType) {
|
val text = when (reportType) {
|
||||||
ReportType.BUG_REPORT -> "[Element] $bugDescription"
|
ReportType.BUG_REPORT -> "[Element] $bugDescription"
|
||||||
ReportType.SUGGESTION -> "[Element] [Suggestion] $bugDescription"
|
ReportType.SUGGESTION -> "[Element] [Suggestion] $bugDescription"
|
||||||
ReportType.SPACE_BETA_FEEDBACK -> "[Element] [spaces-feedback] $bugDescription"
|
ReportType.SPACE_BETA_FEEDBACK -> "[Element] [spaces-feedback] $bugDescription"
|
||||||
|
ReportType.AUTO_UISI_SENDER,
|
||||||
|
ReportType.AUTO_UISI -> "[AutoUISI] $bugDescription"
|
||||||
}
|
}
|
||||||
|
|
||||||
// build the multi part request
|
// build the multi part request
|
||||||
|
@ -273,7 +283,11 @@ class BugReporter @Inject constructor(
|
||||||
.addFormDataPart("app_language", VectorLocale.applicationLocale.toString())
|
.addFormDataPart("app_language", VectorLocale.applicationLocale.toString())
|
||||||
.addFormDataPart("default_app_language", systemLocaleProvider.getSystemLocale().toString())
|
.addFormDataPart("default_app_language", systemLocaleProvider.getSystemLocale().toString())
|
||||||
.addFormDataPart("theme", ThemeUtils.getApplicationTheme(context))
|
.addFormDataPart("theme", ThemeUtils.getApplicationTheme(context))
|
||||||
.addFormDataPart("server_version", serverVersion)
|
.addFormDataPart("server_version", serverVersion).apply {
|
||||||
|
customFields?.forEach { (name, value) ->
|
||||||
|
addFormDataPart(name, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val buildNumber = context.getString(R.string.build_number)
|
val buildNumber = context.getString(R.string.build_number)
|
||||||
if (buildNumber.isNotEmpty() && buildNumber != "0") {
|
if (buildNumber.isNotEmpty() && buildNumber != "0") {
|
||||||
|
@ -321,11 +335,19 @@ class BugReporter @Inject constructor(
|
||||||
builder.addFormDataPart("label", "[Element]")
|
builder.addFormDataPart("label", "[Element]")
|
||||||
|
|
||||||
when (reportType) {
|
when (reportType) {
|
||||||
ReportType.BUG_REPORT -> {
|
ReportType.BUG_REPORT -> {
|
||||||
/* nop */
|
/* nop */
|
||||||
}
|
}
|
||||||
ReportType.SUGGESTION -> builder.addFormDataPart("label", "[Suggestion]")
|
ReportType.SUGGESTION -> builder.addFormDataPart("label", "[Suggestion]")
|
||||||
ReportType.SPACE_BETA_FEEDBACK -> builder.addFormDataPart("label", "spaces-feedback")
|
ReportType.SPACE_BETA_FEEDBACK -> builder.addFormDataPart("label", "spaces-feedback")
|
||||||
|
ReportType.AUTO_UISI -> {
|
||||||
|
builder.addFormDataPart("label", "auto-uisis-receiver")
|
||||||
|
builder.addFormDataPart("label", "auto-uisis")
|
||||||
|
}
|
||||||
|
ReportType.AUTO_UISI_SENDER -> {
|
||||||
|
builder.addFormDataPart("label", "auto-uisis-sender")
|
||||||
|
builder.addFormDataPart("label", "auto-uisis")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getCrashFile(context).exists()) {
|
if (getCrashFile(context).exists()) {
|
||||||
|
@ -417,6 +439,10 @@ class BugReporter @Inject constructor(
|
||||||
Timber.e(e, "## sendBugReport() : failed to parse error")
|
Timber.e(e, "## sendBugReport() : failed to parse error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
reportURL = response?.body?.string()?.let { stringBody ->
|
||||||
|
adapter.fromJson(stringBody)?.get("report_url")?.toString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -434,7 +460,7 @@ class BugReporter @Inject constructor(
|
||||||
if (mIsCancelled) {
|
if (mIsCancelled) {
|
||||||
listener.onUploadCancelled()
|
listener.onUploadCancelled()
|
||||||
} else if (null == serverError) {
|
} else if (null == serverError) {
|
||||||
listener.onUploadSucceed()
|
listener.onUploadSucceed(reportURL)
|
||||||
} else {
|
} else {
|
||||||
listener.onUploadFailed(serverError)
|
listener.onUploadFailed(serverError)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,5 +19,7 @@ package im.vector.app.features.rageshake
|
||||||
enum class ReportType {
|
enum class ReportType {
|
||||||
BUG_REPORT,
|
BUG_REPORT,
|
||||||
SUGGESTION,
|
SUGGESTION,
|
||||||
SPACE_BETA_FEEDBACK
|
SPACE_BETA_FEEDBACK,
|
||||||
|
AUTO_UISI,
|
||||||
|
AUTO_UISI_SENDER,
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,6 +152,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
|
||||||
const val SETTINGS_LABS_ALLOW_EXTENDED_LOGS = "SETTINGS_LABS_ALLOW_EXTENDED_LOGS"
|
const val SETTINGS_LABS_ALLOW_EXTENDED_LOGS = "SETTINGS_LABS_ALLOW_EXTENDED_LOGS"
|
||||||
const val SETTINGS_LABS_USE_RESTRICTED_JOIN_RULE = "SETTINGS_LABS_USE_RESTRICTED_JOIN_RULE"
|
const val SETTINGS_LABS_USE_RESTRICTED_JOIN_RULE = "SETTINGS_LABS_USE_RESTRICTED_JOIN_RULE"
|
||||||
const val SETTINGS_LABS_SPACES_HOME_AS_ORPHAN = "SETTINGS_LABS_SPACES_HOME_AS_ORPHAN"
|
const val SETTINGS_LABS_SPACES_HOME_AS_ORPHAN = "SETTINGS_LABS_SPACES_HOME_AS_ORPHAN"
|
||||||
|
const val SETTINGS_LABS_AUTO_REPORT_UISI = "SETTINGS_LABS_AUTO_REPORT_UISI"
|
||||||
const val SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME = "SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME"
|
const val SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME = "SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME"
|
||||||
|
|
||||||
private const val SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY = "SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY"
|
private const val SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY = "SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY"
|
||||||
|
@ -245,7 +246,7 @@ class VectorPreferences @Inject constructor(private val context: Context) {
|
||||||
SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY,
|
SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY,
|
||||||
SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY,
|
SETTINGS_LABS_SHOW_HIDDEN_EVENTS_PREFERENCE_KEY,
|
||||||
SETTINGS_LABS_ALLOW_EXTENDED_LOGS,
|
SETTINGS_LABS_ALLOW_EXTENDED_LOGS,
|
||||||
SETTINGS_LABS_USE_RESTRICTED_JOIN_RULE,
|
// SETTINGS_LABS_USE_RESTRICTED_JOIN_RULE,
|
||||||
SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY,
|
SETTINGS_DEVELOPER_MODE_FAIL_FAST_PREFERENCE_KEY,
|
||||||
|
|
||||||
SETTINGS_USE_RAGE_SHAKE_KEY,
|
SETTINGS_USE_RAGE_SHAKE_KEY,
|
||||||
|
@ -974,6 +975,10 @@ class VectorPreferences @Inject constructor(private val context: Context) {
|
||||||
return defaultPrefs.getBoolean(SETTINGS_LABS_SPACES_HOME_AS_ORPHAN, false)
|
return defaultPrefs.getBoolean(SETTINGS_LABS_SPACES_HOME_AS_ORPHAN, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun labsAutoReportUISI(): Boolean {
|
||||||
|
return defaultPrefs.getBoolean(SETTINGS_LABS_AUTO_REPORT_UISI, false)
|
||||||
|
}
|
||||||
|
|
||||||
fun prefSpacesShowAllRoomInHome(): Boolean {
|
fun prefSpacesShowAllRoomInHome(): Boolean {
|
||||||
return defaultPrefs.getBoolean(SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME,
|
return defaultPrefs.getBoolean(SETTINGS_PREF_SPACE_SHOW_ALL_ROOM_IN_HOME,
|
||||||
// migration of old property
|
// migration of old property
|
||||||
|
|
|
@ -16,13 +16,26 @@
|
||||||
|
|
||||||
package im.vector.app.features.settings
|
package im.vector.app.features.settings
|
||||||
|
|
||||||
|
// import im.vector.app.AutoRageShaker
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
import im.vector.app.core.preference.VectorSwitchPreference
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class VectorSettingsLabsFragment @Inject constructor() : VectorSettingsBaseFragment() {
|
class VectorSettingsLabsFragment @Inject constructor(
|
||||||
|
private val vectorPreferences: VectorPreferences,
|
||||||
|
// private val autoRageShaker: AutoRageShaker
|
||||||
|
) : VectorSettingsBaseFragment() {
|
||||||
|
|
||||||
override var titleRes = R.string.room_settings_labs_pref_title
|
override var titleRes = R.string.room_settings_labs_pref_title
|
||||||
override val preferenceXmlRes = R.xml.vector_settings_labs
|
override val preferenceXmlRes = R.xml.vector_settings_labs
|
||||||
|
|
||||||
override fun bindPref() {}
|
override fun bindPref() {
|
||||||
|
findPreference<VectorSwitchPreference>(VectorPreferences.SETTINGS_LABS_AUTO_REPORT_UISI)?.let { pref ->
|
||||||
|
pref.isChecked = vectorPreferences.labsAutoReportUISI()
|
||||||
|
pref.setOnPreferenceChangeListener { _, isChecked ->
|
||||||
|
// autoRageShaker.enable(isChecked as Boolean)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3565,6 +3565,11 @@
|
||||||
|
|
||||||
<string name="labs_use_restricted_join_rule">Experimental Space - Restricted Room.</string>
|
<string name="labs_use_restricted_join_rule">Experimental Space - Restricted Room.</string>
|
||||||
<string name="labs_use_restricted_join_rule_desc">Warning requires server support and experimental room version</string>
|
<string name="labs_use_restricted_join_rule_desc">Warning requires server support and experimental room version</string>
|
||||||
|
|
||||||
|
|
||||||
|
<string name="labs_auto_report_uisi">Auto Report Decryption Errors.</string>
|
||||||
|
<string name="labs_auto_report_uisi_desc">Your system will automatically send logs when an unable to decrypt error occurs</string>
|
||||||
|
|
||||||
<string name="user_invites_you">%s invites you</string>
|
<string name="user_invites_you">%s invites you</string>
|
||||||
|
|
||||||
<string name="looking_for_someone_not_in_space">Looking for someone not in %s?</string>
|
<string name="looking_for_someone_not_in_space">Looking for someone not in %s?</string>
|
||||||
|
|
|
@ -44,11 +44,6 @@
|
||||||
android:key="SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB"
|
android:key="SETTINGS_LABS_UNREAD_NOTIFICATIONS_AS_TAB"
|
||||||
android:title="@string/labs_show_unread_notifications_as_tab" />
|
android:title="@string/labs_show_unread_notifications_as_tab" />
|
||||||
|
|
||||||
<im.vector.app.core.preference.VectorSwitchPreference
|
|
||||||
android:defaultValue="false"
|
|
||||||
android:key="SETTINGS_LABS_USE_RESTRICTED_JOIN_RULE"
|
|
||||||
android:summary="@string/labs_use_restricted_join_rule_desc"
|
|
||||||
android:title="@string/labs_use_restricted_join_rule" />
|
|
||||||
|
|
||||||
<im.vector.app.core.preference.VectorSwitchPreference
|
<im.vector.app.core.preference.VectorSwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
|
@ -63,4 +58,10 @@
|
||||||
android:title="@string/labs_enable_polls" />
|
android:title="@string/labs_enable_polls" />
|
||||||
|
|
||||||
|
|
||||||
|
<im.vector.app.core.preference.VectorSwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="SETTINGS_LABS_AUTO_REPORT_UISI"
|
||||||
|
android:title="@string/labs_auto_report_uisi"
|
||||||
|
android:summary="@string/labs_auto_report_uisi_desc"/>
|
||||||
|
|
||||||
</androidx.preference.PreferenceScreen>
|
</androidx.preference.PreferenceScreen>
|
Loading…
Reference in a new issue