Merge branch 'develop' into feature/ons/live_location_service

* develop: (152 commits)
  Remove exhaustive.
  fixing the onboarding sanity test failing - adds tapping the new take me home button within the sanity test
  Fix lint issues on weblate sync
  fixing view model tests not collecting flow results - the switch from runBlockingTest to runTest means we need to provide a separate scope from the test in order to asynchronously collect the flow results
  Do not suggest collapse if there is only one section
  Translated using Weblate (Spanish)
  Translated using Weblate (Spanish)
  runBlocking -> runTest https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/MIGRATION.md
  runBlockingTest -> runTest https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/MIGRATION.md
  Small cleanup
  The `.exhaustive` trick is not needed anymore in Kotlin 1.6.0 https://kotlinlang.org/docs/whatsnew16.html#stable-exhaustive-when-statements-for-enum-sealed-and-boolean-subjects
  Also upgrade the coroutine lib
  Fix compilation warning (exhaustive when)
  Fix compilation warning (exhaustive when)
  Format file (no other change)
  Fix compilation warning (exhaustive when)
  Bump moshi from 1.12.0 to 1.13.0
  Bump kotlin-gradle-plugin from 1.5.31 to 1.6.0
  Code review fixes.
  fixing presence icon anchoring to the middle of the room icon - creates a secondary verification shield and aligns to the start of the room title when presence is present
  ...

# Conflicts:
#	vector/src/main/java/im/vector/app/features/location/LocationSharingFragment.kt
#	vector/src/main/java/im/vector/app/features/location/LocationSharingViewModel.kt
#	vector/src/main/res/values/strings.xml
This commit is contained in:
Onuray Sahin 2022-03-23 16:25:01 +03:00
commit f18a107df8
260 changed files with 3817 additions and 1446 deletions

View file

@ -26,7 +26,7 @@ jobs:
cancel-in-progress: true
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@ -50,7 +50,7 @@ jobs:
# Only runs on main, no concurrency.
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches

View file

@ -34,7 +34,7 @@ jobs:
uses: actions/setup-python@v3
with:
python-version: 3.8
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@ -43,7 +43,7 @@ jobs:
restore-keys: |
${{ runner.os }}-gradle-
- name: Start synapse server
uses: michaelkaye/setup-matrix-synapse@v0.3.0
uses: michaelkaye/setup-matrix-synapse@v0.4.0
with:
uploadLogs: true
httpPort: 8080
@ -221,7 +221,7 @@ jobs:
uses: actions/setup-python@v3
with:
python-version: 3.8
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@ -230,7 +230,7 @@ jobs:
restore-keys: |
${{ runner.os }}-gradle-
- name: Start synapse server
uses: michaelkaye/setup-matrix-synapse@v0.3.0
uses: michaelkaye/setup-matrix-synapse@v0.4.0
with:
uploadLogs: true
httpPort: 8080
@ -265,6 +265,7 @@ jobs:
failure_screenshots/
codecov-units:
name: Unit tests with code coverage
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
@ -272,7 +273,7 @@ jobs:
with:
distribution: 'adopt'
java-version: '11'
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@ -290,6 +291,7 @@ jobs:
build/reports/jacoco/allCodeCoverageReport/allCodeCoverageReport.xml
sonarqube:
name: Sonarqube upload
runs-on: macos-latest
if: always()
needs:
@ -300,7 +302,7 @@ jobs:
with:
distribution: 'adopt'
java-version: '11'
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@ -319,6 +321,7 @@ jobs:
# Notify the channel about scheduled runs, do not notify for manually triggered runs
notify:
name: Notify matrix
runs-on: ubuntu-latest
needs:
- integration-tests
@ -333,4 +336,4 @@ jobs:
matrix_access_token: ${{ secrets.ELEMENT_ANDROID_NOTIFICATION_ACCESS_TOKEN }}
matrix_room_id: ${{ secrets.ELEMENT_ANDROID_INTERNAL_ROOM_ID }}
text_template: "Nightly test run: {{#each job_statuses }}{{#with this }}{{#if completed }} {{name}} {{conclusion}} at {{completed_at}}, {{/if}}{{/with}}{{/each}}"
html_template: "Nightly test run results: {{#each job_statuses }}{{#with this }}{{#if completed }}<br />{{name}} {{conclusion}} at {{completed_at}} <a href=\"{{html_url}}\">[details]</a>{{/if}}{{/with}}{{/each}}"
html_template: "Nightly test run results: {{#each job_statuses }}{{#with this }}{{#if completed }}<br />{{icon conclusion}} {{name}} <font color='{{color conclusion}}'>{{conclusion}} at {{completed_at}} <a href=\"{{html_url}}\">[details]</a></font>{{/if}}{{/with}}{{/each}}"

View file

@ -97,7 +97,7 @@ jobs:
cancel-in-progress: true
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@ -130,7 +130,7 @@ jobs:
cancel-in-progress: true
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches

View file

@ -25,7 +25,7 @@ jobs:
with:
distribution: 'adopt'
java-version: 11
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches
@ -45,7 +45,7 @@ jobs:
cancel-in-progress: true
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v2
- uses: actions/cache@v3
with:
path: |
~/.gradle/caches

1
changelog.d/5347.misc Normal file
View file

@ -0,0 +1 @@
[Rooms list] Do not suggest collapse the unique section

1
changelog.d/5408.misc Normal file
View file

@ -0,0 +1 @@
Improved onboarding registration unit test coverage

1
changelog.d/5426.feature Normal file
View file

@ -0,0 +1 @@
Allow scrolling position of Voice Message playback

1
changelog.d/5489.bugfix Normal file
View file

@ -0,0 +1 @@
Fix presence indicator being aligned to the center of the room image

1
changelog.d/5513.misc Normal file
View file

@ -0,0 +1 @@
Added online presence indicator attribute online to match offline styling

1
changelog.d/5551.bugfix Normal file
View file

@ -0,0 +1 @@
Fix local echos not being shown when re-opening rooms

1
changelog.d/5564.bugfix Normal file
View file

@ -0,0 +1 @@
Fix sometimes read marker not properly updating

1
changelog.d/5571.feature Normal file
View file

@ -0,0 +1 @@
Live location sharing: Adding indicator view when enabled

2
changelog.d/5572.misc Normal file
View file

@ -0,0 +1,2 @@
Show stickers on click

View file

@ -9,13 +9,13 @@ ext.versions = [
def gradle = "7.0.4"
// Ref: https://kotlinlang.org/releases.html
def kotlin = "1.5.31"
def kotlinCoroutines = "1.5.2"
def kotlin = "1.6.0"
def kotlinCoroutines = "1.6.0"
def dagger = "2.40.5"
def retrofit = "2.9.0"
def arrow = "0.8.2"
def markwon = "4.6.2"
def moshi = "1.12.0"
def moshi = "1.13.0"
def lifecycle = "2.4.0"
def flowBinding = "1.2.0"
def epoxy = "4.6.2"

View file

@ -0,0 +1,2 @@
Principales cambios de esta versión: primera implementación de los hilos de mensajes. Burbujas de mensajes.
Todos los cambios en: https://github.com/vector-im/element-android/releases/tag/v1.4.0

View file

@ -0,0 +1,2 @@
Principales cambios de esta versión: añadir @room, encuestas cerradas y muchos cambios menores más.
Todos los cambios en: https://github.com/vector-im/element-android/releases/tag/v1.4.2

View file

@ -0,0 +1,2 @@
تغییرات اصلی در این نگارش: پیاده سازی نخستین پیام‌های رشته‌ای. حباب‌های پیام.
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.4.0

View file

@ -0,0 +1,2 @@
تغییرات اصلی در این نگارش: افزودن پشتیبانی به @room و نظرسنجی‌های فاش نشده در کنار تغییرات کوچک دیگر.
گزارش دگرگونی کامل: https://github.com/vector-im/element-android/releases/tag/v1.4.2

View file

@ -8,12 +8,13 @@ Az Element egy biztonságos üzenetküldő, és egy csapatmunka app, amely távo
- Videochat, VoIP, és képernyőmegosztási lehetőséggel
- Egyszerű integráció a kedvenc online kollaborációs eszközeiddel, projektkezelési eszközökkel, VoIP szolgáltatásokkal, és más csoportos üzenetküldő alkalmazásokkal
Element is completely different from other messaging and collaboration apps. It operates on Matrix, an open network for secure messaging and decentralized communication. It allows self-hosting to give users maximum ownership and control of their data and messages.
Az Element teljesen más, mint az összes többi üzenetküldő és kollaborációs alkalmazás. A biztonságos üzenetküldést és decentralizált kommunikációt biztosító Matrix platformot használja. Akár egyénileg üzemeltetett szervereket is lehet használni az adatok teljes kontrollálása érdekében.
<b>Privacy and encrypted messaging</b>
Element protects you from unwanted ads, data mining and walled gardens. It also secures all your data, one-to-one video and voice communication through end-to-end encryption and cross-signed device verification.
<b>Magánszféra és titkosított csevegés</b>
Az Element megvéd a nemkívánatos hirdetésektől, adatbányászattól, és a zárt platformoktól. Ezeken felül biztonságban tartja az összes adatod és 1:1 hívásod a végponti titkosításnak és az eszközök-közti hitelesítésnek köszönhetően.
Az Element átadja neked az irányítást a magánszférád felett, miközben lehetővé teszi, hogy biztonságosan kommunikálj bárkivel a Matrix hálózatban, vagy a többi üzleti kommunikációs eszközt használókkal, az olyan appok integrálásának köszönhetően, mint például a Slack.
Element gives you control over your privacy while allowing you to communicate securely with anyone on the Matrix network, or other business collaboration tools by integrating with apps such as Slack.
<b>Element can be self-hosted</b>
To allow more control of your sensitive data and conversations, Element can be self-hosted or you can choose any Matrix-based host - the standard for open source, decentralized communication. Element gives you privacy, security compliance and integration flexibility.

View file

@ -0,0 +1,2 @@
Основные изменения в этой версии: Начальная реализация веток сообщений. Сообщения пузыри.
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.4.0

View file

@ -0,0 +1,2 @@
Основные изменения в этой версии: добавлена поддержка @room и нераскрытых опросов, а также множество других мелких изменений.
Полный список изменений: https://github.com/vector-im/element-android/releases/tag/v1.4.2

View file

@ -59,7 +59,7 @@ dependencies {
implementation libs.jetbrains.coroutinesCore
implementation libs.jetbrains.coroutinesAndroid
testImplementation 'org.json:json:20211205'
testImplementation 'org.json:json:20220320'
testImplementation libs.tests.junit
androidTestImplementation libs.androidx.junit
androidTestImplementation libs.androidx.espressoCore

View file

@ -20,7 +20,6 @@ import android.content.Context
import android.view.View
import com.airbnb.epoxy.TypedEpoxyController
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Success
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import me.gujun.android.span.Span
import me.gujun.android.span.span
@ -44,10 +43,8 @@ internal class JSonViewerEpoxyController(private val context: Context) :
text(async.error.localizedMessage?.toEpoxyCharSequence())
}
}
is Success -> {
val model = data.root.invoke()
model?.let {
else -> {
async.invoke()?.let {
buildRec(it, 0, "")
}
}

View file

@ -60,6 +60,4 @@ dependencies {
implementation 'com.github.vector-im:PFLockScreen-Android:1.0.0-beta12'
// dialpad dimen
implementation 'im.dlg:android-dialer:1.2.5'
// AudioRecordView attr
implementation 'com.github.Armen101:AudioRecordView:1.0.5'
}

View file

@ -122,6 +122,10 @@
<color name="vctr_presence_indicator_offline_light">@color/palette_gray_100</color>
<color name="vctr_presence_indicator_offline_dark">@color/palette_gray_450</color>
<attr name="vctr_presence_indicator_online" format="color" />
<color name="vctr_presence_indicator_online_light">@color/palette_element_green</color>
<color name="vctr_presence_indicator_online_dark">@color/palette_element_green</color>
<!-- Location sharing colors -->
<attr name="vctr_live_location" format="color" />
<color name="vctr_live_location_light">@color/palette_prune</color>

View file

@ -53,5 +53,4 @@
<color name="element_room_01">@color/palette_verde</color>
<color name="element_room_02">@color/palette_azure</color>
<color name="element_room_03">@color/palette_grape</color>
</resources>

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="AudioWaveformView">
<attr name="alignment" format="enum">
<enum name="center" value="0" />
<enum name="bottom" value="1" />
<enum name="top" value="2" />
</attr>
<attr name="flow" format="enum">
<enum name="leftToRight" value="0" />
<enum name="rightToLeft" value="1" />
</attr>
<attr name="verticalPadding" format="dimension" />
<attr name="horizontalPadding" format="dimension" />
<attr name="barWidth" format="dimension" />
<attr name="barSpace" format="dimension" />
<attr name="barMinHeight" format="dimension" />
<attr name="isBarRounded" format="boolean" />
</declare-styleable>
</resources>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Widget.Vector.Button.Text.OnPrimary.LocationLive">
<item name="android:background">?selectableItemBackground</item>
<item name="android:textSize">12sp</item>
<item name="android:padding">0dp</item>
<item name="android:gravity">center</item>
</style>
</resources>

View file

@ -2,14 +2,14 @@
<resources>
<style name="VoicePlaybackWaveform">
<item name="chunkColor">?vctr_content_secondary</item>
<item name="chunkAlignTo">center</item>
<item name="chunkMinHeight">1dp</item>
<item name="chunkRoundedCorners">true</item>
<item name="chunkSoftTransition">true</item>
<item name="chunkSpace">2dp</item>
<item name="chunkWidth">2dp</item>
<item name="direction">rightToLeft</item>
<item name="alignment">center</item>
<item name="flow">leftToRight</item>
<item name="verticalPadding">4dp</item>
<item name="horizontalPadding">4dp</item>
<item name="barWidth">2dp</item>
<item name="barSpace">2dp</item>
<item name="barMinHeight">1dp</item>
<item name="isBarRounded">true</item>
</style>
</resources>

View file

@ -43,6 +43,7 @@
<!-- Presence Indicator colors -->
<item name="vctr_presence_indicator_offline">@color/vctr_presence_indicator_offline_dark</item>
<item name="vctr_presence_indicator_online">@color/vctr_presence_indicator_online_dark</item>
<!-- Some aliases -->
<item name="vctr_header_background">?vctr_system</item>

View file

@ -43,6 +43,7 @@
<!-- Presence Indicator colors -->
<item name="vctr_presence_indicator_offline">@color/vctr_presence_indicator_offline_light</item>
<item name="vctr_presence_indicator_online">@color/vctr_presence_indicator_online_light</item>
<!-- Some aliases -->
<item name="vctr_header_background">?vctr_system</item>

View file

@ -64,7 +64,11 @@ data class MatrixConfiguration(
/**
* True to enable presence information sync (if available). False to disable regardless of server setting.
*/
val presenceSyncEnabled: Boolean = true
val presenceSyncEnabled: Boolean = true,
/**
* Thread messages default enable/disabled value
*/
val threadMessagesEnabledDefault: Boolean = false,
) {
/**

View file

@ -29,7 +29,6 @@ import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64Safe
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.verification.DefaultVerificationTransaction
import org.matrix.android.sdk.internal.crypto.verification.ValidVerificationInfoStart
import org.matrix.android.sdk.internal.util.exhaustive
import timber.log.Timber
internal class DefaultQrCodeVerificationTransaction(
@ -129,7 +128,7 @@ internal class DefaultQrCodeVerificationTransaction(
// Nothing special here, we will send a reciprocate start event, and then the other session will trust it's view of the MSK
}
}
}.exhaustive
}
val toVerifyDeviceIds = mutableListOf<String>()
@ -174,7 +173,7 @@ internal class DefaultQrCodeVerificationTransaction(
Unit
}
}
}.exhaustive
}
if (!canTrustOtherUserMasterKey && toVerifyDeviceIds.isEmpty()) {
// Nothing to verify
@ -272,6 +271,7 @@ internal class DefaultQrCodeVerificationTransaction(
// I now know that i can trust my MSK
trust(true, emptyList(), true)
}
null -> Unit
}
}

View file

@ -19,15 +19,19 @@ package org.matrix.android.sdk.internal.database.lightweight
import android.content.Context
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import org.matrix.android.sdk.api.MatrixConfiguration
import javax.inject.Inject
/**
* The purpose of this class is to provide an alternative and lightweight way to store settings/data
* on the sdi without using the database. This should be used just for sdk/user preferences and
* on the sdk without using the database. This should be used just for sdk/user preferences and
* not for large data sets
*/
class LightweightSettingsStorage @Inject constructor(context: Context) {
class LightweightSettingsStorage @Inject constructor(
context: Context,
private val matrixConfiguration: MatrixConfiguration
) {
private val sdkDefaultPrefs = PreferenceManager.getDefaultSharedPreferences(context.applicationContext)
@ -38,7 +42,7 @@ class LightweightSettingsStorage @Inject constructor(context: Context) {
}
fun areThreadMessagesEnabled(): Boolean {
return sdkDefaultPrefs.getBoolean(MATRIX_SDK_SETTINGS_THREAD_MESSAGES_ENABLED, false)
return sdkDefaultPrefs.getBoolean(MATRIX_SDK_SETTINGS_THREAD_MESSAGES_ENABLED, matrixConfiguration.threadMessagesEnabledDefault)
}
companion object {

View file

@ -80,8 +80,8 @@ internal class WorkManagerProvider @Inject constructor(
workManager.enqueue(checkWorkerRequest)
val checkWorkerLiveState = workManager.getWorkInfoByIdLiveData(checkWorkerRequest.id)
val observer = object : Observer<WorkInfo> {
override fun onChanged(workInfo: WorkInfo) {
if (workInfo.state.isFinished) {
override fun onChanged(workInfo: WorkInfo?) {
if (workInfo?.state?.isFinished == true) {
checkWorkerLiveState.removeObserver(this)
if (workInfo.state == WorkInfo.State.FAILED) {
throw RuntimeException("MatrixWorkerFactory is not being set on your worker configuration.\n" +

View file

@ -44,7 +44,7 @@ internal interface FetchThreadSummariesTask : Task<FetchThreadSummariesTask.Para
data class Params(
val roomId: String,
val from: String = "",
val limit: Int = 100,
val limit: Int = 500,
val isUserParticipating: Boolean = true
)
}

View file

@ -314,6 +314,7 @@ internal class RoomSummaryDataSource @Inject constructor(
RoomCategoryFilter.ONLY_ROOMS -> query.equalTo(RoomSummaryEntityFields.IS_DIRECT, false)
RoomCategoryFilter.ONLY_WITH_NOTIFICATIONS -> query.greaterThan(RoomSummaryEntityFields.NOTIFICATION_COUNT, 0)
RoomCategoryFilter.ALL -> Unit // nop
null -> Unit
}
// Timber.w("VAL: activeSpaceId : ${queryParams.activeSpaceId}")

View file

@ -55,6 +55,7 @@ internal class RealmSendingEventsDataSource(
roomEntity = RoomEntity.where(safeRealm, roomId = roomId).findFirst()
sendingTimelineEvents = roomEntity?.sendingTimelineEvents
sendingTimelineEvents?.addChangeListener(sendingTimelineEventsListener)
updateFrozenResults(sendingTimelineEvents)
}
override fun stop() {

View file

@ -83,11 +83,15 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
isLastBackward.set(chunkEntity.isLastBackward)
}
if (changeSet.isFieldChanged(ChunkEntityFields.NEXT_CHUNK.`$`)) {
nextChunk = createTimelineChunk(chunkEntity.nextChunk)
nextChunk = createTimelineChunk(chunkEntity.nextChunk).also {
it?.prevChunk = this
}
nextChunkLatch?.complete(Unit)
}
if (changeSet.isFieldChanged(ChunkEntityFields.PREV_CHUNK.`$`)) {
prevChunk = createTimelineChunk(chunkEntity.prevChunk)
prevChunk = createTimelineChunk(chunkEntity.prevChunk).also {
it?.nextChunk = this
}
prevChunkLatch?.complete(Unit)
}
}
@ -194,7 +198,9 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
when {
nextChunkEntity != null -> {
if (nextChunk == null) {
nextChunk = createTimelineChunk(nextChunkEntity)
nextChunk = createTimelineChunk(nextChunkEntity).also {
it?.prevChunk = this
}
}
nextChunk?.loadMore(offsetCount, direction, fetchFromServerIfNeeded) ?: LoadMoreResult.FAILURE
}
@ -210,7 +216,9 @@ internal class TimelineChunk(private val chunkEntity: ChunkEntity,
when {
prevChunkEntity != null -> {
if (prevChunk == null) {
prevChunk = createTimelineChunk(prevChunkEntity)
prevChunk = createTimelineChunk(prevChunkEntity).also {
it?.nextChunk = this
}
}
prevChunk?.loadMore(offsetCount, direction, fetchFromServerIfNeeded) ?: LoadMoreResult.FAILURE
}

View file

@ -1,20 +0,0 @@
/*
* 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.util
// Trick to ensure that when block is exhaustive
internal val <T> T.exhaustive: T get() = this

View file

@ -16,7 +16,8 @@
package org.matrix.android.sdk.internal.session.pushers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.amshove.kluent.internal.assertFailsWith
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
@ -39,6 +40,7 @@ private val A_JSON_PUSHER = JsonPusher(
data = JsonPusherData(brand = "Element")
)
@ExperimentalCoroutinesApi
class DefaultAddPusherTaskTest {
private val pushersAPI = FakePushersAPI()
@ -55,7 +57,7 @@ class DefaultAddPusherTaskTest {
fun `given no persisted pusher when adding Pusher then updates api and inserts result with Registered state`() {
monarchy.givenWhereReturns<PusherEntity>(result = null)
runBlocking { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) }
runTest { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) }
pushersAPI.verifySetPusher(A_JSON_PUSHER)
monarchy.verifyInsertOrUpdate<PusherEntity> {
@ -70,7 +72,7 @@ class DefaultAddPusherTaskTest {
val realmResult = PusherEntity(appDisplayName = null)
monarchy.givenWhereReturns(result = realmResult)
runBlocking { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) }
runTest { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) }
pushersAPI.verifySetPusher(A_JSON_PUSHER)
@ -85,7 +87,7 @@ class DefaultAddPusherTaskTest {
pushersAPI.givenSetPusherErrors(SocketException())
assertFailsWith<SocketException> {
runBlocking { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) }
runTest { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) }
}
realmResult.state shouldBeEqualTo PusherState.FAILED_TO_REGISTER
@ -97,7 +99,7 @@ class DefaultAddPusherTaskTest {
pushersAPI.givenSetPusherErrors(SocketException())
assertFailsWith<SocketException> {
runBlocking { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) }
runTest { addPusherTask.execute(AddPusherTask.Params(A_JSON_PUSHER)) }
}
}
}

View file

@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.session.space
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.test.runTest
import okhttp3.ResponseBody.Companion.toResponseBody
import org.amshove.kluent.shouldBeEqualTo
import org.junit.Test
@ -35,7 +35,7 @@ internal class DefaultResolveSpaceInfoTaskTest {
private val resolveSpaceInfoTask = DefaultResolveSpaceInfoTask(spaceApi.instance, globalErrorReceiver)
@Test
fun `given stable endpoint works, when execute, then return stable api data`() = runBlockingTest {
fun `given stable endpoint works, when execute, then return stable api data`() = runTest {
spaceApi.givenStableEndpointReturns(response)
val result = resolveSpaceInfoTask.execute(spaceApi.params)
@ -44,7 +44,7 @@ internal class DefaultResolveSpaceInfoTaskTest {
}
@Test
fun `given stable endpoint fails, when execute, then fallback to unstable endpoint`() = runBlockingTest {
fun `given stable endpoint fails, when execute, then fallback to unstable endpoint`() = runTest {
spaceApi.givenStableEndpointThrows(httpException)
spaceApi.givenUnstableEndpointReturns(response)

View file

@ -21,7 +21,7 @@ import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.delay
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Test
import org.matrix.android.sdk.MatrixTest
@ -51,7 +51,7 @@ class CoroutineSequencersTest : MatrixTest {
.also { results.add(it) }
}
)
runBlocking {
runTest {
jobs.joinAll()
}
assertEquals(3, results.size)
@ -81,7 +81,7 @@ class CoroutineSequencersTest : MatrixTest {
.also { results.add(it) }
}
)
runBlocking {
runTest {
jobs.joinAll()
}
assertEquals(3, results.size)
@ -109,7 +109,7 @@ class CoroutineSequencersTest : MatrixTest {
)
// We are canceling the second job
jobs[1].cancel()
runBlocking {
runTest {
jobs.joinAll()
}
assertEquals(2, results.size)

View file

@ -13,6 +13,7 @@ print("::group::Arguments")
print(f"{sys.argv}")
print("::endgroup::")
for xmlfile in xmlfiles:
try:
tree = ET.parse(xmlfile)
root = tree.getroot()
@ -40,5 +41,7 @@ for xmlfile in xmlfiles:
print(child.text)
body = f" passed={success} failures={failures} errors={errors} skipped={skipped}"
print(f"::set-output name={suitename}::={body}")
except FileNotFoundError:
print(f"::error::Unable to open test results file {xmlfile} - check if the tests completed")
print("::endgroup::")

View file

@ -2056,7 +2056,9 @@
"disappear",
"dissolve",
"liquid",
"melt"
"melt",
"hot",
"heat"
]
},
"winking-face": {
@ -2351,7 +2353,10 @@
"disbelief",
"embarrass",
"scared",
"surprise"
"surprise",
"silence",
"secret",
"shock"
]
},
"face-with-peeking-eye": {
@ -2360,7 +2365,10 @@
"j": [
"captivated",
"peep",
"stare"
"stare",
"scared",
"frightening",
"embarrassing"
]
},
"shushing-face": {
@ -2392,7 +2400,8 @@
"salute",
"sunny",
"troops",
"yes"
"yes",
"respect"
]
},
"zippermouth-face": {
@ -2467,7 +2476,10 @@
"disappear",
"hide",
"introvert",
"invisible"
"invisible",
"lonely",
"isolation",
"depression"
]
},
"face-in-clouds": {
@ -2863,7 +2875,11 @@
"disappointed",
"meh",
"skeptical",
"unsure"
"unsure",
"skeptic",
"confuse",
"frustrated",
"indifferent"
]
},
"worried-face": {
@ -2969,7 +2985,9 @@
"cry",
"proud",
"resist",
"sad"
"sad",
"touched",
"gratitude"
]
},
"frowning-face-with-open-mouth": {
@ -4065,7 +4083,9 @@
"j": [
"hand",
"right",
"rightward"
"rightward",
"palm",
"offer"
]
},
"leftwards-hand": {
@ -4074,7 +4094,9 @@
"j": [
"hand",
"left",
"leftward"
"leftward",
"palm",
"offer"
]
},
"palm-down-hand": {
@ -4083,7 +4105,8 @@
"j": [
"dismiss",
"drop",
"shoo"
"shoo",
"palm"
]
},
"palm-up-hand": {
@ -4093,7 +4116,9 @@
"beckon",
"catch",
"come",
"offer"
"offer",
"lift",
"demand"
]
},
"ok-hand": {
@ -4290,7 +4315,8 @@
"b": "1FAF5",
"j": [
"point",
"you"
"you",
"recruit"
]
},
"thumbs-up": {
@ -4404,7 +4430,9 @@
"a": "⊛ Heart Hands",
"b": "1FAF6",
"j": [
"love"
"love",
"appreciation",
"support"
]
},
"open-hands": {
@ -4662,7 +4690,11 @@
"flirting",
"nervous",
"uncomfortable",
"worried"
"worried",
"flirt",
"sexy",
"pain",
"worry"
]
},
"baby": {
@ -6058,7 +6090,8 @@
"monarch",
"noble",
"regal",
"royalty"
"royalty",
"power"
]
},
"prince": {
@ -6231,7 +6264,8 @@
"belly",
"bloated",
"full",
"pregnant"
"pregnant",
"baby"
]
},
"pregnant-person": {
@ -6241,7 +6275,8 @@
"belly",
"bloated",
"full",
"pregnant"
"pregnant",
"baby"
]
},
"breastfeeding": {
@ -6635,7 +6670,8 @@
"j": [
"fairy tale",
"fantasy",
"monster"
"monster",
"mystical"
]
},
"person-getting-massage": {
@ -9374,7 +9410,8 @@
"b": "1FAB8",
"j": [
"ocean",
"reef"
"reef",
"sea"
]
},
"snail": {
@ -9587,7 +9624,9 @@
"Hinduism",
"India",
"purity",
"Vietnam"
"Vietnam",
"calm",
"meditation"
]
},
"rosette": {
@ -9832,14 +9871,16 @@
"a": "⊛ Empty Nest",
"b": "1FAB9",
"j": [
"nesting"
"nesting",
"bird"
]
},
"nest-with-eggs": {
"a": "⊛ Nest with Eggs",
"b": "1FABA",
"j": [
"nesting"
"nesting",
"bird"
]
},
"grapes": {
@ -11187,7 +11228,9 @@
"drink",
"empty",
"glass",
"spill"
"spill",
"cup",
"water"
]
},
"cup-with-straw": {
@ -12003,7 +12046,9 @@
"b": "1F6DD",
"j": [
"amusement park",
"play"
"play",
"fun",
"park"
]
},
"ferris-wheel": {
@ -12533,7 +12578,9 @@
"j": [
"circle",
"tire",
"turn"
"turn",
"car",
"transport"
]
},
"police-car-light": {
@ -14666,7 +14713,8 @@
"hand",
"Mary",
"Miriam",
"protection"
"protection",
"religion"
]
},
"video-game": {
@ -15864,7 +15912,9 @@
"b": "1FAAB",
"j": [
"electronic",
"low energy"
"low energy",
"drained",
"dead"
]
},
"electric-plug": {
@ -17508,7 +17558,9 @@
"disability",
"hurt",
"mobility aid",
"stick"
"stick",
"accessibility",
"assist"
]
},
"stethoscope": {
@ -17528,7 +17580,9 @@
"bones",
"doctor",
"medical",
"skeleton"
"skeleton",
"x-ray",
"medicine"
]
},
"door": {
@ -17733,7 +17787,10 @@
"burp",
"clean",
"soap",
"underwater"
"underwater",
"fun",
"carbonation",
"sparkling"
]
},
"toothbrush": {
@ -17856,7 +17913,8 @@
"credentials",
"ID",
"license",
"security"
"security",
"document"
]
},
"atm-sign": {

View file

@ -7,7 +7,6 @@ import com.airbnb.mvrx.ViewModelContext
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import dagger.assisted.AssistedFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
<#if createViewEvents>
@ -42,6 +41,6 @@ class ${viewModelClass} @AssistedInject constructor(@Assisted initialState: ${vi
override fun handle(action: ${actionClass}) {
when (action) {
}.exhaustive
}
}
}

View file

@ -36,8 +36,9 @@
<!-- Level 1: Security and Privacy -->
<!-- Level 1: Labs -->
<bool name="settings_labs_thread_messages_default">false</bool>
<!-- Level 1: Advcanced settings -->
<!-- Level 1: Advanced settings -->
<!-- Level 1: Help and about -->

View file

@ -411,7 +411,6 @@ dependencies {
implementation 'jp.wasabeef:glide-transformations:4.3.0'
implementation 'com.github.vector-im:PFLockScreen-Android:1.0.0-beta12'
implementation 'com.github.hyuwah:DraggableView:1.0.0'
implementation 'com.github.Armen101:AudioRecordView:1.0.5'
// Custom Tab
implementation 'androidx.browser:browser:1.4.0'

View file

@ -21,6 +21,7 @@ import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.pressBack
import androidx.test.espresso.matcher.ViewMatchers.isRoot
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import com.adevinta.android.barista.assertion.BaristaEnabledAssertions.assertDisabled
import com.adevinta.android.barista.assertion.BaristaEnabledAssertions.assertEnabled
import com.adevinta.android.barista.assertion.BaristaVisibilityAssertions.assertDisplayed
@ -55,6 +56,8 @@ class OnboardingRobot {
fun createAccount(userId: String, password: String = "password", homeServerUrl: String = "http://10.0.2.2:8080") {
initSession(true, userId, password, homeServerUrl)
waitUntilViewVisible(withText(R.string.ftue_account_created_congratulations_title))
clickOn(R.string.ftue_account_created_take_me_home)
}
fun login(userId: String, password: String = "password", homeServerUrl: String = "http://10.0.2.2:8080") {

View file

@ -22,7 +22,6 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.analytics.store.AnalyticsStore
@ -53,7 +52,7 @@ class DebugAnalyticsViewModel @AssistedInject constructor(
override fun handle(action: DebugAnalyticsViewActions) {
when (action) {
DebugAnalyticsViewActions.ResetAnalyticsOptInDisplayed -> handleResetAnalyticsOptInDisplayed()
}.exhaustive
}
}
private fun handleResetAnalyticsOptInDisplayed() {

View file

@ -22,7 +22,6 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.debug.features.DebugVectorOverrides
@ -71,7 +70,7 @@ class DebugPrivateSettingsViewModel @AssistedInject constructor(
is DebugPrivateSettingsViewActions.SetForceLoginFallbackEnabled -> handleSetForceLoginFallbackEnabled(action)
is SetDisplayNameCapabilityOverride -> handSetDisplayNameCapabilityOverride(action)
is SetAvatarCapabilityOverride -> handSetAvatarCapabilityOverride(action)
}.exhaustive
}
}
private fun handleSetDialPadVisibility(action: DebugPrivateSettingsViewActions.SetDialPadVisibility) {

View file

@ -437,11 +437,6 @@ THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
<br/>
Copyright (c) 2017-present, dialog LLC &lt;info@dlg.im&gt;
</li>
<li>
<b>Armen101 / AudioRecordView</b>
<br/>
Copyright 2019 Armen Gevorgyan
</li>
</ul>
<pre>
Apache License

View file

@ -46,6 +46,7 @@ import im.vector.app.features.navigation.Navigator
import im.vector.app.features.pin.PinCodeStore
import im.vector.app.features.pin.SharedPrefPinCodeStore
import im.vector.app.features.room.VectorRoomDisplayNameFallbackProvider
import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.ui.SharedPreferencesUiStateRepository
import im.vector.app.features.ui.UiStateRepository
import kotlinx.coroutines.CoroutineScope
@ -113,10 +114,13 @@ object VectorStaticModule {
}
@Provides
fun providesMatrixConfiguration(vectorRoomDisplayNameFallbackProvider: VectorRoomDisplayNameFallbackProvider): MatrixConfiguration {
fun providesMatrixConfiguration(
vectorPreferences: VectorPreferences,
vectorRoomDisplayNameFallbackProvider: VectorRoomDisplayNameFallbackProvider): MatrixConfiguration {
return MatrixConfiguration(
applicationFlavor = BuildConfig.FLAVOR_DESCRIPTION,
roomDisplayNameFallbackProvider = vectorRoomDisplayNameFallbackProvider,
threadMessagesEnabledDefault = vectorPreferences.areThreadMessagesEnabled(),
presenceSyncEnabled = BuildConfig.PRESENCE_SYNC_ENABLED
)
}

View file

@ -1,20 +0,0 @@
/*
* 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.core.extensions
// Trick to ensure that when block is exhaustive
val <T> T.exhaustive: T get() = this

View file

@ -54,7 +54,6 @@ import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.ActivityEntryPoint
import im.vector.app.core.dialogs.DialogLocker
import im.vector.app.core.dialogs.UnrecognizedCertificateDialog
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.extensions.observeEvent
import im.vector.app.core.extensions.observeNotNull
import im.vector.app.core.extensions.registerStartForActivityResult
@ -267,7 +266,7 @@ abstract class VectorBaseActivity<VB : ViewBinding> : AppCompatActivity(), Maver
is GlobalError.CertificateError ->
handleCertificateError(globalError)
GlobalError.ExpiredAccount -> Unit // TODO Handle account expiration
}.exhaustive
}
}
private fun handleCertificateError(certificateError: GlobalError.CertificateError) {

View file

@ -83,6 +83,7 @@ class PushRulePreference : VectorPreference {
NotificationIndex.NOISY -> {
radioGroup?.check(R.id.bingPreferenceRadioBingRuleNoisy)
}
null -> Unit
}
radioGroup?.setOnCheckedChangeListener { _, checkedId ->

View file

@ -77,13 +77,10 @@ class KeysBackupBanner @JvmOverloads constructor(
override fun onClick(v: View?) {
when (state) {
is State.Setup -> {
delegate?.setupKeysBackup()
}
is State.Setup -> delegate?.setupKeysBackup()
is State.Update,
is State.Recover -> {
delegate?.recoverKeysBackup()
}
is State.Recover -> delegate?.recoverKeysBackup()
else -> Unit
}
}

View file

@ -27,7 +27,6 @@ import androidx.core.text.italic
import im.vector.app.R
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.error.ResourceLimitErrorFormatter
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.utils.DimensionConverter
import im.vector.app.databinding.ViewNotificationAreaBinding
import im.vector.app.features.themes.ThemeUtils
@ -77,7 +76,7 @@ class NotificationAreaView @JvmOverloads constructor(
is State.UnsupportedAlgorithm -> renderUnsupportedAlgorithm(newState)
is State.Tombstone -> renderTombstone()
is State.ResourceLimitExceededError -> renderResourceLimitExceededError(newState)
}.exhaustive
}
}
// PRIVATE METHODS ****************************************************************************************************************************************

View file

@ -49,6 +49,7 @@ class PresenceStateImageView @JvmOverloads constructor(
setImageResource(R.drawable.ic_presence_offline)
contentDescription = context.getString(R.string.a11y_presence_offline)
}
null -> Unit
}
}
}

View file

@ -65,6 +65,7 @@ class ShieldImageView @JvmOverloads constructor(
contentDescription = context.getString(R.string.a11y_trust_level_trusted)
setImageResource(R.drawable.ic_warning_badge)
}
null -> Unit
}
}
}

View file

@ -241,7 +241,7 @@ class MainActivity : VectorBaseActivity<ActivityMainBinding>(), UnlockedActivity
// We have a session.
// Check it can be opened
if (sessionHolder.getActiveSession().isOpenable) {
HomeActivity.newIntent(this)
HomeActivity.newIntent(this, existingSession = true)
} else {
// The token is still invalid
navigator.softLogout(this)

View file

@ -22,7 +22,6 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.analytics.VectorAnalytics
import kotlinx.coroutines.launch
@ -55,7 +54,7 @@ class AnalyticsConsentViewModel @AssistedInject constructor(
override fun handle(action: AnalyticsConsentViewActions) {
when (action) {
is AnalyticsConsentViewActions.SetUserConsent -> handleSetUserConsent(action)
}.exhaustive
}
}
private fun handleSetUserConsent(action: AnalyticsConsentViewActions.SetUserConsent) {

View file

@ -19,7 +19,6 @@ package im.vector.app.features.analytics.ui.consent
import com.airbnb.mvrx.viewModel
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.addFragment
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.ScreenOrientationLocker
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivitySimpleBinding
@ -48,7 +47,7 @@ class AnalyticsOptInActivity : VectorBaseActivity<ActivitySimpleBinding>() {
viewModel.observeViewEvents {
when (it) {
AnalyticsOptInViewEvents.OnDataSaved -> finish()
}.exhaustive
}
}
}
}

View file

@ -17,7 +17,6 @@
package im.vector.app.features.attachments.preview
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
class AttachmentsPreviewViewModel(initialState: AttachmentsPreviewViewState) :
@ -28,7 +27,7 @@ class AttachmentsPreviewViewModel(initialState: AttachmentsPreviewViewState) :
is AttachmentsPreviewAction.SetCurrentAttachment -> handleSetCurrentAttachment(action)
is AttachmentsPreviewAction.UpdatePathOfCurrentAttachment -> handleUpdatePathOfCurrentAttachment(action)
AttachmentsPreviewAction.RemoveCurrentAttachment -> handleRemoveCurrentAttachment()
}.exhaustive
}
}
private fun handleRemoveCurrentAttachment() = withState {

View file

@ -111,6 +111,7 @@ class CallControlsView @JvmOverloads constructor(
views.ringingControls.isVisible = false
views.connectedControls.isVisible = false
}
null -> Unit
}
}

View file

@ -525,8 +525,7 @@ class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallContro
navigator.openCallTransfer(this, callTransferActivityResultLauncher, callId)
}
is VectorCallViewEvents.FailToTransfer -> showSnackbar(getString(R.string.call_transfer_failure))
null -> {
}
else -> Unit
}
}

View file

@ -26,7 +26,6 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.call.audio.CallAudioManager
import im.vector.app.features.call.dialpad.DialPadLookup
@ -343,7 +342,7 @@ class VectorCallViewModel @AssistedInject constructor(
setState { VectorCallViewState(action.callArgs) }
setupCallWithCurrentState()
}
}.exhaustive
}
}
private fun handleCallTransfer() {
@ -358,7 +357,7 @@ class VectorCallViewModel @AssistedInject constructor(
when (result) {
is CallTransferResult.ConnectWithUserId -> connectWithUserId(result)
is CallTransferResult.ConnectWithPhoneNumber -> connectWithPhoneNumber(result)
}.exhaustive
}
}
private fun connectWithUserId(result: CallTransferResult.ConnectWithUserId) {

View file

@ -27,7 +27,6 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
@ -103,7 +102,7 @@ class JitsiCallViewModel @AssistedInject constructor(
when (action) {
is JitsiCallViewActions.SwitchTo -> handleSwitchTo(action)
JitsiCallViewActions.OnConferenceLeft -> handleOnConferenceLeft()
}.exhaustive
}
}
private fun handleSwitchTo(action: JitsiCallViewActions.SwitchTo) = withState { state ->

View file

@ -35,7 +35,6 @@ import com.facebook.react.modules.core.PermissionListener
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityJitsiBinding
import kotlinx.parcelize.Parcelize
@ -79,7 +78,7 @@ class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMee
JitsiCallViewEvents.FailJoiningConference -> handleFailJoining()
JitsiCallViewEvents.Finish -> finish()
JitsiCallViewEvents.LeaveConference -> handleLeaveConference()
}.exhaustive
}
}
lifecycle.addObserver(ConferenceEventObserver(this, this::onBroadcastEvent))
}

View file

@ -26,7 +26,6 @@ import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityCallTransferBinding
import kotlinx.parcelize.Parcelize
@ -57,7 +56,7 @@ class CallTransferActivity : VectorBaseActivity<ActivityCallTransferBinding>() {
callTransferViewModel.observeViewEvents {
when (it) {
is CallTransferViewEvents.Complete -> handleComplete()
}.exhaustive
}
}
sectionsPagerAdapter = CallTransferPagerAdapter(this)

View file

@ -26,7 +26,6 @@ import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.withState
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.showIdentityServerConsentDialog
@ -73,7 +72,7 @@ class ContactsBookFragment @Inject constructor(
when (it) {
is ContactsBookViewEvents.Failure -> showFailure(it.throwable)
is ContactsBookViewEvents.OnPoliciesRetrieved -> showConsentDialog(it)
}.exhaustive
}
}
}

View file

@ -27,7 +27,6 @@ import im.vector.app.core.contacts.ContactsDataSource
import im.vector.app.core.contacts.MappedContact
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.discovery.fetchIdentityServerWithTerms
@ -165,7 +164,7 @@ class ContactsBookViewModel @AssistedInject constructor(
is ContactsBookAction.OnlyBoundContacts -> handleOnlyBoundContacts(action)
ContactsBookAction.UserConsentGranted -> handleUserConsentGranted()
ContactsBookAction.UserConsentRequest -> handleUserConsentRequest()
}.exhaustive
}
}
private fun handleUserConsentRequest() {

View file

@ -28,6 +28,7 @@ import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.viewModel
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
@ -35,7 +36,6 @@ import im.vector.app.R
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.extensions.addFragment
import im.vector.app.core.extensions.addFragmentToBackstack
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.SimpleFragmentActivity
import im.vector.app.core.platform.WaitingViewData
import im.vector.app.core.utils.PERMISSIONS_FOR_MEMBERS_SEARCH
@ -84,7 +84,7 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
is UserListSharedAction.OnMenuItemSelected -> onMenuItemSelected(action)
UserListSharedAction.OpenPhoneBook -> openPhoneBook()
UserListSharedAction.AddByQrCode -> openAddByQrCode()
}.exhaustive
}
}
.launchIn(lifecycleScope)
if (isFirstCreation()) {
@ -111,7 +111,7 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
Toast.makeText(this, R.string.cannot_dm_self, Toast.LENGTH_SHORT).show()
finish()
}
}.exhaustive
}
}
qrViewModel.observeViewEvents {
@ -124,7 +124,7 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
finish()
}
else -> Unit
}.exhaustive
}
}
}
@ -167,6 +167,7 @@ class CreateDirectRoomActivity : SimpleFragmentActivity() {
private fun renderCreateAndInviteState(state: Async<String>) {
when (state) {
Uninitialized,
is Loading -> renderCreationLoading()
is Success -> renderCreationSuccess(state())
is Fail -> renderCreationFailure(state.error)

View file

@ -24,7 +24,6 @@ import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.mvrx.runCatchingToAsync
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.raw.wellknown.getElementWellknown
@ -56,7 +55,7 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
when (action) {
is CreateDirectRoomAction.CreateRoomAndInviteSelectedUsers -> onSubmitInvitees(action.selections)
is CreateDirectRoomAction.QrScannedAction -> onCodeParsed(action)
}.exhaustive
}
}
private fun onCodeParsed(action: CreateDirectRoomAction.QrScannedAction) {
@ -108,7 +107,7 @@ class CreateDirectRoomViewModel @AssistedInject constructor(@Assisted
when (it) {
is PendingSelection.UserPendingSelection -> invitedUserIds.add(it.user.userId)
is PendingSelection.ThreePidPendingSelection -> invite3pids.add(it.threePid)
}.exhaustive
}
}
setDirectMessage()
enableEncryptionIfInvitedUsersSupportIt = adminE2EByDefault

View file

@ -140,6 +140,7 @@ class KeysBackupSettingsRecyclerViewController @Inject constructor(
isBackupAlreadySetup = true
}
null -> Unit
}
if (isBackupAlreadySetup) {

View file

@ -116,12 +116,13 @@ class SharedSecureStorageActivity :
is SharedSecureStorageViewEvent.FinishSuccess -> {
val dataResult = Intent()
dataResult.putExtra(EXTRA_DATA_RESULT, it.cypherResult)
setResult(Activity.RESULT_OK, dataResult)
setResult(RESULT_OK, dataResult)
finish()
}
is SharedSecureStorageViewEvent.ShowResetBottomSheet -> {
navigator.open4SSetup(this, SetupMode.HARD_RESET)
}
else -> Unit
}
}

View file

@ -29,7 +29,6 @@ import dagger.assisted.AssistedInject
import im.vector.app.R
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.platform.WaitingViewData
import im.vector.app.core.resources.StringProvider
@ -142,7 +141,7 @@ class SharedSecureStorageViewModel @AssistedInject constructor(
SharedSecureStorageAction.Back -> handleBack()
SharedSecureStorageAction.ForgotResetAll -> handleResetAll()
SharedSecureStorageAction.DoResetAll -> handleDoResetAll()
}.exhaustive
}
}
private fun handleDoResetAll() {

View file

@ -77,6 +77,7 @@ class SharedSecuredStorageKeyFragment @Inject constructor() : VectorBaseFragment
is SharedSecureStorageViewEvent.KeyInlineError -> {
views.ssssKeyEnterTil.error = it.message
}
else -> Unit
}
}

View file

@ -86,6 +86,7 @@ class SharedSecuredStoragePassphraseFragment @Inject constructor(
is SharedSecureStorageViewEvent.InlineError -> {
views.ssssPassphraseEnterTil.error = it.message
}
else -> Unit
}
}

View file

@ -36,7 +36,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.extensions.commitTransaction
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.extensions.toMvRxBundle
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
@ -209,7 +208,7 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetBoot
views.bootstrapTitleText.text = getString(R.string.upgrade_security)
showFragment(BootstrapMigrateBackupFragment::class)
}
}.exhaustive
}
super.invalidate()
}

View file

@ -29,7 +29,6 @@ import im.vector.app.R
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.platform.WaitingViewData
import im.vector.app.core.resources.StringProvider
@ -259,7 +258,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
copy(step = BootstrapStep.AccountReAuth(stringProvider.getString(R.string.authentication_error)))
}
}
}.exhaustive
}
}
private fun handleStart(action: BootstrapActions.Start) = withState {
@ -344,7 +343,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
)
}
}
}.exhaustive
}
}
}
}
@ -449,7 +448,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
}
}
}
}.exhaustive
}
}
}
}
@ -533,7 +532,7 @@ class BootstrapSharedViewModel @AssistedInject constructor(
)
}
}
}.exhaustive
}
}
private fun BackupToQuadSMigrationTask.Result.Failure.toHumanReadable(): String {

View file

@ -30,7 +30,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.extensions.commitTransaction
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.extensions.toMvRxBundle
import im.vector.app.core.platform.VectorBaseActivity
@ -118,7 +117,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetV
activity.navigator.openSettings(activity, VectorSettingsActivity.EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY)
}
}
}.exhaustive
}
}
}
@ -252,6 +251,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetV
VerificationConclusionFragment.Args(false, state.sasTransactionState.cancelCode.value, state.isMe)
)
}
else -> Unit
}
return@withState

View file

@ -28,7 +28,6 @@ import dagger.assisted.AssistedInject
import im.vector.app.R
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import kotlinx.coroutines.Dispatchers
@ -365,7 +364,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
copy(verifyingFrom4S = false)
}
}
}.exhaustive
}
}
private fun handleSecretBackFromSSSS(action: VerificationAction.GotResultFromSsss) {

View file

@ -139,6 +139,7 @@ class VerificationEmojiCodeViewModel @AssistedInject constructor(
)
}
}
else -> Unit
}
}

View file

@ -18,6 +18,7 @@ package im.vector.app.features.crypto.verification.request
import androidx.core.text.toSpannable
import com.airbnb.epoxy.EpoxyController
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
@ -153,6 +154,7 @@ class VerificationRequestController @Inject constructor(
}
}
}
is Fail -> Unit
}
}

View file

@ -34,7 +34,6 @@ import com.airbnb.mvrx.withState
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.extensions.replaceFragment
import im.vector.app.core.platform.SimpleFragmentActivity
import im.vector.app.core.resources.ColorProvider
@ -79,7 +78,7 @@ class RoomDevToolActivity : SimpleFragmentActivity(), FragmentManager.OnBackStac
Unit
}
is DevToolsViewEvents.ShowSnackMessage -> showSnackbar(it.message)
}.exhaustive
}
}
supportFragmentManager.addOnBackStackChangedListener(this)
}

View file

@ -50,6 +50,7 @@ class DiscoverySettingsController @Inject constructor(
override fun buildModels(data: DiscoverySettingsState) {
when (data.identityServer) {
Uninitialized,
is Loading -> {
loadingItem {
id("identityServerLoading")
@ -209,7 +210,8 @@ class DiscoverySettingsController @Inject constructor(
titleResId(R.string.settings_discovery_emails_title)
}
when (emails) {
is Incomplete -> {
Uninitialized,
is Loading -> {
loadingItem {
id("emailsLoading")
}
@ -277,7 +279,8 @@ class DiscoverySettingsController @Inject constructor(
}
when (msisdns) {
is Incomplete -> {
Uninitialized,
is Loading -> {
loadingItem {
id("msisdnLoading")
}
@ -353,6 +356,7 @@ class DiscoverySettingsController @Inject constructor(
colorProvider(host.colorProvider)
stringProvider(host.stringProvider)
when (pidInfo.isShared) {
Uninitialized,
is Loading -> {
buttonIndeterminate(true)
}
@ -384,6 +388,7 @@ class DiscoverySettingsController @Inject constructor(
else -> iconMode(IconMode.NONE)
}
}
null -> Unit
}
}
}

View file

@ -28,7 +28,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
import im.vector.app.R
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.extensions.observeEvent
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseFragment
@ -70,7 +69,7 @@ class DiscoverySettingsFragment @Inject constructor(
when (it) {
is DiscoverySharedViewModelAction.ChangeIdentityServer ->
viewModel.handle(DiscoverySettingsAction.ChangeIdentityServer(it.newUrl))
}.exhaustive
}
}
viewModel.observeViewEvents {
@ -78,7 +77,7 @@ class DiscoverySettingsFragment @Inject constructor(
is DiscoverySettingsViewEvents.Failure -> {
displayErrorDialog(it.throwable)
}
}.exhaustive
}
}
if (discoveryArgs.expandIdentityPolicies) {
viewModel.handle(DiscoverySettingsAction.SetPoliciesExpandState(expanded = true))

View file

@ -27,7 +27,6 @@ import dagger.assisted.AssistedInject
import im.vector.app.R
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import kotlinx.coroutines.flow.launchIn
@ -113,7 +112,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
is DiscoverySettingsAction.FinalizeBind3pid -> finalizeBind3pid(action, true)
is DiscoverySettingsAction.SubmitMsisdnToken -> submitMsisdnToken(action)
is DiscoverySettingsAction.CancelBinding -> cancelBinding(action)
}.exhaustive
}
}
private fun handleUpdateUserConsent(action: DiscoverySettingsAction.UpdateUserConsent) {
@ -235,7 +234,7 @@ class DiscoverySettingsViewModel @AssistedInject constructor(
when (action.threePid) {
is ThreePid.Email -> revokeEmail(action.threePid)
is ThreePid.Msisdn -> revokeMsisdn(action.threePid)
}.exhaustive
}
}
private fun revokeEmail(threePid: ThreePid.Email) = withState { state ->

View file

@ -34,7 +34,6 @@ import im.vector.app.core.epoxy.attributes.ButtonStyle
import im.vector.app.core.epoxy.attributes.ButtonType
import im.vector.app.core.epoxy.attributes.IconMode
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider
@ -122,7 +121,7 @@ abstract class SettingsTextButtonSingleLineItem : EpoxyModelWithHolder<SettingsT
ButtonStyle.DESTRUCTIVE -> {
holder.mainButton.setTextColor(colorProvider.getColorFromAttribute(R.attr.colorError))
}
}.exhaustive
}
holder.mainButton.onClick(buttonClickListener)
}
ButtonType.SWITCH -> {
@ -133,7 +132,7 @@ abstract class SettingsTextButtonSingleLineItem : EpoxyModelWithHolder<SettingsT
checked?.let { holder.switchButton.isChecked = it }
holder.switchButton.setOnCheckedChangeListener(switchChangeListener)
}
}.exhaustive
}
}
when (iconMode) {

View file

@ -29,7 +29,6 @@ import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import im.vector.app.R
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.extensions.toReducedUrl
import im.vector.app.core.platform.VectorBaseFragment
@ -132,7 +131,7 @@ class SetIdentityServerFragment @Inject constructor(
it.identityServerUrl,
null)
}
}.exhaustive
}
}
}

View file

@ -25,7 +25,6 @@ import im.vector.app.R
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.SingletonEntryPoint
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.ensureProtocol
@ -67,7 +66,7 @@ class SetIdentityServerViewModel @AssistedInject constructor(
when (action) {
SetIdentityServerAction.UseDefaultIdentityServer -> useDefault()
is SetIdentityServerAction.UseCustomIdentityServer -> usedCustomIdentityServerUrl(action)
}.exhaustive
}
}
private fun useDefault() = withState { state ->

View file

@ -38,7 +38,6 @@ import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.AppStateHandler
import im.vector.app.R
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.extensions.replaceFragment
@ -90,6 +89,7 @@ import javax.inject.Inject
data class HomeActivityArgs(
val clearNotification: Boolean,
val accountCreation: Boolean,
val hasExistingSession: Boolean = false,
val inviteNotificationRoomId: String? = null
) : Parcelable
@ -106,6 +106,7 @@ class HomeActivity :
@Suppress("UNUSED")
private val analyticsAccountDataViewModel: AnalyticsAccountDataViewModel by viewModel()
@Suppress("UNUSED")
private val userColorAccountDataViewModel: UserColorAccountDataViewModel by viewModel()
@ -231,7 +232,7 @@ class HomeActivity :
HomeActivitySharedAction.SendSpaceFeedBack -> {
bugReporter.openBugReportScreen(this, ReportType.SPACE_BETA_FEEDBACK)
}
}.exhaustive
}
}
.launchIn(lifecycleScope)
@ -253,7 +254,9 @@ class HomeActivity :
HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush()
is HomeActivityViewEvents.OnCrossSignedInvalidated -> handleCrossSigningInvalidated(it)
HomeActivityViewEvents.ShowAnalyticsOptIn -> handleShowAnalyticsOptIn()
}.exhaustive
HomeActivityViewEvents.NotifyUserForThreadsMigration -> handleNotifyUserForThreadsMigration()
is HomeActivityViewEvents.MigrateThreads -> migrateThreadsIfNeeded(it.checkSession)
}
}
homeActivityViewModel.onEach { renderState(it) }
@ -269,6 +272,48 @@ class HomeActivity :
navigator.openAnalyticsOptIn(this)
}
/**
* Migrating from old threads io.element.thread to new m.thread needs an initial sync to
* sync and display existing messages appropriately
*/
private fun migrateThreadsIfNeeded(checkSession: Boolean) {
if (checkSession) {
// We should check session to ensure we will only clear cache if needed
val args = intent.getParcelableExtra<HomeActivityArgs>(Mavericks.KEY_ARG)
if (args?.hasExistingSession == true) {
// existingSession --> Will be true only if we came from an existing active session
Timber.i("----> Migrating threads from an existing session..")
handleThreadsMigration()
} else {
// We came from a new session and not an existing one,
// so there is no need to migrate threads while an initial synced performed
Timber.i("----> No thread migration needed, we are ok")
vectorPreferences.setShouldMigrateThreads(shouldMigrate = false)
}
} else {
// Proceed with migration
handleThreadsMigration()
}
}
/**
* Clear cache and restart to invoke an initial sync for threads migration
*/
private fun handleThreadsMigration() {
Timber.i("----> Threads Migration detected, clearing cache and sync...")
vectorPreferences.setShouldMigrateThreads(shouldMigrate = false)
MainActivity.restartApp(this, MainActivityArgs(clearCache = true))
}
private fun handleNotifyUserForThreadsMigration() {
MaterialAlertDialogBuilder(this)
.setTitle(R.string.threads_notice_migration_title)
.setMessage(R.string.threads_notice_migration_message)
.setCancelable(true)
.setPositiveButton(R.string.sas_got_it) { _, _ -> }
.show()
}
private fun handleIntent(intent: Intent?) {
intent?.dataString?.let { deepLink ->
val resolvedLink = when {
@ -329,7 +374,7 @@ class HomeActivity :
// Idle or Incremental sync status
views.waitingView.root.isVisible = false
}
}.exhaustive
}
}
private fun handleAskPasswordToInitCrossSigning(events: HomeActivityViewEvents.AskPasswordToInitCrossSigning) {
@ -546,11 +591,13 @@ class HomeActivity :
fun newIntent(context: Context,
clearNotification: Boolean = false,
accountCreation: Boolean = false,
existingSession: Boolean = false,
inviteNotificationRoomId: String? = null
): Intent {
val args = HomeActivityArgs(
clearNotification = clearNotification,
accountCreation = accountCreation,
hasExistingSession = existingSession,
inviteNotificationRoomId = inviteNotificationRoomId
)

View file

@ -25,4 +25,6 @@ sealed interface HomeActivityViewEvents : VectorViewEvents {
data class OnCrossSignedInvalidated(val userItem: MatrixItem.UserItem) : HomeActivityViewEvents
object PromptToEnableSessionPush : HomeActivityViewEvents
object ShowAnalyticsOptIn : HomeActivityViewEvents
object NotifyUserForThreadsMigration : HomeActivityViewEvents
data class MigrateThreads(val checkSession: Boolean) : HomeActivityViewEvents
}

View file

@ -25,7 +25,6 @@ import im.vector.app.config.analyticsConfig
import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.analytics.store.AnalyticsStore
import im.vector.app.features.login.ReAuthHelper
@ -51,6 +50,7 @@ import org.matrix.android.sdk.api.util.toMatrixItem
import org.matrix.android.sdk.flow.flow
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.database.lightweight.LightweightSettingsStorage
import org.matrix.android.sdk.internal.util.awaitCallback
import timber.log.Timber
import kotlin.coroutines.Continuation
@ -62,6 +62,7 @@ class HomeActivityViewModel @AssistedInject constructor(
private val activeSessionHolder: ActiveSessionHolder,
private val reAuthHelper: ReAuthHelper,
private val analyticsStore: AnalyticsStore,
private val lightweightSettingsStorage: LightweightSettingsStorage,
private val vectorPreferences: VectorPreferences
) : VectorViewModel<HomeActivityViewState, HomeActivityViewActions, HomeActivityViewEvents>(initialState) {
@ -84,6 +85,7 @@ class HomeActivityViewModel @AssistedInject constructor(
checkSessionPushIsOn()
observeCrossSigningReset()
observeAnalytics()
initThreadsMigration()
}
private fun observeAnalytics() {
@ -130,6 +132,46 @@ class HomeActivityViewModel @AssistedInject constructor(
.launchIn(viewModelScope)
}
/**
* Handle threads migration. The migration includes:
* - Notify users that had io.element.thread enabled from labs
* - Re-Enable m.thread to those users (that they had enabled labs threads)
* - Handle migration when threads are enabled by default
*/
private fun initThreadsMigration() {
// When we would like to enable threads for all users
// if(vectorPreferences.shouldMigrateThreads()) {
// vectorPreferences.setThreadMessagesEnabled()
// lightweightSettingsStorage.setThreadMessagesEnabled(vectorPreferences.areThreadMessagesEnabled())
// }
when {
// Notify users
vectorPreferences.shouldNotifyUserAboutThreads() && vectorPreferences.areThreadMessagesEnabled() -> {
Timber.i("----> Notify users about threads")
// Notify the user if needed that we migrated to support m.thread
// instead of io.element.thread so old thread messages will be displayed as normal timeline messages
_viewEvents.post(HomeActivityViewEvents.NotifyUserForThreadsMigration)
vectorPreferences.userNotifiedAboutThreads()
}
// Migrate users with enabled lab settings
vectorPreferences.shouldNotifyUserAboutThreads() && vectorPreferences.shouldMigrateThreads() -> {
Timber.i("----> Migrate threads with enabled labs")
// If user had io.element.thread enabled then enable the new thread support,
// clear cache to sync messages appropriately
vectorPreferences.setThreadMessagesEnabled()
lightweightSettingsStorage.setThreadMessagesEnabled(vectorPreferences.areThreadMessagesEnabled())
// Clear Cache
_viewEvents.post(HomeActivityViewEvents.MigrateThreads(checkSession = false))
}
// Enable all users
vectorPreferences.shouldMigrateThreads() && vectorPreferences.areThreadMessagesEnabled() -> {
Timber.i("----> Try to migrate threads")
_viewEvents.post(HomeActivityViewEvents.MigrateThreads(checkSession = true))
}
}
}
private fun observeInitialSync() {
val session = activeSessionHolder.getSafeActiveSession() ?: return
@ -263,6 +305,6 @@ class HomeActivityViewModel @AssistedInject constructor(
HomeActivityViewActions.ViewStarted -> {
initialize()
}
}.exhaustive
}
}
}

View file

@ -273,6 +273,7 @@ class HomeDetailViewModel @AssistedInject constructor(
)
}
}
null -> Unit
}
}
.launchIn(viewModelScope)

View file

@ -72,7 +72,6 @@ import im.vector.app.core.dialogs.ConfirmationDialogBuilder
import im.vector.app.core.dialogs.GalleryOrCameraDialogHelper
import im.vector.app.core.epoxy.LayoutManagerStateRestorer
import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.extensions.setTextOrHide
@ -177,6 +176,7 @@ import im.vector.app.features.html.PillsPostProcessor
import im.vector.app.features.invite.VectorInviteView
import im.vector.app.features.location.LocationSharingMode
import im.vector.app.features.location.toLocationData
import im.vector.app.features.media.AttachmentData
import im.vector.app.features.media.ImageContentRenderer
import im.vector.app.features.media.VideoContentRenderer
import im.vector.app.features.notifications.NotificationDrawerManager
@ -445,7 +445,7 @@ class TimelineFragment @Inject constructor(
}
showErrorInSnackbar(it.throwable)
}
}.exhaustive
}
}
timelineViewModel.observeViewEvents {
@ -482,7 +482,7 @@ class TimelineFragment @Inject constructor(
RoomDetailViewEvents.StopChatEffects -> handleStopChatEffects()
is RoomDetailViewEvents.DisplayAndAcceptCall -> acceptIncomingCall(it)
RoomDetailViewEvents.RoomReplacementStarted -> handleRoomReplacement()
}.exhaustive
}
}
if (savedInstanceState == null) {
@ -783,6 +783,18 @@ class TimelineFragment @Inject constructor(
updateRecordingUiState(RecordingUiState.Draft)
}
override fun onVoiceWaveformTouchedUp(percentage: Float, duration: Int) {
messageComposerViewModel.handle(
MessageComposerAction.VoiceWaveformTouchedUp(VoiceMessagePlaybackTracker.RECORDING_ID, duration, percentage)
)
}
override fun onVoiceWaveformMoved(percentage: Float, duration: Int) {
messageComposerViewModel.handle(
MessageComposerAction.VoiceWaveformTouchedUp(VoiceMessagePlaybackTracker.RECORDING_ID, duration, percentage)
)
}
private fun updateRecordingUiState(state: RecordingUiState) {
messageComposerViewModel.handle(
MessageComposerAction.OnVoiceRecordingUiStateChanged(state))
@ -874,7 +886,7 @@ class TimelineFragment @Inject constructor(
onContentAttachmentsReady(sharedData.attachmentData)
}
null -> Timber.v("No share data to process")
}.exhaustive
}
}
private fun handleSpaceShare() {
@ -1240,7 +1252,7 @@ class TimelineFragment @Inject constructor(
insertUserDisplayNameInTextEditor(roomDetailPendingAction.userId)
is RoomDetailPendingAction.OpenRoom ->
handleOpenRoom(RoomDetailViewEvents.OpenRoom(roomDetailPendingAction.roomId, roomDetailPendingAction.closeCurrentRoom))
}.exhaustive
}
}
override fun onPause() {
@ -1612,11 +1624,10 @@ class TimelineFragment @Inject constructor(
views.includeRoomToolbar.roomToolbarContentView.isClickable = roomSummary.membership == Membership.JOIN
views.includeRoomToolbar.roomToolbarTitleView.text = roomSummary.displayName
avatarRenderer.render(roomSummary.toMatrixItem(), views.includeRoomToolbar.roomToolbarAvatarImageView)
views.includeRoomToolbar.roomToolbarDecorationImageView.render(roomSummary.roomEncryptionTrustLevel)
views.includeRoomToolbar.roomToolbarPresenceImageView.render(
roomSummary.isDirect && matrixConfiguration.presenceSyncEnabled,
roomSummary.directUserPresence
)
val showPresence = roomSummary.isDirect && matrixConfiguration.presenceSyncEnabled
views.includeRoomToolbar.roomToolbarPresenceImageView.render(showPresence, roomSummary.directUserPresence)
val shieldView = if (showPresence) views.includeRoomToolbar.roomToolbarTitleShield else views.includeRoomToolbar.roomToolbarAvatarShield
shieldView.render(roomSummary.roomEncryptionTrustLevel)
views.includeRoomToolbar.roomToolbarPublicImageView.isVisible = roomSummary.isPublic && !roomSummary.isDirect
}
} else {
@ -1658,7 +1669,7 @@ class TimelineFragment @Inject constructor(
is MessageComposerViewEvents.SlashCommandNotSupportedInThreads -> {
displayCommandError(getString(R.string.command_not_supported_in_threads, sendMessageResult.command.command))
}
} // .exhaustive
} //
lockSendButton = false
}
@ -1782,6 +1793,7 @@ class TimelineFragment @Inject constructor(
transactionId = data.transactionId,
).show(parentFragmentManager, "REQ")
}
else -> Unit
}
}
@ -1871,12 +1883,16 @@ class TimelineFragment @Inject constructor(
vectorBaseActivity.notImplemented("encrypted message click")
}
override fun onImageMessageClicked(messageImageContent: MessageImageInfoContent, mediaData: ImageContentRenderer.Data, view: View) {
override fun onImageMessageClicked(messageImageContent: MessageImageInfoContent,
mediaData: ImageContentRenderer.Data,
view: View,
inMemory: List<AttachmentData>) {
navigator.openMediaViewer(
activity = requireActivity(),
roomId = timelineArgs.roomId,
mediaData = mediaData,
view = view
view = view,
inMemory = inMemory
) { pairs ->
pairs.add(Pair(views.roomToolbar, ViewCompat.getTransitionName(views.roomToolbar) ?: ""))
pairs.add(Pair(views.composerLayout, ViewCompat.getTransitionName(views.composerLayout) ?: ""))
@ -2047,6 +2063,14 @@ class TimelineFragment @Inject constructor(
messageComposerViewModel.handle(MessageComposerAction.PlayOrPauseVoicePlayback(eventId, messageAudioContent))
}
override fun onVoiceWaveformTouchedUp(eventId: String, duration: Int, percentage: Float) {
messageComposerViewModel.handle(MessageComposerAction.VoiceWaveformTouchedUp(eventId, duration, percentage))
}
override fun onVoiceWaveformMovedTo(eventId: String, duration: Int, percentage: Float) {
messageComposerViewModel.handle(MessageComposerAction.VoiceWaveformMovedTo(eventId, duration, percentage))
}
private fun onShareActionClicked(action: EventSharedAction.Share) {
when (action.messageContent) {
is MessageTextContent -> shareText(requireContext(), action.messageContent.body)
@ -2232,6 +2256,8 @@ class TimelineFragment @Inject constructor(
is EventSharedAction.EndPoll -> {
askConfirmationToEndPoll(action.eventId)
}
is EventSharedAction.ReportContent -> Unit /* Not clickable */
EventSharedAction.Separator -> Unit /* Not clickable */
}
}
@ -2430,7 +2456,7 @@ class TimelineFragment @Inject constructor(
locationOwnerId = session.myUserId
)
}
}.exhaustive
}
}
// AttachmentsHelper.Callback

View file

@ -33,7 +33,6 @@ import im.vector.app.BuildConfig
import im.vector.app.R
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.mvrx.runCatchingToAsync
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
@ -440,7 +439,7 @@ class TimelineViewModel @AssistedInject constructor(
_viewEvents.post(RoomDetailViewEvents.OpenRoom(action.replacementRoomId, closeCurrentRoom = true))
}
is RoomDetailAction.EndPoll -> handleEndPoll(action.eventId)
}.exhaustive
}
}
private fun handleJitsiCallJoinStatus(action: RoomDetailAction.UpdateJoinJitsiCallStatus) = withState { state ->

View file

@ -40,4 +40,6 @@ sealed class MessageComposerAction : VectorViewModelAction {
data class PlayOrPauseVoicePlayback(val eventId: String, val messageAudioContent: MessageAudioContent) : MessageComposerAction()
object PlayOrPauseRecordingPlayback : MessageComposerAction()
data class EndAllVoiceActions(val deleteRecord: Boolean = true) : MessageComposerAction()
data class VoiceWaveformTouchedUp(val eventId: String, val duration: Int, val percentage: Float) : MessageComposerAction()
data class VoiceWaveformMovedTo(val eventId: String, val duration: Int, val percentage: Float) : MessageComposerAction()
}

View file

@ -23,7 +23,6 @@ import dagger.assisted.AssistedInject
import im.vector.app.R
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.core.resources.StringProvider
import im.vector.app.features.analytics.AnalyticsTracker
@ -109,6 +108,8 @@ class MessageComposerViewModel @AssistedInject constructor(
is MessageComposerAction.EndAllVoiceActions -> handleEndAllVoiceActions(action.deleteRecord)
is MessageComposerAction.InitializeVoiceRecorder -> handleInitializeVoiceRecorder(action.attachmentData)
is MessageComposerAction.OnEntersBackground -> handleEntersBackground(action.composerText)
is MessageComposerAction.VoiceWaveformTouchedUp -> handleVoiceWaveformTouchedUp(action)
is MessageComposerAction.VoiceWaveformMovedTo -> handleVoiceWaveformMovedTo(action)
}
}
@ -463,7 +464,7 @@ class MessageComposerViewModel @AssistedInject constructor(
_viewEvents.post(MessageComposerViewEvents.SlashCommandResultOk())
popDraft()
}
}.exhaustive
}
}
is SendMode.Edit -> {
// is original event a reply?
@ -536,7 +537,7 @@ class MessageComposerViewModel @AssistedInject constructor(
is SendMode.Voice -> {
// do nothing
}
}.exhaustive
}
}
}
@ -869,12 +870,23 @@ class MessageComposerViewModel @AssistedInject constructor(
voiceMessageHelper.pauseRecording()
}
private fun handleVoiceWaveformTouchedUp(action: MessageComposerAction.VoiceWaveformTouchedUp) {
voiceMessageHelper.movePlaybackTo(action.eventId, action.percentage, action.duration)
}
private fun handleVoiceWaveformMovedTo(action: MessageComposerAction.VoiceWaveformMovedTo) {
voiceMessageHelper.movePlaybackTo(action.eventId, action.percentage, action.duration)
}
private fun handleEntersBackground(composerText: String) {
// Always stop all voice actions. It may be playing in timeline or active recording
val playingAudioContent = voiceMessageHelper.stopAllVoiceActions(deleteRecord = false)
voiceMessageHelper.clearTracker()
val isVoiceRecording = com.airbnb.mvrx.withState(this) { it.isVoiceRecording }
if (isVoiceRecording) {
voiceMessageHelper.clearTracker()
viewModelScope.launch {
voiceMessageHelper.stopAllVoiceActions(deleteRecord = false)?.toContentAttachmentData()?.let { voiceDraft ->
playingAudioContent?.toContentAttachmentData()?.let { voiceDraft ->
val content = voiceDraft.toJsonString()
room.saveDraft(UserDraft.Voice(content))
setState { copy(sendMode = SendMode.Voice(content)) }

Some files were not shown because too many files have changed in this diff Show more