diff --git a/vector/build.gradle b/vector/build.gradle index 66ec6808c8..263c561921 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -323,7 +323,7 @@ dependencies { implementation 'com.nulab-inc:zxcvbn:1.2.7' //Alerter - implementation 'com.tapadoo.android:alerter:4.0.3' + implementation 'com.tapadoo.android:alerter:5.1.2' implementation 'com.otaliastudios:autocomplete:1.1.0' diff --git a/vector/src/main/java/im/vector/riotx/VectorApplication.kt b/vector/src/main/java/im/vector/riotx/VectorApplication.kt index 81cf1402b0..bd85596924 100644 --- a/vector/src/main/java/im/vector/riotx/VectorApplication.kt +++ b/vector/src/main/java/im/vector/riotx/VectorApplication.kt @@ -48,6 +48,7 @@ import im.vector.riotx.features.lifecycle.VectorActivityLifecycleCallbacks import im.vector.riotx.features.notifications.NotificationDrawerManager import im.vector.riotx.features.notifications.NotificationUtils import im.vector.riotx.features.notifications.PushRuleTriggerListener +import im.vector.riotx.features.popup.PopupAlertManager import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler import im.vector.riotx.features.session.SessionListener import im.vector.riotx.features.settings.VectorPreferences @@ -77,6 +78,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration. @Inject lateinit var notificationUtils: NotificationUtils @Inject lateinit var appStateHandler: AppStateHandler @Inject lateinit var rxConfig: RxConfig + @Inject lateinit var popupAlertManager: PopupAlertManager lateinit var vectorComponent: VectorComponent private var fontThreadHandler: Handler? = null @@ -102,7 +104,7 @@ class VectorApplication : Application(), HasVectorInjector, MatrixConfiguration. BigImageViewer.initialize(GlideImageLoader.with(applicationContext)) EpoxyController.defaultDiffingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler() EpoxyController.defaultModelBuildingHandler = EpoxyAsyncUtil.getAsyncBackgroundHandler() - registerActivityLifecycleCallbacks(VectorActivityLifecycleCallbacks()) + registerActivityLifecycleCallbacks(VectorActivityLifecycleCallbacks(popupAlertManager)) val fontRequest = FontRequest( "com.google.android.gms.fonts", "com.google.android.gms", diff --git a/vector/src/main/java/im/vector/riotx/core/di/VectorComponent.kt b/vector/src/main/java/im/vector/riotx/core/di/VectorComponent.kt index 4ae92b29b1..2652f58b04 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/VectorComponent.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/VectorComponent.kt @@ -45,6 +45,7 @@ import im.vector.riotx.features.notifications.NotificationBroadcastReceiver import im.vector.riotx.features.notifications.NotificationDrawerManager import im.vector.riotx.features.notifications.NotificationUtils import im.vector.riotx.features.notifications.PushRuleTriggerListener +import im.vector.riotx.features.popup.PopupAlertManager import im.vector.riotx.features.rageshake.BugReporter import im.vector.riotx.features.rageshake.VectorFileLogger import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler @@ -128,6 +129,8 @@ interface VectorComponent { fun emojiDataSource(): EmojiDataSource + fun alertManager() : PopupAlertManager + @Component.Factory interface Factory { fun create(@BindsInstance context: Context): VectorComponent diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt index d9860e6bad..8fe128ef73 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt @@ -36,7 +36,9 @@ import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse import im.vector.riotx.R +import im.vector.riotx.features.popup.DefaultVectorAlert import im.vector.riotx.features.popup.PopupAlertManager +import im.vector.riotx.features.popup.VectorAlert import timber.log.Timber import java.text.DateFormat import java.text.SimpleDateFormat @@ -54,7 +56,7 @@ import javax.inject.Singleton */ @Singleton -class KeyRequestHandler @Inject constructor(private val context: Context) +class KeyRequestHandler @Inject constructor(private val context: Context, private val popupAlertManager: PopupAlertManager) : GossipingRequestListener, VerificationService.Listener { @@ -118,9 +120,9 @@ class KeyRequestHandler @Inject constructor(private val context: Context) } if (deviceInfo.isUnknown) { - session?.cryptoService()?.setDeviceVerification(DeviceTrustLevel(false, false), userId, deviceId) + session?.cryptoService()?.setDeviceVerification(DeviceTrustLevel(crossSigningVerified = false, locallyVerified = false), userId, deviceId) - deviceInfo.trustLevel = DeviceTrustLevel(false, false) + deviceInfo.trustLevel = DeviceTrustLevel(crossSigningVerified = false, locallyVerified = false) // can we get more info on this device? session?.cryptoService()?.getDevicesList(object : MatrixCallback<DevicesListResponse> { @@ -188,7 +190,7 @@ class KeyRequestHandler @Inject constructor(private val context: Context) } } - val alert = PopupAlertManager.VectorAlert( + val alert = DefaultVectorAlert( alertManagerId(userId, deviceId), context.getString(R.string.key_share_request), dialogText, @@ -210,7 +212,7 @@ class KeyRequestHandler @Inject constructor(private val context: Context) denyAllRequests(mappingKey) }) - PopupAlertManager.postVectorAlert(alert) + popupAlertManager.postVectorAlert(alert) } private fun denyAllRequests(mappingKey: String) { @@ -250,7 +252,7 @@ class KeyRequestHandler @Inject constructor(private val context: Context) && it.requestId == request.requestId } if (alertsToRequests[alertMgrUniqueKey]?.isEmpty() == true) { - PopupAlertManager.cancelAlert(alertMgrUniqueKey) + popupAlertManager.cancelAlert(alertMgrUniqueKey) alertsToRequests.remove(keyForMap(userId, deviceId)) } } @@ -261,7 +263,7 @@ class KeyRequestHandler @Inject constructor(private val context: Context) if (state == VerificationTxState.Verified) { // ok it's verified, see if we have key request for that shareAllSessions("${tx.otherDeviceId}${tx.otherUserId}") - PopupAlertManager.cancelAlert("ikr_${tx.otherDeviceId}${tx.otherUserId}") + popupAlertManager.cancelAlert("ikr_${tx.otherDeviceId}${tx.otherUserId}") } } // should do it with QR tx also @@ -271,7 +273,7 @@ class KeyRequestHandler @Inject constructor(private val context: Context) override fun markedAsManuallyVerified(userId: String, deviceId: String) { // accept related requests shareAllSessions(keyForMap(userId, deviceId)) - PopupAlertManager.cancelAlert(alertManagerId(userId, deviceId)) + popupAlertManager.cancelAlert(alertManagerId(userId, deviceId)) } private fun keyForMap(userId: String, deviceId: String) = "$deviceId$userId" diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/IncomingVerificationRequestHandler.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/IncomingVerificationRequestHandler.kt index e7e26f52a4..9f12e9eb55 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/IncomingVerificationRequestHandler.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/IncomingVerificationRequestHandler.kt @@ -17,15 +17,17 @@ package im.vector.riotx.features.crypto.verification import android.content.Context import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.session.crypto.verification.PendingVerificationRequest import im.vector.matrix.android.api.session.crypto.verification.VerificationService import im.vector.matrix.android.api.session.crypto.verification.VerificationTransaction import im.vector.matrix.android.api.session.crypto.verification.VerificationTxState -import im.vector.matrix.android.api.session.crypto.verification.PendingVerificationRequest +import im.vector.matrix.android.api.util.toMatrixItem import im.vector.riotx.R import im.vector.riotx.core.platform.VectorBaseActivity import im.vector.riotx.features.home.room.detail.RoomDetailActivity import im.vector.riotx.features.home.room.detail.RoomDetailArgs import im.vector.riotx.features.popup.PopupAlertManager +import im.vector.riotx.features.popup.VerificationVectorAlert import im.vector.riotx.features.themes.ThemeUtils import javax.inject.Inject import javax.inject.Singleton @@ -34,7 +36,9 @@ import javax.inject.Singleton * Listens to the VerificationManager and add a new notification when an incoming request is detected. */ @Singleton -class IncomingVerificationRequestHandler @Inject constructor(private val context: Context) : VerificationService.Listener { +class IncomingVerificationRequestHandler @Inject constructor( + private val context: Context, + private val popupAlertManager: PopupAlertManager) : VerificationService.Listener { private var session: Session? = null @@ -58,7 +62,7 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context val name = session?.getUser(tx.otherUserId)?.displayName ?: tx.otherUserId - val alert = PopupAlertManager.VectorAlert( + val alert = VerificationVectorAlert( uid, context.getString(R.string.sas_incoming_request_notif_title), context.getString(R.string.sas_incoming_request_notif_content, name), @@ -68,12 +72,15 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context // TODO a bit too hugly :/ activity.supportFragmentManager.findFragmentByTag(VerificationBottomSheet.WAITING_SELF_VERIF_TAG)?.let { false.also { - PopupAlertManager.cancelAlert(uid) + popupAlertManager.cancelAlert(uid) } } ?: true } else true }) .apply { + + matrixItem = session?.getUser(tx.otherUserId ?: "")?.toMatrixItem() + contentAction = Runnable { (weakCurrentActivity?.get() as? VectorBaseActivity)?.let { it.navigator.performDeviceVerification(it, tx.otherUserId, tx.transactionId) @@ -99,11 +106,11 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context // 10mn expiration expirationTimestamp = System.currentTimeMillis() + (10 * 60 * 1000L) } - PopupAlertManager.postVectorAlert(alert) + popupAlertManager.postVectorAlert(alert) } is VerificationTxState.TerminalTxState -> { // cancel related notification - PopupAlertManager.cancelAlert(uid) + popupAlertManager.cancelAlert(uid) } else -> Unit } @@ -115,7 +122,7 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context val name = session?.getUser(pr.otherUserId)?.displayName ?: pr.otherUserId - val alert = PopupAlertManager.VectorAlert( + val alert = VerificationVectorAlert( uniqueIdForVerificationRequest(pr), context.getString(R.string.sas_incoming_request_notif_title), "$name(${pr.otherUserId})", @@ -128,6 +135,9 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context } else true }) .apply { + + matrixItem = session?.getUser(pr.otherUserId)?.toMatrixItem() + contentAction = Runnable { (weakCurrentActivity?.get() as? VectorBaseActivity)?.let { val roomId = pr.roomId @@ -148,14 +158,14 @@ class IncomingVerificationRequestHandler @Inject constructor(private val context // 5mn expiration expirationTimestamp = System.currentTimeMillis() + (5 * 60 * 1000L) } - PopupAlertManager.postVectorAlert(alert) + popupAlertManager.postVectorAlert(alert) } } override fun verificationRequestUpdated(pr: PendingVerificationRequest) { // If an incoming request is readied (by another device?) we should discard the alert if (pr.isIncoming && (pr.isReady || pr.handledByOtherSession)) { - PopupAlertManager.cancelAlert(uniqueIdForVerificationRequest(pr)) + popupAlertManager.cancelAlert(uniqueIdForVerificationRequest(pr)) } } diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt index dfe80de9de..9a9c1241a1 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeActivity.kt @@ -42,6 +42,8 @@ import im.vector.riotx.core.pushers.PushersManager import im.vector.riotx.features.disclaimer.showDisclaimerDialog import im.vector.riotx.features.notifications.NotificationDrawerManager import im.vector.riotx.features.popup.PopupAlertManager +import im.vector.riotx.features.popup.VectorAlert +import im.vector.riotx.features.popup.VerificationVectorAlert import im.vector.riotx.features.rageshake.VectorUncaughtExceptionHandler import im.vector.riotx.features.settings.VectorPreferences import im.vector.riotx.features.workers.signout.SignOutViewModel @@ -60,6 +62,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable { @Inject lateinit var pushManager: PushersManager @Inject lateinit var notificationDrawerManager: NotificationDrawerManager @Inject lateinit var vectorPreferences: VectorPreferences + @Inject lateinit var popupAlertManager: PopupAlertManager private val drawerListener = object : DrawerLayout.SimpleDrawerListener() { override fun onDrawerStateChanged(newState: Int) { @@ -149,8 +152,8 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable { if (crossSigningEnabledOnAccount && myCrossSigningKeys?.isTrusted() == false) { // We need to ask sharedActionViewModel.hasDisplayedCompleteSecurityPrompt = true - PopupAlertManager.postVectorAlert( - PopupAlertManager.VectorAlert( + popupAlertManager.postVectorAlert( + VerificationVectorAlert( uid = "completeSecurity", title = getString(R.string.new_signin), description = getString(R.string.complete_security), diff --git a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt index 40b92923ec..6fbe6f58d3 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/HomeDetailFragment.kt @@ -42,6 +42,7 @@ import im.vector.riotx.features.home.room.list.RoomListFragment import im.vector.riotx.features.home.room.list.RoomListParams import im.vector.riotx.features.home.room.list.UnreadCounterBadgeView import im.vector.riotx.features.popup.PopupAlertManager +import im.vector.riotx.features.popup.VectorAlert import im.vector.riotx.features.workers.signout.SignOutViewModel import kotlinx.android.synthetic.main.fragment_home_detail.* import timber.log.Timber @@ -53,7 +54,8 @@ private const val INDEX_ROOMS = 2 class HomeDetailFragment @Inject constructor( val homeDetailViewModelFactory: HomeDetailViewModel.Factory, - private val avatarRenderer: AvatarRenderer + private val avatarRenderer: AvatarRenderer, + private val alertManager: PopupAlertManager ) : VectorBaseFragment(), KeysBackupBanner.Delegate { private val unreadCounterBadgeViews = arrayListOf<UnreadCounterBadgeView>() @@ -89,19 +91,21 @@ class HomeDetailFragment @Inject constructor( it.unknownSessions.invoke()?.let { unknownDevices -> Timber.v("## Detector - ${unknownDevices.size} Unknown sessions") unknownDevices.forEachIndexed { index, deviceInfo -> - Timber.v("## Detector - #${index} deviceId:${deviceInfo.deviceId} lastSeenTs:${deviceInfo.lastSeenTs}") + Timber.v("## Detector - #${index} deviceId:${deviceInfo.second.deviceId} lastSeenTs:${deviceInfo.second.lastSeenTs}") } if (it.canCrossSign && unknownDevices.isNotEmpty()) { - val newest = unknownDevices.first() + val newest = unknownDevices.first().second + val user = unknownDevices.first().first val uid = "ND_${newest.deviceId}" - PopupAlertManager.cancelAlert(uid) - PopupAlertManager.postVectorAlert( - PopupAlertManager.VectorAlert( + alertManager.cancelAlert(uid) + alertManager.postVectorAlert( + VectorAlert( uid = uid, title = getString(R.string.new_session), description = getString(R.string.new_session_review), iconId = R.drawable.ic_shield_warning ).apply { + matrixItem = user colorInt = ContextCompat.getColor(requireActivity(), R.color.riotx_accent) contentAction = Runnable { (weakCurrentActivity?.get() as? VectorBaseActivity) diff --git a/vector/src/main/java/im/vector/riotx/features/home/UnknwonDeviceDetectorSharedViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/UnknwonDeviceDetectorSharedViewModel.kt index addcf33684..6ba194bb54 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/UnknwonDeviceDetectorSharedViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/UnknwonDeviceDetectorSharedViewModel.kt @@ -22,7 +22,9 @@ import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Uninitialized import com.airbnb.mvrx.ViewModelContext import im.vector.matrix.android.api.session.Session +import im.vector.matrix.android.api.util.MatrixItem import im.vector.matrix.android.api.util.NoOpCancellable +import im.vector.matrix.android.api.util.toMatrixItem import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse import im.vector.matrix.rx.rx @@ -34,7 +36,8 @@ import im.vector.riotx.core.platform.VectorViewModel import io.reactivex.android.schedulers.AndroidSchedulers data class UnknownDevicesState( - val unknownSessions: Async<List<DeviceInfo>?> = Uninitialized, + val unknownSessions: Async<List<Pair<MatrixItem?,DeviceInfo>>?> = Uninitialized, + val verifiedSessions: Async<List<Pair<MatrixItem?,DeviceInfo>>?> = Uninitialized, val canCrossSign: Boolean = false ) : MvRxState @@ -51,6 +54,9 @@ class UnknownDeviceDetectorSharedViewModel(session: Session, initialState: Unkno resp.devices?.filter { info -> deviceList.firstOrNull { info.deviceId == it.deviceId }?.isVerified?.not() ?: false }?.sortedByDescending { it.lastSeenTs } + ?.map { + session.getUser(it.user_id ?: "")?.toMatrixItem() to it + } } .toObservable() } diff --git a/vector/src/main/java/im/vector/riotx/features/lifecycle/VectorActivityLifecycleCallbacks.kt b/vector/src/main/java/im/vector/riotx/features/lifecycle/VectorActivityLifecycleCallbacks.kt index bf932a74be..6074309f13 100644 --- a/vector/src/main/java/im/vector/riotx/features/lifecycle/VectorActivityLifecycleCallbacks.kt +++ b/vector/src/main/java/im/vector/riotx/features/lifecycle/VectorActivityLifecycleCallbacks.kt @@ -22,12 +22,12 @@ import android.os.Bundle import im.vector.riotx.features.popup.PopupAlertManager import javax.inject.Inject -class VectorActivityLifecycleCallbacks @Inject constructor() : Application.ActivityLifecycleCallbacks { +class VectorActivityLifecycleCallbacks constructor(private val popupAlertManager: PopupAlertManager) : Application.ActivityLifecycleCallbacks { override fun onActivityPaused(activity: Activity) { } override fun onActivityResumed(activity: Activity) { - PopupAlertManager.onNewActivityDisplayed(activity) + popupAlertManager.onNewActivityDisplayed(activity) } override fun onActivityStarted(activity: Activity) { diff --git a/vector/src/main/java/im/vector/riotx/features/popup/PopupAlertManager.kt b/vector/src/main/java/im/vector/riotx/features/popup/PopupAlertManager.kt index aa198eba02..1876d83617 100644 --- a/vector/src/main/java/im/vector/riotx/features/popup/PopupAlertManager.kt +++ b/vector/src/main/java/im/vector/riotx/features/popup/PopupAlertManager.kt @@ -20,20 +20,23 @@ import android.os.Build import android.os.Handler import android.os.Looper import android.view.View -import androidx.annotation.ColorInt -import androidx.annotation.ColorRes -import androidx.annotation.DrawableRes +import android.widget.ImageView import com.tapadoo.alerter.Alerter import com.tapadoo.alerter.OnHideAlertListener +import dagger.Lazy import im.vector.riotx.R +import im.vector.riotx.features.home.AvatarRenderer import timber.log.Timber import java.lang.ref.WeakReference +import javax.inject.Inject +import javax.inject.Singleton /** * Responsible of displaying important popup alerts on top of the screen. * Alerts are stacked and will be displayed sequentially */ -object PopupAlertManager { +@Singleton +class PopupAlertManager @Inject constructor(private val avatarRenderer: Lazy<AvatarRenderer>) { private var weakCurrentActivity: WeakReference<Activity>? = null private var currentAlerter: VectorAlert? = null @@ -160,9 +163,19 @@ object PopupAlertManager { clearLightStatusBar() alert.weakCurrentActivity = WeakReference(activity) - Alerter.create(activity) - .setTitle(alert.title) + val alerter = if (alert is VerificationVectorAlert) Alerter.create(activity, R.layout.alerter_verification_layout) + else Alerter.create(activity) + + alerter.setTitle(alert.title) .setText(alert.description) + .also { al -> + if (alert is VerificationVectorAlert) { + val tvCustomView = al.getLayoutContainer() + tvCustomView?.findViewById<ImageView>(R.id.ivUserAvatar)?.let { imageView -> + alert.matrixItem?.let { avatarRenderer.get().render(it, imageView) } + } + } + } .apply { if (!animate) { setEnterAnimation(R.anim.anim_alerter_no_anim) @@ -226,37 +239,4 @@ object PopupAlertManager { displayNextIfPossible() }, 500) } - - /** - * Dataclass to describe an important alert with actions. - */ - data class VectorAlert(val uid: String, - val title: String, - val description: String, - @DrawableRes val iconId: Int?, - val shouldBeDisplayedIn: ((Activity) -> Boolean)? = null) { - - data class Button(val title: String, val action: Runnable, val autoClose: Boolean) - - // will be set by manager, and accessible by actions at runtime - var weakCurrentActivity: WeakReference<Activity>? = null - - val actions = ArrayList<Button>() - - var contentAction: Runnable? = null - var dismissedAction: Runnable? = null - - /** If this timestamp is after current time, this alert will be skipped */ - var expirationTimestamp: Long? = null - - fun addButton(title: String, action: Runnable, autoClose: Boolean = true) { - actions.add(Button(title, action, autoClose)) - } - - @ColorRes - var colorRes: Int? = null - - @ColorInt - var colorInt: Int? = null - } } diff --git a/vector/src/main/java/im/vector/riotx/features/popup/VectorAlert.kt b/vector/src/main/java/im/vector/riotx/features/popup/VectorAlert.kt new file mode 100644 index 0000000000..259df1c7e0 --- /dev/null +++ b/vector/src/main/java/im/vector/riotx/features/popup/VectorAlert.kt @@ -0,0 +1,95 @@ +/* + * 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.riotx.features.popup + +import android.app.Activity +import androidx.annotation.ColorInt +import androidx.annotation.ColorRes +import androidx.annotation.DrawableRes +import im.vector.matrix.android.api.util.MatrixItem +import java.lang.ref.WeakReference + +interface VectorAlert { + val uid: String + val title: String + val description: String + val iconId: Int? + val shouldBeDisplayedIn: ((Activity) -> Boolean)? + + data class Button(val title: String, val action: Runnable, val autoClose: Boolean) + + // will be set by manager, and accessible by actions at runtime + var weakCurrentActivity: WeakReference<Activity>? + + val actions: MutableList<Button> + + var contentAction: Runnable? + var dismissedAction: Runnable? + + /** If this timestamp is after current time, this alert will be skipped */ + var expirationTimestamp: Long? + + fun addButton(title: String, action: Runnable, autoClose: Boolean = true) { + actions.add(Button(title, action, autoClose)) + } + + var colorRes: Int? + + var colorInt: Int? +} + +/** + * Dataclass to describe an important alert with actions. + */ +open class DefaultVectorAlert(override val uid: String, + override val title: String, + override val description: String, + @DrawableRes override val iconId: Int?, + override val shouldBeDisplayedIn: ((Activity) -> Boolean)? = null) : VectorAlert { + + // will be set by manager, and accessible by actions at runtime + override var weakCurrentActivity: WeakReference<Activity>? = null + + override val actions = ArrayList<VectorAlert.Button>() + + override var contentAction: Runnable? = null + override var dismissedAction: Runnable? = null + + /** If this timestamp is after current time, this alert will be skipped */ + override var expirationTimestamp: Long? = null + + override fun addButton(title: String, action: Runnable, autoClose: Boolean) { + actions.add(VectorAlert.Button(title, action, autoClose)) + } + + @ColorRes + override var colorRes: Int? = null + + @ColorInt + override var colorInt: Int? = null +} + +class VerificationVectorAlert(uid: String, + title: String, + override val description: String, + @DrawableRes override val iconId: Int?, + override val shouldBeDisplayedIn: ((Activity) -> Boolean)? = null +) : DefaultVectorAlert( + uid, title, description, iconId, shouldBeDisplayedIn +) { + var matrixItem: MatrixItem? = null +} diff --git a/vector/src/main/res/layout/alerter_verification_layout.xml b/vector/src/main/res/layout/alerter_verification_layout.xml new file mode 100644 index 0000000000..b06883b056 --- /dev/null +++ b/vector/src/main/res/layout/alerter_verification_layout.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:clipToPadding="false" + tools:background="@android:color/darker_gray" + tools:foreground="?android:attr/selectableItemBackground" + tools:style="@style/AlertStyle"> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/ivUserAvatar" + android:layout_width="40dp" + android:layout_height="40dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/alerter_texts" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:src="@tools:sample/avatars" /> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/ivIcon" + android:layout_width="24dp" + android:layout_height="24dp" + app:layout_constraintCircle="@+id/ivUserAvatar" + app:layout_constraintCircleAngle="135" + app:layout_constraintCircleRadius="20dp" + tools:ignore="MissingConstraints" + android:src="@drawable/ic_shield_warning" + tools:visibility="visible" /> + + <LinearLayout + android:id="@+id/alerter_texts" + android:layout_width="0dp" + android:layout_height="0dp" + android:orientation="vertical" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/ivUserAvatar" + app:layout_constraintTop_toTopOf="parent"> + + <androidx.appcompat.widget.AppCompatTextView + android:id="@+id/tvTitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/alerter_padding_half" + android:layout_marginEnd="@dimen/alerter_padding_half" + android:paddingStart="@dimen/alerter_padding_small" + android:paddingLeft="@dimen/alerter_padding_small" + android:paddingEnd="@dimen/alerter_padding_small" + android:textAppearance="@style/AlertTextAppearance.Title" + android:visibility="gone" + tools:text="Title" + tools:visibility="visible" /> + + <androidx.appcompat.widget.AppCompatTextView + android:id="@+id/tvText" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/alerter_padding_half" + android:layout_marginEnd="@dimen/alerter_padding_half" + android:paddingStart="@dimen/alerter_padding_small" + android:paddingLeft="@dimen/alerter_padding_small" + android:paddingTop="@dimen/alerter_padding_small" + android:paddingEnd="@dimen/alerter_padding_small" + android:paddingBottom="@dimen/alerter_padding_small" + android:textAppearance="@style/AlertTextAppearance.Text" + android:visibility="gone" + tools:text="Text" + tools:visibility="visible" /> + + </LinearLayout> + + <!-- <FrameLayout--> + <!-- android:id="@+id/flRightIconContainer"--> + <!-- android:layout_width="wrap_content"--> + <!-- android:layout_height="wrap_content"--> + <!-- android:layout_gravity="center_vertical">--> + + <!-- <androidx.appcompat.widget.AppCompatImageView--> + <!-- android:id="@+id/ivRightIcon"--> + <!-- android:layout_width="@dimen/alerter_alert_icn_size"--> + <!-- android:layout_height="@dimen/alerter_alert_icn_size"--> + <!-- android:maxWidth="@dimen/alerter_alert_icn_size"--> + <!-- android:maxHeight="@dimen/alerter_alert_icn_size"--> + <!-- android:visibility="gone"--> + <!-- app:srcCompat="@drawable/alerter_ic_notifications"--> + <!-- app:tint="@color/alert_default_icon_color"--> + <!-- tools:visibility="visible" />--> + <!-- </FrameLayout>--> +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file