Merge branch 'develop' into feature/ons/change_pin

* develop:
  Format source
  Fix / double bottomsheet effect
  Version**
  Ensure the Activity is destroyed, it seems that the intent flags are not enough now. - finish all
  Ensure the Activity is destroyed, it seems that the intent flags are not enough now.
  Remove redundant returns
  Prepare version 1.0.11
  Fix issue when there is no display name
  Cleanup
  Fix issue with too big icons
  Use style instead of duplicating the whole layout file
  Try other asset strategy as lint not happy
  Convert TermsService to suspend functions
  Convert UploadsService to suspend functions
  Convert IntegrationManagerService to suspend functions
  update change log
  Home empty screen design update
This commit is contained in:
Onuray Sahin 2020-11-30 13:14:13 +03:00
commit ffe9a03d3e
34 changed files with 304 additions and 216 deletions

View file

@ -1,4 +1,31 @@
Changes in Element 1.0.11 (2020-XX-XX)
Changes in Element 1.0.12 (2020-XX-XX)
===================================================
Features ✨:
-
Improvements 🙌:
- Add Setting Item to Change PIN (#2462)
Bugfix 🐛:
- Double bottomsheet effect after verify with passphrase
Translations 🗣:
-
SDK API changes ⚠️:
-
Build 🧱:
-
Test:
-
Other changes:
-
Changes in Element 1.0.11 (2020-11-27)
===================================================
Features ✨:
@ -14,8 +41,8 @@ Improvements 🙌:
- Handle events of type "m.room.server_acl" (#890)
- Room creation form: add advanced section to disable federation (#1314)
- Move "Enable Encryption" from room setting screen to room profile screen (#2394)
- Home empty screens quick design update (#2347)
- Improve Invite user screen (seamless search for matrix ID)
- Add Setting Item to Change PIN (#2462)
Bugfix 🐛:
- Fix crash on AttachmentViewer (#2365)
@ -30,14 +57,9 @@ Bugfix 🐛:
- Update profile has no effect if user is in zero rooms
- Fix issues with matrix.to deep linking (#2349)
Translations 🗣:
-
SDK API changes ⚠️:
- AccountService now exposes suspendable function instead of using MatrixCallback (#2354). Note: We will incrementally migrate all the SDK API in a near future.
Build 🧱:
-
- AccountService now exposes suspendable function instead of using MatrixCallback (#2354).
Note: We will incrementally migrate all the SDK API in a near future (#2449)
Test:
- Add `allScreensTest` to cover all screens of the app

View file

@ -0,0 +1,2 @@
This new version mainly contains user interface and user experience improvements. Now you can invite friends, and create DM very fast by scanning QR codes.
Full changelog: https://github.com/vector-im/element-android/releases/tag/v1.0.11

View file

@ -16,9 +16,6 @@
package org.matrix.android.sdk.api.session.integrationmanager
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.util.Cancelable
/**
* This is the entry point to manage integration. You can grab an instance of this service through an active session.
*/
@ -80,19 +77,17 @@ interface IntegrationManagerService {
/**
* Offers to enable or disable the integration.
* @param enable the param to change
* @param callback the matrix callback to listen for result.
* @return Cancelable
*/
fun setIntegrationEnabled(enable: Boolean, callback: MatrixCallback<Unit>): Cancelable
suspend fun setIntegrationEnabled(enable: Boolean)
/**
* Offers to allow or disallow a widget.
* @param stateEventId the eventId of the state event defining the widget.
* @param allowed the param to change
* @param callback the matrix callback to listen for result.
* @return Cancelable
*/
fun setWidgetAllowed(stateEventId: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable
suspend fun setWidgetAllowed(stateEventId: String, allowed: Boolean)
/**
* Returns true if the widget is allowed, false otherwise.
@ -105,7 +100,7 @@ interface IntegrationManagerService {
* @param widgetType the widget type to check for
* @param domain the domain to check for
*/
fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable
suspend fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean)
/**
* Returns true if the widget domain is allowed, false otherwise.

View file

@ -16,9 +16,6 @@
package org.matrix.android.sdk.api.session.room.uploads
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.util.Cancelable
/**
* This interface defines methods to get event with uploads (= attachments) sent to a room. It's implemented at the room level.
*/
@ -29,7 +26,5 @@ interface UploadsService {
* @param numberOfEvents the expected number of events to retrieve. The result can contain less events.
* @param since token to get next page, or null to get the first page
*/
fun getUploads(numberOfEvents: Int,
since: String?,
callback: MatrixCallback<GetUploadsResult>): Cancelable
suspend fun getUploads(numberOfEvents: Int, since: String?): GetUploadsResult
}

View file

@ -16,22 +16,16 @@
package org.matrix.android.sdk.api.session.terms
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.util.Cancelable
interface TermsService {
enum class ServiceType {
IntegrationManager,
IdentityService
}
fun getTerms(serviceType: ServiceType,
baseUrl: String,
callback: MatrixCallback<GetTermsResponse>): Cancelable
suspend fun getTerms(serviceType: ServiceType, baseUrl: String): GetTermsResponse
fun agreeToTerms(serviceType: ServiceType,
baseUrl: String,
agreedUrls: List<String>,
token: String?,
callback: MatrixCallback<Unit>): Cancelable
suspend fun agreeToTerms(serviceType: ServiceType,
baseUrl: String,
agreedUrls: List<String>,
token: String?)
}

View file

@ -16,10 +16,8 @@
package org.matrix.android.sdk.internal.session.integrationmanager
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
import org.matrix.android.sdk.api.util.Cancelable
import javax.inject.Inject
internal class DefaultIntegrationManagerService @Inject constructor(private val integrationManager: IntegrationManager) : IntegrationManagerService {
@ -44,20 +42,20 @@ internal class DefaultIntegrationManagerService @Inject constructor(private val
return integrationManager.isIntegrationEnabled()
}
override fun setIntegrationEnabled(enable: Boolean, callback: MatrixCallback<Unit>): Cancelable {
return integrationManager.setIntegrationEnabled(enable, callback)
override suspend fun setIntegrationEnabled(enable: Boolean) {
integrationManager.setIntegrationEnabled(enable)
}
override fun setWidgetAllowed(stateEventId: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable {
return integrationManager.setWidgetAllowed(stateEventId, allowed, callback)
override suspend fun setWidgetAllowed(stateEventId: String, allowed: Boolean) {
integrationManager.setWidgetAllowed(stateEventId, allowed)
}
override fun isWidgetAllowed(stateEventId: String): Boolean {
return integrationManager.isWidgetAllowed(stateEventId)
}
override fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable {
return integrationManager.setNativeWidgetDomainAllowed(widgetType, domain, allowed, callback)
override suspend fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean) {
integrationManager.setNativeWidgetDomainAllowed(widgetType, domain, allowed)
}
override fun isNativeWidgetDomainAllowed(widgetType: String, domain: String): Boolean {

View file

@ -20,15 +20,12 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import com.zhuinden.monarchy.Monarchy
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
import org.matrix.android.sdk.api.session.widgets.model.WidgetContent
import org.matrix.android.sdk.api.session.widgets.model.WidgetType
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.api.util.NoOpCancellable
import org.matrix.android.sdk.internal.database.model.WellknownIntegrationManagerConfigEntity
import org.matrix.android.sdk.internal.di.SessionDatabase
import org.matrix.android.sdk.internal.extensions.observeNotNull
@ -41,7 +38,6 @@ import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccoun
import org.matrix.android.sdk.internal.session.widgets.helper.WidgetFactory
import org.matrix.android.sdk.internal.session.widgets.helper.extractWidgetSequence
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.task.configureWith
import timber.log.Timber
import javax.inject.Inject
@ -137,22 +133,17 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri
return integrationProvisioningContent?.enabled ?: false
}
fun setIntegrationEnabled(enable: Boolean, callback: MatrixCallback<Unit>): Cancelable {
suspend fun setIntegrationEnabled(enable: Boolean) {
val isIntegrationEnabled = isIntegrationEnabled()
if (enable == isIntegrationEnabled) {
callback.onSuccess(Unit)
return NoOpCancellable
return
}
val integrationProvisioningContent = IntegrationProvisioningContent(enabled = enable)
val params = UpdateUserAccountDataTask.IntegrationProvisioning(integrationProvisioningContent = integrationProvisioningContent)
return updateUserAccountDataTask
.configureWith(params) {
this.callback = callback
}
.executeBy(taskExecutor)
return updateUserAccountDataTask.execute(params)
}
fun setWidgetAllowed(stateEventId: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable {
suspend fun setWidgetAllowed(stateEventId: String, allowed: Boolean) {
val currentAllowedWidgets = accountDataDataSource.getAccountDataEvent(UserAccountDataTypes.TYPE_ALLOWED_WIDGETS)
val currentContent = currentAllowedWidgets?.content?.toModel<AllowedWidgetsContent>()
val newContent = if (currentContent == null) {
@ -165,11 +156,7 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri
currentContent.copy(widgets = allowedWidgets)
}
val params = UpdateUserAccountDataTask.AllowedWidgets(allowedWidgetsContent = newContent)
return updateUserAccountDataTask
.configureWith(params) {
this.callback = callback
}
.executeBy(taskExecutor)
return updateUserAccountDataTask.execute(params)
}
fun isWidgetAllowed(stateEventId: String): Boolean {
@ -178,7 +165,7 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri
return currentContent?.widgets?.get(stateEventId) ?: false
}
fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean, callback: MatrixCallback<Unit>): Cancelable {
suspend fun setNativeWidgetDomainAllowed(widgetType: String, domain: String, allowed: Boolean) {
val currentAllowedWidgets = accountDataDataSource.getAccountDataEvent(UserAccountDataTypes.TYPE_ALLOWED_WIDGETS)
val currentContent = currentAllowedWidgets?.content?.toModel<AllowedWidgetsContent>()
val newContent = if (currentContent == null) {
@ -195,11 +182,7 @@ internal class IntegrationManager @Inject constructor(matrixConfiguration: Matri
currentContent.copy(native = nativeAllowedWidgets)
}
val params = UpdateUserAccountDataTask.AllowedWidgets(allowedWidgetsContent = newContent)
return updateUserAccountDataTask
.configureWith(params) {
this.callback = callback
}
.executeBy(taskExecutor)
return updateUserAccountDataTask.execute(params)
}
fun isNativeWidgetDomainAllowed(widgetType: String, domain: String?): Boolean {

View file

@ -18,17 +18,12 @@ package org.matrix.android.sdk.internal.session.room.uploads
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.room.uploads.GetUploadsResult
import org.matrix.android.sdk.api.session.room.uploads.UploadsService
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.task.configureWith
internal class DefaultUploadsService @AssistedInject constructor(
@Assisted private val roomId: String,
private val taskExecutor: TaskExecutor,
private val getUploadsTask: GetUploadsTask,
private val cryptoService: CryptoService
) : UploadsService {
@ -38,11 +33,7 @@ internal class DefaultUploadsService @AssistedInject constructor(
fun create(roomId: String): UploadsService
}
override fun getUploads(numberOfEvents: Int, since: String?, callback: MatrixCallback<GetUploadsResult>): Cancelable {
return getUploadsTask
.configureWith(GetUploadsTask.Params(roomId, cryptoService.isRoomEncrypted(roomId), numberOfEvents, since)) {
this.callback = callback
}
.executeBy(taskExecutor)
override suspend fun getUploads(numberOfEvents: Int, since: String?): GetUploadsResult {
return getUploadsTask.execute(GetUploadsTask.Params(roomId, cryptoService.isRoomEncrypted(roomId), numberOfEvents, since))
}
}

View file

@ -17,11 +17,10 @@
package org.matrix.android.sdk.internal.session.terms
import dagger.Lazy
import org.matrix.android.sdk.api.MatrixCallback
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.terms.GetTermsResponse
import org.matrix.android.sdk.api.session.terms.TermsService
import org.matrix.android.sdk.api.util.Cancelable
import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate
import org.matrix.android.sdk.internal.network.NetworkConstants
import org.matrix.android.sdk.internal.network.RetrofitFactory
@ -33,8 +32,6 @@ import org.matrix.android.sdk.internal.session.sync.model.accountdata.AcceptedTe
import org.matrix.android.sdk.api.session.accountdata.UserAccountDataTypes
import org.matrix.android.sdk.internal.session.user.accountdata.AccountDataDataSource
import org.matrix.android.sdk.internal.session.user.accountdata.UpdateUserAccountDataTask
import org.matrix.android.sdk.internal.task.TaskExecutor
import org.matrix.android.sdk.internal.task.launchToCallback
import org.matrix.android.sdk.internal.util.MatrixCoroutineDispatchers
import org.matrix.android.sdk.internal.util.ensureTrailingSlash
import okhttp3.OkHttpClient
@ -49,13 +46,11 @@ internal class DefaultTermsService @Inject constructor(
private val getOpenIdTokenTask: GetOpenIdTokenTask,
private val identityRegisterTask: IdentityRegisterTask,
private val updateUserAccountDataTask: UpdateUserAccountDataTask,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val taskExecutor: TaskExecutor
private val coroutineDispatchers: MatrixCoroutineDispatchers
) : TermsService {
override fun getTerms(serviceType: TermsService.ServiceType,
baseUrl: String,
callback: MatrixCallback<GetTermsResponse>): Cancelable {
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
override suspend fun getTerms(serviceType: TermsService.ServiceType,
baseUrl: String): GetTermsResponse {
return withContext(coroutineDispatchers.main) {
val url = buildUrl(baseUrl, serviceType)
val termsResponse = executeRequest<TermsResponse>(null) {
apiCall = termsAPI.getTerms("${url}terms")
@ -64,12 +59,11 @@ internal class DefaultTermsService @Inject constructor(
}
}
override fun agreeToTerms(serviceType: TermsService.ServiceType,
baseUrl: String,
agreedUrls: List<String>,
token: String?,
callback: MatrixCallback<Unit>): Cancelable {
return taskExecutor.executorScope.launchToCallback(coroutineDispatchers.main, callback) {
override suspend fun agreeToTerms(serviceType: TermsService.ServiceType,
baseUrl: String,
agreedUrls: List<String>,
token: String?) {
withContext(coroutineDispatchers.main) {
val url = buildUrl(baseUrl, serviceType)
val tokenToUse = token?.takeIf { it.isNotEmpty() } ?: getToken(baseUrl)

View file

@ -17,7 +17,7 @@ androidExtensions {
// Note: 2 digits max for each value
ext.versionMajor = 1
ext.versionMinor = 0
ext.versionPatch = 11
ext.versionPatch = 12
static def getGitTimestamp() {
def cmd = 'git show -s --format=%ct'

View file

@ -36,6 +36,7 @@ import im.vector.app.features.crypto.recover.BootstrapMigrateBackupFragment
import im.vector.app.features.crypto.recover.BootstrapSaveRecoveryKeyFragment
import im.vector.app.features.crypto.recover.BootstrapSetupRecoveryKeyFragment
import im.vector.app.features.crypto.recover.BootstrapWaitingFragment
import im.vector.app.features.crypto.verification.QuadSLoadingFragment
import im.vector.app.features.crypto.verification.cancel.VerificationCancelFragment
import im.vector.app.features.crypto.verification.cancel.VerificationNotMeFragment
import im.vector.app.features.crypto.verification.choose.VerificationChooseMethodFragment
@ -418,6 +419,11 @@ interface FragmentModule {
@FragmentKey(VerificationCancelFragment::class)
fun bindVerificationCancelFragment(fragment: VerificationCancelFragment): Fragment
@Binds
@IntoMap
@FragmentKey(QuadSLoadingFragment::class)
fun bindQuadSLoadingFragment(fragment: QuadSLoadingFragment): Fragment
@Binds
@IntoMap
@FragmentKey(VerificationNotMeFragment::class)

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.core.extensions
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
fun ConstraintLayout.updateConstraintSet(block: (ConstraintSet) -> Unit) {
ConstraintSet().let {
it.clone(this)
block.invoke(it)
it.applyTo(this)
}
}

View file

@ -23,6 +23,7 @@ import android.view.View
import android.widget.FrameLayout
import androidx.core.view.isVisible
import im.vector.app.R
import im.vector.app.core.extensions.updateConstraintSet
import kotlinx.android.synthetic.main.view_state.view.*
class StateView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0)
@ -31,7 +32,12 @@ class StateView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
sealed class State {
object Content : State()
object Loading : State()
data class Empty(val title: CharSequence? = null, val image: Drawable? = null, val message: CharSequence? = null) : State()
data class Empty(
val title: CharSequence? = null,
val image: Drawable? = null,
val isBigImage: Boolean = false,
val message: CharSequence? = null
) : State()
data class Error(val message: CharSequence? = null) : State()
}
@ -71,6 +77,9 @@ class StateView @JvmOverloads constructor(context: Context, attrs: AttributeSet?
is State.Loading -> Unit
is State.Empty -> {
emptyImageView.setImageDrawable(newState.image)
emptyView.updateConstraintSet {
it.constrainPercentHeight(R.id.emptyImageView, if (newState.isBigImage) 0.5f else 0.1f)
}
emptyMessageView.text = newState.message
emptyTitleView.text = newState.title
}

View file

@ -62,8 +62,8 @@ data class MainActivityArgs(
) : Parcelable
/**
* This is the entry point of RiotX
* This Activity, when started with argument, is also doing some cleanup when user disconnects,
* This is the entry point of Element Android
* This Activity, when started with argument, is also doing some cleanup when user signs out,
* clears cache, is logged out, or is soft logged out
*/
class MainActivity : VectorBaseActivity(), UnlockedActivity {
@ -78,6 +78,8 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity {
intent.putExtra(EXTRA_ARGS, args)
activity.startActivity(intent)
// Ensure all the Activities are destroyed, it seems that the intent flags are not enough now.
activity.finishAffinity()
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.crypto.verification
import im.vector.app.R
import im.vector.app.core.platform.VectorBaseFragment
import javax.inject.Inject
class QuadSLoadingFragment @Inject constructor() : VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_progress
}

View file

@ -31,5 +31,6 @@ sealed class VerificationAction : VectorViewModelAction {
object SkipVerification : VerificationAction()
object VerifyFromPassphrase : VerificationAction()
data class GotResultFromSsss(val cypherData: String, val alias: String) : VerificationAction()
object CancelledFromSsss : VerificationAction()
object SecuredStorageHasBeenReset : VerificationAction()
}

View file

@ -155,6 +155,8 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
// all have been reset, so we are verified?
viewModel.handle(VerificationAction.SecuredStorageHasBeenReset)
}
} else {
viewModel.handle(VerificationAction.CancelledFromSsss)
}
}
@ -209,6 +211,10 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
return@withState
}
if (state.selfVerificationMode && state.verifyingFrom4S) {
showFragment(QuadSLoadingFragment::class, Bundle())
return@withState
}
if (state.selfVerificationMode && state.verifiedFromPrivateKeys) {
showFragment(VerificationConclusionFragment::class, Bundle().apply {
putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(true, null, state.isMe))

View file

@ -32,6 +32,7 @@ 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
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.session.Session
@ -70,6 +71,7 @@ data class VerificationBottomSheetViewState(
// true when we display the loading and we wait for the other (incoming request)
val selfVerificationMode: Boolean = false,
val verifiedFromPrivateKeys: Boolean = false,
val verifyingFrom4S: Boolean = false,
val isMe: Boolean = false,
val currentDeviceCanCrossSign: Boolean = false,
val userWantsToCancel: Boolean = false,
@ -170,7 +172,9 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
}
} else {
// if the verification is already done you can't cancel anymore
if (state.pendingRequest.invoke()?.cancelConclusion != null || state.sasTransactionState is VerificationTxState.TerminalTxState) {
if (state.pendingRequest.invoke()?.cancelConclusion != null
|| state.sasTransactionState is VerificationTxState.TerminalTxState
|| state.verifyingFrom4S) {
// you cannot cancel anymore
} else {
setState {
@ -346,6 +350,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
_viewEvents.post(VerificationBottomSheetViewEvents.Dismiss)
}
is VerificationAction.VerifyFromPassphrase -> {
setState { copy(verifyingFrom4S = true) }
_viewEvents.post(VerificationBottomSheetViewEvents.AccessSecretStore)
}
is VerificationAction.GotResultFromSsss -> {
@ -354,56 +359,73 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
VerificationAction.SecuredStorageHasBeenReset -> {
if (session.cryptoService().crossSigningService().allPrivateKeysKnown()) {
setState {
copy(quadSHasBeenReset = true)
copy(quadSHasBeenReset = true, verifyingFrom4S = false)
}
}
Unit
}
VerificationAction.CancelledFromSsss -> {
setState {
copy(verifyingFrom4S = false)
}
}
}.exhaustive
}
private fun handleSecretBackFromSSSS(action: VerificationAction.GotResultFromSsss) {
try {
action.cypherData.fromBase64().inputStream().use { ins ->
val res = session.loadSecureSecret<Map<String, String>>(ins, action.alias)
val trustResult = session.cryptoService().crossSigningService().checkTrustFromPrivateKeys(
res?.get(MASTER_KEY_SSSS_NAME),
res?.get(USER_SIGNING_KEY_SSSS_NAME),
res?.get(SELF_SIGNING_KEY_SSSS_NAME)
)
if (trustResult.isVerified()) {
// Sign this device and upload the signature
session.sessionParams.deviceId?.let { deviceId ->
session.cryptoService()
.crossSigningService().trustDevice(deviceId, object : MatrixCallback<Unit> {
override fun onFailure(failure: Throwable) {
Timber.w(failure, "Failed to sign my device after recovery")
}
})
}
viewModelScope.launch(Dispatchers.IO) {
try {
action.cypherData.fromBase64().inputStream().use { ins ->
val res = session.loadSecureSecret<Map<String, String>>(ins, action.alias)
val trustResult = session.cryptoService().crossSigningService().checkTrustFromPrivateKeys(
res?.get(MASTER_KEY_SSSS_NAME),
res?.get(USER_SIGNING_KEY_SSSS_NAME),
res?.get(SELF_SIGNING_KEY_SSSS_NAME)
)
if (trustResult.isVerified()) {
// Sign this device and upload the signature
session.sessionParams.deviceId?.let { deviceId ->
session.cryptoService()
.crossSigningService().trustDevice(deviceId, object : MatrixCallback<Unit> {
override fun onFailure(failure: Throwable) {
Timber.w(failure, "Failed to sign my device after recovery")
}
})
}
setState {
copy(verifiedFromPrivateKeys = true)
}
setState {
copy(
verifyingFrom4S = false,
verifiedFromPrivateKeys = true
)
}
// try to get keybackup key
} else {
// POP UP something
_viewEvents.post(VerificationBottomSheetViewEvents.ModalError(stringProvider.getString(R.string.error_failed_to_import_keys)))
// try the keybackup
tentativeRestoreBackup(res)
} else {
setState {
copy(
verifyingFrom4S = false
)
}
// POP UP something
_viewEvents.post(VerificationBottomSheetViewEvents.ModalError(stringProvider.getString(R.string.error_failed_to_import_keys)))
}
}
// try the keybackup
tentativeRestoreBackup(res)
Unit
} catch (failure: Throwable) {
setState {
copy(
verifyingFrom4S = false
)
}
_viewEvents.post(
VerificationBottomSheetViewEvents.ModalError(failure.localizedMessage ?: stringProvider.getString(R.string.unexpected_error)))
}
} catch (failure: Throwable) {
_viewEvents.post(
VerificationBottomSheetViewEvents.ModalError(failure.localizedMessage ?: stringProvider.getString(R.string.unexpected_error)))
}
}
private fun tentativeRestoreBackup(res: Map<String, String>?) {
viewModelScope.launch(Dispatchers.IO) {
GlobalScope.launch(Dispatchers.IO) {
try {
val secret = res?.get(KEYBACKUP_SECRET_SSSS_NAME) ?: return@launch Unit.also {
Timber.v("## Keybackup secret not restored from SSSS")

View file

@ -31,7 +31,6 @@ import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.identity.IdentityServiceError
import org.matrix.android.sdk.api.session.terms.GetTermsResponse
import org.matrix.android.sdk.api.session.terms.TermsService
import org.matrix.android.sdk.internal.util.awaitCallback
import java.net.UnknownHostException
@ -117,9 +116,7 @@ class SetIdentityServerViewModel @AssistedInject constructor(
private suspend fun checkTerms(baseUrl: String) {
try {
val data = awaitCallback<GetTermsResponse> {
mxSession.getTerms(TermsService.ServiceType.IdentityService, baseUrl, it)
}
val data = mxSession.getTerms(TermsService.ServiceType.IdentityService, baseUrl)
// has all been accepted?
val resp = data.serverResponse

View file

@ -91,7 +91,7 @@ class HomeActivityViewModel @AssistedInject constructor(
val isVerified = it.getOrNull()?.isTrusted() ?: false
if (!isVerified && onceTrusted) {
// cross signing keys have been reset
// Tigger a popup to re-verify
// Trigger a popup to re-verify
// Note: user can be null in case of logout
safeActiveSession.getUser(safeActiveSession.myUserId)
?.toMatrixItem()

View file

@ -294,28 +294,30 @@ class RoomListFragment @Inject constructor(
RoomListDisplayMode.NOTIFICATIONS -> {
if (hasNoRoom) {
StateView.State.Empty(
getString(R.string.room_list_catchup_welcome_title),
ContextCompat.getDrawable(requireContext(), R.drawable.ic_home_bottom_catchup),
getString(R.string.room_list_catchup_welcome_body)
title = getString(R.string.room_list_catchup_welcome_title),
image = ContextCompat.getDrawable(requireContext(), R.drawable.ic_home_bottom_catchup),
message = getString(R.string.room_list_catchup_welcome_body)
)
} else {
StateView.State.Empty(
getString(R.string.room_list_catchup_empty_title),
ContextCompat.getDrawable(requireContext(), R.drawable.ic_noun_party_popper),
getString(R.string.room_list_catchup_empty_body))
title = getString(R.string.room_list_catchup_empty_title),
image = ContextCompat.getDrawable(requireContext(), R.drawable.ic_noun_party_popper),
message = getString(R.string.room_list_catchup_empty_body))
}
}
RoomListDisplayMode.PEOPLE ->
StateView.State.Empty(
getString(R.string.room_list_people_empty_title),
ContextCompat.getDrawable(requireContext(), R.drawable.ic_home_bottom_chat),
getString(R.string.room_list_people_empty_body)
title = getString(R.string.room_list_people_empty_title),
image = ContextCompat.getDrawable(requireContext(), R.drawable.empty_state_dm),
isBigImage = true,
message = getString(R.string.room_list_people_empty_body)
)
RoomListDisplayMode.ROOMS ->
StateView.State.Empty(
getString(R.string.room_list_rooms_empty_title),
ContextCompat.getDrawable(requireContext(), R.drawable.ic_home_bottom_group),
getString(R.string.room_list_rooms_empty_body)
title = getString(R.string.room_list_rooms_empty_title),
image = ContextCompat.getDrawable(requireContext(), R.drawable.empty_state_room),
isBigImage = true,
message = getString(R.string.room_list_rooms_empty_body)
)
else ->
// Always display the content in this mode, because if the footer

View file

@ -33,7 +33,6 @@ import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.file.FileService
import org.matrix.android.sdk.api.session.room.model.message.MessageType
import org.matrix.android.sdk.api.session.room.model.message.getFileUrl
import org.matrix.android.sdk.api.session.room.uploads.GetUploadsResult
import org.matrix.android.sdk.internal.crypto.attachments.toElementToDecrypt
import org.matrix.android.sdk.internal.util.awaitCallback
import org.matrix.android.sdk.rx.rx
@ -90,9 +89,7 @@ class RoomUploadsViewModel @AssistedInject constructor(
viewModelScope.launch {
try {
val result = awaitCallback<GetUploadsResult> {
room.getUploads(20, token, it)
}
val result = room.getUploads(20, token)
token = result.nextToken

View file

@ -58,7 +58,6 @@ import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.NoOpMatrixCallback
import org.matrix.android.sdk.api.failure.isInvalidPassword
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerConfig
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
@ -214,7 +213,9 @@ class VectorSettingsGeneralFragment @Inject constructor(
it.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
// Disable it while updating the state, will be re-enabled by the account data listener.
it.isEnabled = false
session.integrationManagerService().setIntegrationEnabled(newValue as Boolean, NoOpMatrixCallback())
lifecycleScope.launch {
session.integrationManagerService().setIntegrationEnabled(newValue as Boolean)
}
true
}
}

View file

@ -28,8 +28,6 @@ import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorViewModel
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.terms.GetTermsResponse
import org.matrix.android.sdk.internal.util.awaitCallback
import timber.log.Timber
class ReviewTermsViewModel @AssistedInject constructor(
@ -94,15 +92,12 @@ class ReviewTermsViewModel @AssistedInject constructor(
viewModelScope.launch {
try {
awaitCallback<Unit> {
session.agreeToTerms(
termsArgs.type,
termsArgs.baseURL,
agreedUrls,
termsArgs.token,
it
)
}
session.agreeToTerms(
termsArgs.type,
termsArgs.baseURL,
agreedUrls,
termsArgs.token
)
_viewEvents.post(ReviewTermsViewEvents.Success)
} catch (failure: Throwable) {
Timber.e(failure, "Failed to agree to terms")
@ -122,9 +117,7 @@ class ReviewTermsViewModel @AssistedInject constructor(
viewModelScope.launch {
try {
val data = awaitCallback<GetTermsResponse> {
session.getTerms(termsArgs.type, termsArgs.baseURL, it)
}
val data = session.getTerms(termsArgs.type, termsArgs.baseURL)
val terms = data.serverResponse.getLocalizedTerms(action.preferredLanguageCode).map {
Term(it.localizedUrl ?: "",
it.localizedName ?: "",

View file

@ -29,7 +29,6 @@ import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.widgets.model.WidgetType
import org.matrix.android.sdk.internal.util.awaitCallback
import org.matrix.android.sdk.rx.rx
import timber.log.Timber
import java.net.URL
@ -106,14 +105,11 @@ class RoomWidgetPermissionViewModel @AssistedInject constructor(@Assisted val in
if (state.permissionData()?.isWebviewWidget.orFalse()) {
WidgetPermissionsHelper(integrationManagerService, widgetService).changePermission(state.roomId, widgetId, false)
} else {
awaitCallback<Unit> {
session.integrationManagerService().setNativeWidgetDomainAllowed(
state.permissionData.invoke()?.widget?.type?.preferred ?: "",
state.permissionData.invoke()?.widgetDomain ?: "",
false,
it
)
}
session.integrationManagerService().setNativeWidgetDomainAllowed(
state.permissionData.invoke()?.widget?.type?.preferred ?: "",
state.permissionData.invoke()?.widgetDomain ?: "",
false
)
}
} catch (failure: Throwable) {
Timber.v("Failure revoking widget: ${state.widgetId}")
@ -131,14 +127,11 @@ class RoomWidgetPermissionViewModel @AssistedInject constructor(@Assisted val in
if (state.permissionData()?.isWebviewWidget.orFalse()) {
WidgetPermissionsHelper(integrationManagerService, widgetService).changePermission(state.roomId, widgetId, true)
} else {
awaitCallback<Unit> {
session.integrationManagerService().setNativeWidgetDomainAllowed(
state.permissionData.invoke()?.widget?.type?.preferred ?: "",
state.permissionData.invoke()?.widgetDomain ?: "",
true,
it
)
}
session.integrationManagerService().setNativeWidgetDomainAllowed(
state.permissionData.invoke()?.widget?.type?.preferred ?: "",
state.permissionData.invoke()?.widgetDomain ?: "",
true
)
}
} catch (failure: Throwable) {
Timber.v("Failure allowing widget: ${state.widgetId}")

View file

@ -19,7 +19,6 @@ package im.vector.app.features.widgets.permissions
import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.integrationmanager.IntegrationManagerService
import org.matrix.android.sdk.api.session.widgets.WidgetService
import org.matrix.android.sdk.internal.util.awaitCallback
class WidgetPermissionsHelper(private val integrationManagerService: IntegrationManagerService,
private val widgetService: WidgetService) {
@ -30,8 +29,6 @@ class WidgetPermissionsHelper(private val integrationManagerService: Integration
widgetId = QueryStringValue.Equals(widgetId, QueryStringValue.Case.SENSITIVE)
).firstOrNull()
val eventId = widget?.event?.eventId ?: return
awaitCallback<Unit> {
integrationManagerService.setWidgetAllowed(eventId, allow, it)
}
integrationManagerService.setWidgetAllowed(eventId, allow)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginTop="@dimen/layout_vertical_margin_big"
android:indeterminate="true" />
</RelativeLayout>

View file

@ -92,14 +92,14 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minWidth="300dp">
android:minWidth="300dp"
android:paddingTop="40dp">
<TextView
android:id="@+id/showUserCodeCardNameText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="40dp"
android:layout_marginEnd="16dp"
android:maxLines="1"
android:singleLine="true"

View file

@ -20,7 +20,9 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical"
android:padding="@dimen/layout_horizontal_margin">
android:padding="@dimen/layout_horizontal_margin"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/errorMessageView"
@ -32,7 +34,6 @@
android:textSize="16sp"
tools:text="Une erreur est survenue" />
<com.google.android.material.button.MaterialButton
android:id="@+id/errorRetryView"
android:layout_width="wrap_content"
@ -44,7 +45,6 @@
</LinearLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/emptyView"
android:layout_width="match_parent"
@ -53,47 +53,56 @@
android:orientation="vertical"
android:padding="@dimen/layout_horizontal_margin">
<ImageView
android:id="@+id/emptyImageView"
style="@style/VectorEmptyImageView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_gravity="center_horizontal"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="30dp"
android:maxHeight="350dp"
app:layout_constraintBottom_toTopOf="@id/emptyTitleView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:ignore="MissingPrefix"
tools:layout_constraintHeight_percent="0.5"
tools:src="@drawable/ic_search_no_results" />
<TextView
android:id="@+id/emptyTitleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="49dp"
android:gravity="center"
android:textColor="?riotx_text_primary"
android:textSize="15sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/emptyImageView"
app:layout_constraintBottom_toTopOf="@id/emptyMessageView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:text="@string/room_list_catchup_empty_title" />
<ImageView
android:id="@+id/emptyImageView"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_gravity="center_horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_noun_party_popper"
app:tint="?riotx_text_primary"
tools:ignore="MissingPrefix" />
app:layout_constraintTop_toBottomOf="@id/emptyImageView"
app:layout_constraintVertical_chainStyle="packed"
tools:text="@string/room_list_people_empty_title" />
<TextView
android:id="@+id/emptyMessageView"
android:layout_width="220dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="49dp"
android:layout_marginTop="20dp"
android:gravity="center"
android:textColor="?riotx_text_secondary"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/emptyImageView"
tools:text="@string/room_list_catchup_empty_body" />
app:layout_constraintTop_toBottomOf="@+id/emptyTitleView"
app:layout_constraintVertical_chainStyle="packed"
tools:text="@string/room_list_people_empty_body" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="VectorEmptyImageView">
<item name="android:visibility">gone</item>
</style>
</resources>

View file

@ -1644,9 +1644,9 @@
<string name="room_list_catchup_welcome_title">Welcome home!</string>
<string name="room_list_catchup_welcome_body">Catch up on unread messages here</string>
<string name="room_list_people_empty_title">Conversations</string>
<string name="room_list_people_empty_body">Your direct message conversations will be displayed here</string>
<string name="room_list_people_empty_body">Your direct message conversations will be displayed here. Tap the + bottom right to start some.</string>
<string name="room_list_rooms_empty_title">Rooms</string>
<string name="room_list_rooms_empty_body">Your rooms will be displayed here</string>
<string name="room_list_rooms_empty_body">Your rooms will be displayed here. Tap the + bottom right to find existing ones or start some of your own.</string>
<string name="title_activity_emoji_reaction_picker">Reactions</string>
<string name="reactions_agree">Agree</string>

View file

@ -3,4 +3,8 @@
<style name="Vector.PopupMenu" parent="Vector.PopupMenuBase" />
<style name="VectorEmptyImageView">
<item name="android:visibility">visible</item>
</style>
</resources>