Migrate to ViewBindings (#1072) - WIP

This commit is contained in:
Benoit Marty 2020-12-16 00:46:52 +01:00
parent 838340bbc8
commit 706736273c
204 changed files with 2225 additions and 1380 deletions

View file

@ -23,7 +23,7 @@ Test:
- -
Other changes: Other changes:
- - Migrate to ViewBindings (#1072)
Changes in Element 1.0.12 (2020-12-15) Changes in Element 1.0.12 (2020-12-15)
=================================================== ===================================================

View file

@ -16,7 +16,6 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
buildscript { buildscript {
repositories { repositories {
@ -55,6 +54,10 @@ android {
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = '1.8'
} }
buildFeatures {
viewBinding true
}
} }
dependencies { dependencies {

View file

@ -33,7 +33,8 @@ import androidx.core.view.isVisible
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.transition.TransitionManager import androidx.transition.TransitionManager
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
import kotlinx.android.synthetic.main.activity_attachment_viewer.* import im.vector.lib.attachmentviewer.databinding.ActivityAttachmentViewerBinding
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import kotlin.math.abs import kotlin.math.abs
@ -50,12 +51,14 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
private var overlayView: View? = null private var overlayView: View? = null
set(value) { set(value) {
if (value == overlayView) return if (value == overlayView) return
overlayView?.let { rootContainer.removeView(it) } overlayView?.let { views.rootContainer.removeView(it) }
rootContainer.addView(value) views.rootContainer.addView(value)
value?.updatePadding(top = topInset, bottom = bottomInset) value?.updatePadding(top = topInset, bottom = bottomInset)
field = value field = value
} }
private lateinit var views: ActivityAttachmentViewerBinding
private lateinit var swipeDismissHandler: SwipeToDismissHandler private lateinit var swipeDismissHandler: SwipeToDismissHandler
private lateinit var directionDetector: SwipeDirectionDetector private lateinit var directionDetector: SwipeDirectionDetector
private lateinit var scaleDetector: ScaleGestureDetector private lateinit var scaleDetector: ScaleGestureDetector
@ -95,17 +98,18 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)
setContentView(R.layout.activity_attachment_viewer) views = ActivityAttachmentViewerBinding.inflate(layoutInflater)
attachmentPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL setContentView(views.root)
views.attachmentPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL
attachmentsAdapter = AttachmentsAdapter() attachmentsAdapter = AttachmentsAdapter()
attachmentPager.adapter = attachmentsAdapter views.attachmentPager.adapter = attachmentsAdapter
imageTransitionView = transitionImageView imageTransitionView = views.transitionImageView
transitionImageContainer = findViewById(R.id.transitionImageContainer) transitionImageContainer = findViewById(R.id.transitionImageContainer)
pager2 = attachmentPager pager2 = views.attachmentPager
directionDetector = createSwipeDirectionDetector() directionDetector = createSwipeDirectionDetector()
gestureDetector = createGestureDetector() gestureDetector = createGestureDetector()
attachmentPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() { views.attachmentPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrollStateChanged(state: Int) { override fun onPageScrollStateChanged(state: Int) {
isImagePagerIdle = state == ViewPager2.SCROLL_STATE_IDLE isImagePagerIdle = state == ViewPager2.SCROLL_STATE_IDLE
} }
@ -116,12 +120,12 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
}) })
swipeDismissHandler = createSwipeToDismissHandler() swipeDismissHandler = createSwipeToDismissHandler()
rootContainer.setOnTouchListener(swipeDismissHandler) views.rootContainer.setOnTouchListener(swipeDismissHandler)
rootContainer.viewTreeObserver.addOnGlobalLayoutListener { swipeDismissHandler.translationLimit = dismissContainer.height / 4 } views.rootContainer.viewTreeObserver.addOnGlobalLayoutListener { swipeDismissHandler.translationLimit = views.dismissContainer.height / 4 }
scaleDetector = createScaleGestureDetector() scaleDetector = createScaleGestureDetector()
ViewCompat.setOnApplyWindowInsetsListener(rootContainer) { _, insets -> ViewCompat.setOnApplyWindowInsetsListener(views.rootContainer) { _, insets ->
overlayView?.updatePadding(top = insets.systemWindowInsetTop, bottom = insets.systemWindowInsetBottom) overlayView?.updatePadding(top = insets.systemWindowInsetTop, bottom = insets.systemWindowInsetBottom)
topInset = insets.systemWindowInsetTop topInset = insets.systemWindowInsetTop
bottomInset = insets.systemWindowInsetBottom bottomInset = insets.systemWindowInsetBottom
@ -170,7 +174,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
if (swipeDirection == null && (scaleDetector.isInProgress || ev.pointerCount > 1 || wasScaled)) { if (swipeDirection == null && (scaleDetector.isInProgress || ev.pointerCount > 1 || wasScaled)) {
wasScaled = true wasScaled = true
// Log.v("ATTACHEMENTS", "dispatch to pager") // Log.v("ATTACHEMENTS", "dispatch to pager")
return attachmentPager.dispatchTouchEvent(ev) return views.attachmentPager.dispatchTouchEvent(ev)
} }
// Log.v("ATTACHEMENTS", "is current item scaled ${isScaled()}") // Log.v("ATTACHEMENTS", "is current item scaled ${isScaled()}")
@ -196,16 +200,16 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
private fun handleEventActionDown(event: MotionEvent) { private fun handleEventActionDown(event: MotionEvent) {
swipeDirection = null swipeDirection = null
wasScaled = false wasScaled = false
attachmentPager.dispatchTouchEvent(event) views.attachmentPager.dispatchTouchEvent(event)
swipeDismissHandler.onTouch(rootContainer, event) swipeDismissHandler.onTouch(views.rootContainer, event)
isOverlayWasClicked = dispatchOverlayTouch(event) isOverlayWasClicked = dispatchOverlayTouch(event)
} }
private fun handleEventActionUp(event: MotionEvent) { private fun handleEventActionUp(event: MotionEvent) {
// wasDoubleTapped = false // wasDoubleTapped = false
swipeDismissHandler.onTouch(rootContainer, event) swipeDismissHandler.onTouch(views.rootContainer, event)
attachmentPager.dispatchTouchEvent(event) views.attachmentPager.dispatchTouchEvent(event)
isOverlayWasClicked = dispatchOverlayTouch(event) isOverlayWasClicked = dispatchOverlayTouch(event)
} }
@ -220,12 +224,12 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
private fun toggleOverlayViewVisibility() { private fun toggleOverlayViewVisibility() {
if (systemUiVisibility) { if (systemUiVisibility) {
// we hide // we hide
TransitionManager.beginDelayedTransition(rootContainer) TransitionManager.beginDelayedTransition(views.rootContainer)
hideSystemUI() hideSystemUI()
overlayView?.isVisible = false overlayView?.isVisible = false
} else { } else {
// we show // we show
TransitionManager.beginDelayedTransition(rootContainer) TransitionManager.beginDelayedTransition(views.rootContainer)
showSystemUI() showSystemUI()
overlayView?.isVisible = true overlayView?.isVisible = true
} }
@ -238,11 +242,11 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
return when (swipeDirection) { return when (swipeDirection) {
SwipeDirection.Up, SwipeDirection.Down -> { SwipeDirection.Up, SwipeDirection.Down -> {
if (isSwipeToDismissAllowed && !wasScaled && isImagePagerIdle) { if (isSwipeToDismissAllowed && !wasScaled && isImagePagerIdle) {
swipeDismissHandler.onTouch(rootContainer, event) swipeDismissHandler.onTouch(views.rootContainer, event)
} else true } else true
} }
SwipeDirection.Left, SwipeDirection.Right -> { SwipeDirection.Left, SwipeDirection.Right -> {
attachmentPager.dispatchTouchEvent(event) views.attachmentPager.dispatchTouchEvent(event)
} }
else -> true else -> true
} }
@ -250,8 +254,8 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
private fun handleSwipeViewMove(translationY: Float, translationLimit: Int) { private fun handleSwipeViewMove(translationY: Float, translationLimit: Int) {
val alpha = calculateTranslationAlpha(translationY, translationLimit) val alpha = calculateTranslationAlpha(translationY, translationLimit)
backgroundView.alpha = alpha views.backgroundView.alpha = alpha
dismissContainer.alpha = alpha views.dismissContainer.alpha = alpha
overlayView?.alpha = alpha overlayView?.alpha = alpha
} }
@ -265,7 +269,7 @@ abstract class AttachmentViewerActivity : AppCompatActivity(), AttachmentEventLi
private fun createSwipeToDismissHandler() private fun createSwipeToDismissHandler()
: SwipeToDismissHandler = SwipeToDismissHandler( : SwipeToDismissHandler = SwipeToDismissHandler(
swipeView = dismissContainer, swipeView = views.dismissContainer,
shouldAnimateDismiss = { shouldAnimateDismiss() }, shouldAnimateDismiss = { shouldAnimateDismiss() },
onDismiss = { animateClose() }, onDismiss = { animateClose() },
onSwipeViewMove = ::handleSwipeViewMove) onSwipeViewMove = ::handleSwipeViewMove)

View file

@ -1,7 +1,7 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-parcelize'
apply plugin: 'realm-android' apply plugin: 'realm-android'
buildscript { buildscript {
@ -13,10 +13,6 @@ buildscript {
} }
} }
androidExtensions {
experimental = true
}
android { android {
compileSdkVersion 29 compileSdkVersion 29
testOptions.unitTests.includeAndroidResources = true testOptions.unitTests.includeAndroidResources = true

View file

@ -19,7 +19,7 @@ package org.matrix.android.sdk.api.auth.data
import android.os.Parcelable import android.os.Parcelable
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
@Parcelize @Parcelize

View file

@ -20,7 +20,7 @@ import android.net.Uri
import android.os.Parcelable import android.os.Parcelable
import androidx.exifinterface.media.ExifInterface import androidx.exifinterface.media.ExifInterface
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.util.MimeTypes.normalizeMimeType import org.matrix.android.sdk.api.util.MimeTypes.normalizeMimeType
@Parcelize @Parcelize

View file

@ -17,7 +17,7 @@
package org.matrix.android.sdk.internal.auth.registration package org.matrix.android.sdk.internal.auth.registration
import android.os.Parcelable import android.os.Parcelable
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
/** /**
* This class represent a localized privacy policy for registration Flow. * This class represent a localized privacy policy for registration Flow.

View file

@ -18,7 +18,7 @@ package org.matrix.android.sdk.internal.crypto.attachments
import android.os.Parcelable import android.os.Parcelable
import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo import org.matrix.android.sdk.internal.crypto.model.rest.EncryptedFileInfo
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
fun EncryptedFileInfo.toElementToDecrypt(): ElementToDecrypt? { fun EncryptedFileInfo.toElementToDecrypt(): ElementToDecrypt? {
// Check the validity of some fields // Check the validity of some fields

View file

@ -16,7 +16,7 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-parcelize'
android { android {
compileSdkVersion 29 compileSdkVersion 29

View file

@ -3,7 +3,7 @@ package ${escapeKotlinIdentifiers(packageName)}
import android.os.Bundle import android.os.Bundle
<#if createFragmentArgs> <#if createFragmentArgs>
import android.os.Parcelable import android.os.Parcelable
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
import com.airbnb.mvrx.args import com.airbnb.mvrx.args
</#if> </#if>
import android.view.View import android.view.View

View file

@ -3,17 +3,13 @@ import com.android.build.OutputFile
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'com.google.android.gms.oss-licenses-plugin' apply plugin: 'com.google.android.gms.oss-licenses-plugin'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-parcelize'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
kapt { kapt {
correctErrorTypes = true correctErrorTypes = true
} }
androidExtensions {
experimental = true
}
// Note: 2 digits max for each value // Note: 2 digits max for each value
ext.versionMajor = 1 ext.versionMajor = 1
ext.versionMinor = 0 ext.versionMinor = 0
@ -280,6 +276,10 @@ android {
java.srcDirs += "src/sharedTest/java" java.srcDirs += "src/sharedTest/java"
} }
} }
buildFeatures {
viewBinding true
}
} }
dependencies { dependencies {

View file

@ -24,7 +24,7 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.utils.toast import im.vector.app.core.utils.toast
import kotlinx.android.synthetic.debug.activity_test_material_theme.*
// Rendering is not the same with VectorBaseActivity // Rendering is not the same with VectorBaseActivity
abstract class DebugMaterialThemeActivity : AppCompatActivity() { abstract class DebugMaterialThemeActivity : AppCompatActivity() {

View file

@ -21,6 +21,7 @@ import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.view.LayoutInflater
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.Person import androidx.core.app.Person
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
@ -34,16 +35,17 @@ import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA
import im.vector.app.core.utils.allGranted import im.vector.app.core.utils.allGranted
import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.toast import im.vector.app.core.utils.toast
import im.vector.app.databinding.ActivityDebugMenuBinding
import im.vector.app.features.debug.sas.DebugSasEmojiActivity import im.vector.app.features.debug.sas.DebugSasEmojiActivity
import im.vector.app.features.qrcode.QrCodeScannerActivity import im.vector.app.features.qrcode.QrCodeScannerActivity
import org.matrix.android.sdk.internal.crypto.verification.qrcode.toQrCodeData import org.matrix.android.sdk.internal.crypto.verification.qrcode.toQrCodeData
import kotlinx.android.synthetic.debug.activity_debug_menu.*
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class DebugMenuActivity : VectorBaseActivity() { class DebugMenuActivity : VectorBaseActivity<ActivityDebugMenuBinding>() {
override fun getLayoutRes() = R.layout.activity_debug_menu override fun getBinding() = ActivityDebugMenuBinding.inflate(layoutInflater)
@Inject @Inject
lateinit var activeSessionHolder: ActiveSessionHolder lateinit var activeSessionHolder: ActiveSessionHolder
@ -69,17 +71,17 @@ class DebugMenuActivity : VectorBaseActivity() {
} }
private fun setupViews() { private fun setupViews() {
debug_test_text_view_link.setOnClickListener { testTextViewLink() } views.debugTestTextViewLink.setOnClickListener { testTextViewLink() }
debug_show_sas_emoji.setOnClickListener { showSasEmoji() } views.debugShowSasEmoji.setOnClickListener { showSasEmoji() }
debug_test_notification.setOnClickListener { testNotification() } views.debugTestNotification.setOnClickListener { testNotification() }
debug_test_material_theme_light.setOnClickListener { testMaterialThemeLight() } views.debugTestMaterialThemeLight.setOnClickListener { testMaterialThemeLight() }
debug_test_material_theme_dark.setOnClickListener { testMaterialThemeDark() } views.debugTestMaterialThemeDark.setOnClickListener { testMaterialThemeDark() }
debug_test_crash.setOnClickListener { testCrash() } views.debugTestCrash.setOnClickListener { testCrash() }
debug_scan_qr_code.setOnClickListener { scanQRCode() } views.debugScanQrCode.setOnClickListener { scanQRCode() }
} }
private fun renderQrCode(text: String) { private fun renderQrCode(text: String) {
debug_qr_code.setData(text) views.debugQrCode.setData(text)
} }
private fun testTextViewLink() { private fun testTextViewLink() {

View file

@ -22,7 +22,7 @@ import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import im.vector.app.R import im.vector.app.R
import kotlinx.android.synthetic.debug.activity_test_linkify.*
class TestLinkifyActivity : AppCompatActivity() { class TestLinkifyActivity : AppCompatActivity() {

View file

@ -22,7 +22,7 @@ import im.vector.app.R
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import org.matrix.android.sdk.api.crypto.getAllVerificationEmojis import org.matrix.android.sdk.api.crypto.getAllVerificationEmojis
import kotlinx.android.synthetic.main.fragment_generic_recycler.*
class DebugSasEmojiActivity : AppCompatActivity() { class DebugSasEmojiActivity : AppCompatActivity() {
@ -30,12 +30,12 @@ class DebugSasEmojiActivity : AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.fragment_generic_recycler) setContentView(R.layout.fragment_generic_recycler)
val controller = SasEmojiController() val controller = SasEmojiController()
genericRecyclerView.configureWith(controller) views.genericRecyclerView.configureWith(controller)
controller.setData(SasState(getAllVerificationEmojis())) controller.setData(SasState(getAllVerificationEmojis()))
} }
override fun onDestroy() { override fun onDestroy() {
genericRecyclerView.cleanup() views.genericRecyclerView.cleanup()
super.onDestroy() super.onDestroy()
} }
} }

View file

@ -21,7 +21,7 @@ import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible import androidx.core.view.isVisible
import im.vector.app.R import im.vector.app.R
import kotlinx.android.synthetic.main.dialog_confirmation_with_reason.view.*
object ConfirmationDialogBuilder { object ConfirmationDialogBuilder {

View file

@ -31,7 +31,7 @@ fun ComponentActivity.registerStartForActivityResult(onResult: (ActivityResult)
return registerForActivityResult(ActivityResultContracts.StartActivityForResult(), onResult) return registerForActivityResult(ActivityResultContracts.StartActivityForResult(), onResult)
} }
fun VectorBaseActivity.addFragment( fun VectorBaseActivity<*>.addFragment(
frameId: Int, frameId: Int,
fragment: Fragment, fragment: Fragment,
allowStateLoss: Boolean = false allowStateLoss: Boolean = false
@ -39,7 +39,7 @@ fun VectorBaseActivity.addFragment(
supportFragmentManager.commitTransaction(allowStateLoss) { add(frameId, fragment) } supportFragmentManager.commitTransaction(allowStateLoss) { add(frameId, fragment) }
} }
fun <T : Fragment> VectorBaseActivity.addFragment( fun <T : Fragment> VectorBaseActivity<*>.addFragment(
frameId: Int, frameId: Int,
fragmentClass: Class<T>, fragmentClass: Class<T>,
params: Parcelable? = null, params: Parcelable? = null,
@ -51,7 +51,7 @@ fun <T : Fragment> VectorBaseActivity.addFragment(
} }
} }
fun VectorBaseActivity.replaceFragment( fun VectorBaseActivity<*>.replaceFragment(
frameId: Int, frameId: Int,
fragment: Fragment, fragment: Fragment,
tag: String? = null, tag: String? = null,
@ -60,7 +60,7 @@ fun VectorBaseActivity.replaceFragment(
supportFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment, tag) } supportFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment, tag) }
} }
fun <T : Fragment> VectorBaseActivity.replaceFragment( fun <T : Fragment> VectorBaseActivity<*>.replaceFragment(
frameId: Int, frameId: Int,
fragmentClass: Class<T>, fragmentClass: Class<T>,
params: Parcelable? = null, params: Parcelable? = null,
@ -72,7 +72,7 @@ fun <T : Fragment> VectorBaseActivity.replaceFragment(
} }
} }
fun VectorBaseActivity.addFragmentToBackstack( fun VectorBaseActivity<*>.addFragmentToBackstack(
frameId: Int, frameId: Int,
fragment: Fragment, fragment: Fragment,
tag: String? = null, tag: String? = null,
@ -81,19 +81,19 @@ fun VectorBaseActivity.addFragmentToBackstack(
supportFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment).addToBackStack(tag) } supportFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment).addToBackStack(tag) }
} }
fun <T : Fragment> VectorBaseActivity.addFragmentToBackstack(frameId: Int, fun <T : Fragment> VectorBaseActivity<*>.addFragmentToBackstack(frameId: Int,
fragmentClass: Class<T>, fragmentClass: Class<T>,
params: Parcelable? = null, params: Parcelable? = null,
tag: String? = null, tag: String? = null,
allowStateLoss: Boolean = false, allowStateLoss: Boolean = false,
option: ((FragmentTransaction) -> Unit)? = null) { option: ((FragmentTransaction) -> Unit)? = null) {
supportFragmentManager.commitTransaction(allowStateLoss) { supportFragmentManager.commitTransaction(allowStateLoss) {
option?.invoke(this) option?.invoke(this)
replace(frameId, fragmentClass, params.toMvRxBundle(), tag).addToBackStack(tag) replace(frameId, fragmentClass, params.toMvRxBundle(), tag).addToBackStack(tag)
} }
} }
fun VectorBaseActivity.hideKeyboard() { fun VectorBaseActivity<*>.hideKeyboard() {
currentFocus?.hideKeyboard() currentFocus?.hideKeyboard()
} }

View file

@ -23,6 +23,7 @@ import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.airbnb.mvrx.BaseMvRxFragment
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.selectTxtFileToWrite import im.vector.app.core.utils.selectTxtFileToWrite
@ -34,7 +35,7 @@ fun Fragment.registerStartForActivityResult(onResult: (ActivityResult) -> Unit):
return registerForActivityResult(ActivityResultContracts.StartActivityForResult(), onResult) return registerForActivityResult(ActivityResultContracts.StartActivityForResult(), onResult)
} }
fun VectorBaseFragment.addFragment( fun Fragment.addFragment(
frameId: Int, frameId: Int,
fragment: Fragment, fragment: Fragment,
allowStateLoss: Boolean = false allowStateLoss: Boolean = false
@ -42,7 +43,7 @@ fun VectorBaseFragment.addFragment(
parentFragmentManager.commitTransaction(allowStateLoss) { add(frameId, fragment) } parentFragmentManager.commitTransaction(allowStateLoss) { add(frameId, fragment) }
} }
fun <T : Fragment> VectorBaseFragment.addFragment( fun <T : Fragment> VectorBaseFragment<*>.addFragment(
frameId: Int, frameId: Int,
fragmentClass: Class<T>, fragmentClass: Class<T>,
params: Parcelable? = null, params: Parcelable? = null,
@ -54,7 +55,7 @@ fun <T : Fragment> VectorBaseFragment.addFragment(
} }
} }
fun VectorBaseFragment.replaceFragment( fun Fragment.replaceFragment(
frameId: Int, frameId: Int,
fragment: Fragment, fragment: Fragment,
allowStateLoss: Boolean = false allowStateLoss: Boolean = false
@ -62,7 +63,7 @@ fun VectorBaseFragment.replaceFragment(
parentFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment) } parentFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment) }
} }
fun <T : Fragment> VectorBaseFragment.replaceFragment( fun <T : Fragment> VectorBaseFragment<*>.replaceFragment(
frameId: Int, frameId: Int,
fragmentClass: Class<T>, fragmentClass: Class<T>,
params: Parcelable? = null, params: Parcelable? = null,
@ -74,7 +75,7 @@ fun <T : Fragment> VectorBaseFragment.replaceFragment(
} }
} }
fun VectorBaseFragment.addFragmentToBackstack( fun Fragment.addFragmentToBackstack(
frameId: Int, frameId: Int,
fragment: Fragment, fragment: Fragment,
tag: String? = null, tag: String? = null,
@ -83,7 +84,7 @@ fun VectorBaseFragment.addFragmentToBackstack(
parentFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment, tag).addToBackStack(tag) } parentFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment, tag).addToBackStack(tag) }
} }
fun <T : Fragment> VectorBaseFragment.addFragmentToBackstack( fun <T : Fragment> VectorBaseFragment<*>.addFragmentToBackstack(
frameId: Int, frameId: Int,
fragmentClass: Class<T>, fragmentClass: Class<T>,
params: Parcelable? = null, params: Parcelable? = null,
@ -95,7 +96,7 @@ fun <T : Fragment> VectorBaseFragment.addFragmentToBackstack(
} }
} }
fun VectorBaseFragment.addChildFragment( fun Fragment.addChildFragment(
frameId: Int, frameId: Int,
fragment: Fragment, fragment: Fragment,
tag: String? = null, tag: String? = null,
@ -104,7 +105,7 @@ fun VectorBaseFragment.addChildFragment(
childFragmentManager.commitTransaction(allowStateLoss) { add(frameId, fragment, tag) } childFragmentManager.commitTransaction(allowStateLoss) { add(frameId, fragment, tag) }
} }
fun <T : Fragment> VectorBaseFragment.addChildFragment( fun <T : Fragment> VectorBaseFragment<*>.addChildFragment(
frameId: Int, frameId: Int,
fragmentClass: Class<T>, fragmentClass: Class<T>,
params: Parcelable? = null, params: Parcelable? = null,
@ -116,7 +117,7 @@ fun <T : Fragment> VectorBaseFragment.addChildFragment(
} }
} }
fun VectorBaseFragment.replaceChildFragment( fun Fragment.replaceChildFragment(
frameId: Int, frameId: Int,
fragment: Fragment, fragment: Fragment,
tag: String? = null, tag: String? = null,
@ -125,7 +126,7 @@ fun VectorBaseFragment.replaceChildFragment(
childFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment, tag) } childFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment, tag) }
} }
fun <T : Fragment> VectorBaseFragment.replaceChildFragment( fun <T : Fragment> VectorBaseFragment<*>.replaceChildFragment(
frameId: Int, frameId: Int,
fragmentClass: Class<T>, fragmentClass: Class<T>,
params: Parcelable? = null, params: Parcelable? = null,
@ -137,7 +138,7 @@ fun <T : Fragment> VectorBaseFragment.replaceChildFragment(
} }
} }
fun VectorBaseFragment.addChildFragmentToBackstack( fun Fragment.addChildFragmentToBackstack(
frameId: Int, frameId: Int,
fragment: Fragment, fragment: Fragment,
tag: String? = null, tag: String? = null,
@ -146,7 +147,7 @@ fun VectorBaseFragment.addChildFragmentToBackstack(
childFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment).addToBackStack(tag) } childFragmentManager.commitTransaction(allowStateLoss) { replace(frameId, fragment).addToBackStack(tag) }
} }
fun <T : Fragment> VectorBaseFragment.addChildFragmentToBackstack( fun <T : Fragment> VectorBaseFragment<*>.addChildFragmentToBackstack(
frameId: Int, frameId: Int,
fragmentClass: Class<T>, fragmentClass: Class<T>,
params: Parcelable? = null, params: Parcelable? = null,

View file

@ -25,7 +25,7 @@ import android.widget.FrameLayout
import androidx.core.view.isInvisible import androidx.core.view.isInvisible
import androidx.core.view.isVisible import androidx.core.view.isVisible
import im.vector.app.R import im.vector.app.R
import kotlinx.android.synthetic.main.view_button_state.view.*
class ButtonStateView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0) class ButtonStateView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0)
: FrameLayout(context, attrs, defStyle) { : FrameLayout(context, attrs, defStyle) {

View file

@ -15,23 +15,24 @@
*/ */
package im.vector.app.core.platform package im.vector.app.core.platform
import android.view.LayoutInflater
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ScreenComponent import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.hideKeyboard
import kotlinx.android.synthetic.main.activity.* import im.vector.app.databinding.ActivityBinding
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import javax.inject.Inject import javax.inject.Inject
/** /**
* Simple activity with a toolbar, a waiting overlay, and a fragment container and a session. * Simple activity with a toolbar, a waiting overlay, and a fragment container and a session.
*/ */
abstract class SimpleFragmentActivity : VectorBaseActivity() { abstract class SimpleFragmentActivity : VectorBaseActivity<ActivityBinding>() {
override fun getLayoutRes() = R.layout.activity override fun getBinding() = ActivityBinding.inflate(layoutInflater)
@Inject lateinit var session: Session @Inject lateinit var session: Session
@ -41,8 +42,8 @@ abstract class SimpleFragmentActivity : VectorBaseActivity() {
} }
override fun initUiAndData() { override fun initUiAndData() {
configureToolbar(toolbar) configureToolbar(views.toolbar)
waitingView = findViewById(R.id.waiting_view) waitingView = views.overlayWaitingView.waitingView
} }
/** /**
@ -51,21 +52,21 @@ abstract class SimpleFragmentActivity : VectorBaseActivity() {
*/ */
fun updateWaitingView(data: WaitingViewData?) { fun updateWaitingView(data: WaitingViewData?) {
data?.let { data?.let {
waitingStatusText.text = data.message views.overlayWaitingView.waitingStatusText.text = data.message
if (data.progress != null && data.progressTotal != null) { if (data.progress != null && data.progressTotal != null) {
waitingHorizontalProgress.isIndeterminate = false views.overlayWaitingView.waitingHorizontalProgress.isIndeterminate = false
waitingHorizontalProgress.progress = data.progress views.overlayWaitingView.waitingHorizontalProgress.progress = data.progress
waitingHorizontalProgress.max = data.progressTotal views.overlayWaitingView.waitingHorizontalProgress.max = data.progressTotal
waitingHorizontalProgress.isVisible = true views.overlayWaitingView.waitingHorizontalProgress.isVisible = true
waitingCircularProgress.isVisible = false views.overlayWaitingView.waitingCircularProgress.isVisible = false
} else if (data.isIndeterminate) { } else if (data.isIndeterminate) {
waitingHorizontalProgress.isIndeterminate = true views.overlayWaitingView.waitingHorizontalProgress.isIndeterminate = true
waitingHorizontalProgress.isVisible = true views.overlayWaitingView.waitingHorizontalProgress.isVisible = true
waitingCircularProgress.isVisible = false views.overlayWaitingView.waitingCircularProgress.isVisible = false
} else { } else {
waitingHorizontalProgress.isVisible = false views.overlayWaitingView.waitingHorizontalProgress.isVisible = false
waitingCircularProgress.isVisible = true views.overlayWaitingView.waitingCircularProgress.isVisible = true
} }
showWaitingView() showWaitingView()
@ -76,15 +77,15 @@ abstract class SimpleFragmentActivity : VectorBaseActivity() {
override fun showWaitingView() { override fun showWaitingView() {
hideKeyboard() hideKeyboard()
waitingStatusText.isGone = waitingStatusText.text.isNullOrBlank() views.overlayWaitingView.waitingStatusText.isGone = views.overlayWaitingView.waitingStatusText.text.isNullOrBlank()
super.showWaitingView() super.showWaitingView()
} }
override fun hideWaitingView() { override fun hideWaitingView() {
waitingStatusText.text = null views.overlayWaitingView.waitingStatusText.text = null
waitingStatusText.isGone = true views.overlayWaitingView.waitingStatusText.isGone = true
waitingHorizontalProgress.progress = 0 views.overlayWaitingView.waitingHorizontalProgress.progress = 0
waitingHorizontalProgress.isVisible = false views.overlayWaitingView.waitingHorizontalProgress.isVisible = false
super.hideWaitingView() super.hideWaitingView()
} }

View file

@ -24,7 +24,7 @@ import android.widget.FrameLayout
import androidx.core.view.isVisible import androidx.core.view.isVisible
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.updateConstraintSet 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) class StateView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0)
: FrameLayout(context, attrs, defStyle) { : FrameLayout(context, attrs, defStyle) {

View file

@ -21,24 +21,26 @@ import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import androidx.annotation.LayoutRes
import androidx.annotation.MainThread import androidx.annotation.MainThread
import androidx.annotation.MenuRes import androidx.annotation.MenuRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentFactory import androidx.fragment.app.FragmentFactory
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.viewbinding.ViewBinding
import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MvRx
import com.bumptech.glide.util.Util import com.bumptech.glide.util.Util
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
@ -79,13 +81,19 @@ import im.vector.app.receivers.DebugReceiver
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.activity.*
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.failure.GlobalError import org.matrix.android.sdk.api.failure.GlobalError
import timber.log.Timber import timber.log.Timber
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector { abstract class VectorBaseActivity<VB: ViewBinding> : AppCompatActivity(), HasScreenInjector {
/* ==========================================================================================
* View
* ========================================================================================== */
protected lateinit var views: VB
/* ========================================================================================== /* ==========================================================================================
* View model * View model
* ========================================================================================== */ * ========================================================================================== */
@ -210,9 +218,8 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
// Hack for font size // Hack for font size
applyFontSize() applyFontSize()
if (getLayoutRes() != -1) { views = getBinding()
setContentView(getLayoutRes()) setContentView(views.root)
}
this.savedInstanceState = savedInstanceState this.savedInstanceState = savedInstanceState
@ -450,7 +457,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
} }
private fun recursivelyDispatchOnBackPressed(fm: FragmentManager, fromToolbar: Boolean): Boolean { private fun recursivelyDispatchOnBackPressed(fm: FragmentManager, fromToolbar: Boolean): Boolean {
val reverseOrder = fm.fragments.filterIsInstance<VectorBaseFragment>().reversed() val reverseOrder = fm.fragments.filterIsInstance<VectorBaseFragment<*>>().reversed()
for (f in reverseOrder) { for (f in reverseOrder) {
val handledByChildFragments = recursivelyDispatchOnBackPressed(f.childFragmentManager, fromToolbar) val handledByChildFragments = recursivelyDispatchOnBackPressed(f.childFragmentManager, fromToolbar)
if (handledByChildFragments) { if (handledByChildFragments) {
@ -537,8 +544,7 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
* OPEN METHODS * OPEN METHODS
* ========================================================================================== */ * ========================================================================================== */
@LayoutRes abstract fun getBinding(): VB
open fun getLayoutRes() = -1
open fun displayInFullscreen() = false open fun displayInFullscreen() = false
@ -565,13 +571,13 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
* ========================================================================================== */ * ========================================================================================== */
fun showSnackbar(message: String) { fun showSnackbar(message: String) {
coordinatorLayout?.let { getCoordinatorLayout()?.let {
Snackbar.make(it, message, Snackbar.LENGTH_SHORT).show() Snackbar.make(it, message, Snackbar.LENGTH_SHORT).show()
} }
} }
fun showSnackbar(message: String, @StringRes withActionTitle: Int?, action: (() -> Unit)?) { fun showSnackbar(message: String, @StringRes withActionTitle: Int?, action: (() -> Unit)?) {
coordinatorLayout?.let { getCoordinatorLayout()?.let {
Snackbar.make(it, message, Snackbar.LENGTH_LONG).apply { Snackbar.make(it, message, Snackbar.LENGTH_LONG).apply {
withActionTitle?.let { withActionTitle?.let {
setAction(withActionTitle, { action?.invoke() }) setAction(withActionTitle, { action?.invoke() })
@ -580,6 +586,9 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
} }
} }
// TODO BMA Provide the CL from the Views
open fun getCoordinatorLayout(): CoordinatorLayout? = null
/* ========================================================================================== /* ==========================================================================================
* User Consent * User Consent
* ========================================================================================== */ * ========================================================================================== */

View file

@ -27,6 +27,7 @@ import android.widget.FrameLayout
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.viewbinding.ViewBinding
import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.MvRxView import com.airbnb.mvrx.MvRxView
import com.airbnb.mvrx.MvRxViewId import com.airbnb.mvrx.MvRxViewId
@ -46,7 +47,7 @@ import java.util.concurrent.TimeUnit
/** /**
* Add MvRx capabilities to bottomsheetdialog (like BaseMvRxFragment) * Add MvRx capabilities to bottomsheetdialog (like BaseMvRxFragment)
*/ */
abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment(), MvRxView { abstract class VectorBaseBottomSheetDialogFragment<VB: ViewBinding> : BottomSheetDialogFragment(), MvRxView {
private val mvrxViewIdProperty = MvRxViewId() private val mvrxViewIdProperty = MvRxViewId()
final override val mvrxViewId: String by mvrxViewIdProperty final override val mvrxViewId: String by mvrxViewIdProperty
@ -56,8 +57,13 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment()
* View * View
* ========================================================================================== */ * ========================================================================================== */
@LayoutRes private var _binding: VB? = null
abstract fun getLayoutResId(): Int
// This property is only valid between onCreateView and onDestroyView.
protected val views: VB
get() = _binding!!
abstract fun getBinding(inflater: LayoutInflater, container: ViewGroup?): VB
/* ========================================================================================== /* ==========================================================================================
* View model * View model
@ -77,8 +83,8 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment()
private var bottomSheetBehavior: BottomSheetBehavior<FrameLayout>? = null private var bottomSheetBehavior: BottomSheetBehavior<FrameLayout>? = null
val vectorBaseActivity: VectorBaseActivity by lazy { val vectorBaseActivity: VectorBaseActivity<*> by lazy {
activity as VectorBaseActivity activity as VectorBaseActivity<*>
} }
open val showExpanded = false open val showExpanded = false
@ -102,7 +108,8 @@ abstract class VectorBaseBottomSheetDialogFragment : BottomSheetDialogFragment()
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(getLayoutResId(), container, false) _binding = getBinding(inflater, container)
return views.root
} }
@CallSuper @CallSuper

View file

@ -33,6 +33,7 @@ import androidx.annotation.MainThread
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.viewbinding.ViewBinding
import com.airbnb.mvrx.BaseMvRxFragment import com.airbnb.mvrx.BaseMvRxFragment
import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MvRx
import com.bumptech.glide.util.Util.assertMainThread import com.bumptech.glide.util.Util.assertMainThread
@ -48,14 +49,14 @@ import im.vector.app.features.navigation.Navigator
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.activity.*
import timber.log.Timber import timber.log.Timber
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector { abstract class VectorBaseFragment<VB: ViewBinding> : BaseMvRxFragment(), HasScreenInjector {
protected val vectorBaseActivity: VectorBaseActivity by lazy { protected val vectorBaseActivity: VectorBaseActivity<*> by lazy {
activity as VectorBaseActivity activity as VectorBaseActivity<*>
} }
/* ========================================================================================== /* ==========================================================================================
@ -82,6 +83,16 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
protected val fragmentViewModelProvider protected val fragmentViewModelProvider
get() = ViewModelProvider(this, viewModelFactory) get() = ViewModelProvider(this, viewModelFactory)
/* ==========================================================================================
* Views
* ========================================================================================== */
private var _binding: VB? = null
// This property is only valid between onCreateView and onDestroyView.
protected val views: VB
get() = _binding!!
/* ========================================================================================== /* ==========================================================================================
* Life cycle * Life cycle
* ========================================================================================== */ * ========================================================================================== */
@ -106,11 +117,11 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
final override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { final override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
Timber.i("onCreateView Fragment ${javaClass.simpleName}") Timber.i("onCreateView Fragment ${javaClass.simpleName}")
return inflater.inflate(getLayoutResId(), container, false) _binding = getBinding(inflater, container)
return views.root
} }
@LayoutRes abstract fun getBinding(inflater: LayoutInflater, container: ViewGroup?): VB
abstract fun getLayoutResId(): Int
@CallSuper @CallSuper
override fun onResume() { override fun onResume() {
@ -137,6 +148,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
super.onDestroyView() super.onDestroyView()
Timber.i("onDestroyView Fragment ${javaClass.simpleName}") Timber.i("onDestroyView Fragment ${javaClass.simpleName}")
uiDisposables.clear() uiDisposables.clear()
_binding = null
} }
override fun onDestroy() { override fun onDestroy() {
@ -174,6 +186,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
arguments = args.toMvRxBundle() arguments = args.toMvRxBundle()
} }
// TODO BMA Extract this and use simple type in Fragment.kt
fun Parcelable?.toMvRxBundle(): Bundle? { fun Parcelable?.toMvRxBundle(): Bundle? {
return this?.let { Bundle().apply { putParcelable(MvRx.KEY_ARG, it) } } return this?.let { Bundle().apply { putParcelable(MvRx.KEY_ARG, it) } }
} }
@ -186,7 +199,7 @@ abstract class VectorBaseFragment : BaseMvRxFragment(), HasScreenInjector {
} }
protected fun showErrorInSnackbar(throwable: Throwable) { protected fun showErrorInSnackbar(throwable: Throwable) {
vectorBaseActivity.coordinatorLayout?.let { vectorBaseActivity.getCoordinatorLayout()?.let {
Snackbar.make(it, errorFormatter.toHumanReadable(throwable), Snackbar.LENGTH_SHORT) Snackbar.make(it, errorFormatter.toHumanReadable(throwable), Snackbar.LENGTH_SHORT)
.show() .show()
} }

View file

@ -17,41 +17,47 @@
package im.vector.app.core.ui.bottomsheet package im.vector.app.core.ui.bottomsheet
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import kotlinx.android.synthetic.main.bottom_sheet_generic_list.* import im.vector.app.databinding.BottomSheetGenericListBinding
import im.vector.app.databinding.FragmentGenericRecyclerBinding
import javax.inject.Inject import javax.inject.Inject
/** /**
* Generic Bottom sheet with actions * Generic Bottom sheet with actions
*/ */
abstract class BottomSheetGeneric<STATE : BottomSheetGenericState, ACTION : BottomSheetGenericAction> : abstract class BottomSheetGeneric<STATE : BottomSheetGenericState, ACTION : BottomSheetGenericAction> :
VectorBaseBottomSheetDialogFragment(), VectorBaseBottomSheetDialogFragment<BottomSheetGenericListBinding>(),
BottomSheetGenericController.Listener<ACTION> { BottomSheetGenericController.Listener<ACTION> {
@Inject lateinit var sharedViewPool: RecyclerView.RecycledViewPool @Inject lateinit var sharedViewPool: RecyclerView.RecycledViewPool
final override val showExpanded = true final override val showExpanded = true
final override fun getLayoutResId() = R.layout.bottom_sheet_generic_list final override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetGenericListBinding {
return BottomSheetGenericListBinding.inflate(inflater, container, false)
}
abstract fun getController(): BottomSheetGenericController<STATE, ACTION> abstract fun getController(): BottomSheetGenericController<STATE, ACTION>
@CallSuper @CallSuper
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
bottomSheetRecyclerView.configureWith(getController(), viewPool = sharedViewPool, hasFixedSize = false, disableItemAnimation = true) views.views.bottomSheetRecyclerView.configureWith(getController(), viewPool = sharedViewPool, hasFixedSize = false, disableItemAnimation = true)
getController().listener = this getController().listener = this
} }
@CallSuper @CallSuper
override fun onDestroyView() { override fun onDestroyView() {
bottomSheetRecyclerView.cleanup() views.views.bottomSheetRecyclerView.cleanup()
getController().listener = null getController().listener = null
super.onDestroyView() super.onDestroyView()
} }

View file

@ -29,8 +29,6 @@ import androidx.core.content.withStyledAttributes
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isInvisible import androidx.core.view.isInvisible
import androidx.core.view.isVisible import androidx.core.view.isVisible
import butterknife.BindView
import butterknife.ButterKnife
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
@ -41,20 +39,11 @@ class BottomSheetActionButton @JvmOverloads constructor(
defStyleAttr: Int = 0 defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) { ) : FrameLayout(context, attrs, defStyleAttr) {
@BindView(R.id.itemVerificationActionTitle) private val actionTextView: TextView
lateinit var actionTextView: TextView private val descriptionTextView: TextView
private val leftIconImageView: ImageView
@BindView(R.id.itemVerificationActionSubTitle) private val rightIconImageView: ImageView
lateinit var descriptionTextView: TextView private val clickableView: View
@BindView(R.id.itemVerificationLeftIcon)
lateinit var leftIconImageView: ImageView
@BindView(R.id.itemVerificationActionIcon)
lateinit var rightIconImageView: ImageView
@BindView(R.id.itemVerificationClickableZone)
lateinit var clickableView: View
var title: String? = null var title: String? = null
set(value) { set(value) {
@ -116,7 +105,12 @@ class BottomSheetActionButton @JvmOverloads constructor(
init { init {
inflate(context, R.layout.item_verification_action, this) inflate(context, R.layout.item_verification_action, this)
ButterKnife.bind(this)
actionTextView = findViewById(R.id.itemVerificationActionTitle)
descriptionTextView = findViewById(R.id.itemVerificationActionSubTitle)
leftIconImageView = findViewById(R.id.itemVerificationLeftIcon)
rightIconImageView = findViewById(R.id.itemVerificationActionIcon)
clickableView = findViewById(R.id.itemVerificationClickableZone)
context.withStyledAttributes(attrs, R.styleable.BottomSheetActionButton) { context.withStyledAttributes(attrs, R.styleable.BottomSheetActionButton) {
title = getString(R.styleable.BottomSheetActionButton_actionTitle) ?: "" title = getString(R.styleable.BottomSheetActionButton_actionTitle) ?: ""

View file

@ -22,7 +22,7 @@ import android.view.View
import android.widget.RelativeLayout import android.widget.RelativeLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import im.vector.app.R import im.vector.app.R
import kotlinx.android.synthetic.main.view_jump_to_read_marker.view.*
class JumpToReadMarkerView @JvmOverloads constructor( class JumpToReadMarkerView @JvmOverloads constructor(
context: Context, context: Context,

View file

@ -24,7 +24,7 @@ import androidx.core.content.edit
import androidx.core.view.isVisible import androidx.core.view.isVisible
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.DefaultSharedPreferences import im.vector.app.core.di.DefaultSharedPreferences
import kotlinx.android.synthetic.main.view_keys_backup_banner.view.*
import timber.log.Timber import timber.log.Timber
/** /**

View file

@ -28,7 +28,7 @@ import im.vector.app.R
import im.vector.app.core.error.ResourceLimitErrorFormatter import im.vector.app.core.error.ResourceLimitErrorFormatter
import im.vector.app.core.utils.DimensionConverter import im.vector.app.core.utils.DimensionConverter
import im.vector.app.features.themes.ThemeUtils import im.vector.app.features.themes.ThemeUtils
import kotlinx.android.synthetic.main.view_notification_area.view.*
import me.gujun.android.span.span import me.gujun.android.span.span
import me.saket.bettermovementmethod.BetterLinkMovementMethod import me.saket.bettermovementmethod.BetterLinkMovementMethod
import org.matrix.android.sdk.api.failure.MatrixError import org.matrix.android.sdk.api.failure.MatrixError

View file

@ -22,7 +22,7 @@ import android.widget.LinearLayout
import androidx.annotation.IntRange import androidx.annotation.IntRange
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import im.vector.app.R import im.vector.app.R
import kotlinx.android.synthetic.main.view_password_strength_bar.view.*
/** /**
* A password strength bar custom widget * A password strength bar custom widget

View file

@ -26,7 +26,7 @@ import im.vector.app.R
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData
import im.vector.app.features.home.room.detail.timeline.item.toMatrixItem import im.vector.app.features.home.room.detail.timeline.item.toMatrixItem
import kotlinx.android.synthetic.main.view_read_receipts.view.*
private const val MAX_RECEIPT_DISPLAYED = 5 private const val MAX_RECEIPT_DISPLAYED = 5
private const val MAX_RECEIPT_DESCRIBED = 3 private const val MAX_RECEIPT_DESCRIBED = 3

View file

@ -20,6 +20,7 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
@ -30,6 +31,7 @@ import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.extensions.startSyncing import im.vector.app.core.extensions.startSyncing
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.utils.deleteAllFiles import im.vector.app.core.utils.deleteAllFiles
import im.vector.app.databinding.FragmentLoadingBinding
import im.vector.app.features.home.HomeActivity import im.vector.app.features.home.HomeActivity
import im.vector.app.features.home.ShortcutsHandler import im.vector.app.features.home.ShortcutsHandler
import im.vector.app.features.login.LoginActivity import im.vector.app.features.login.LoginActivity
@ -42,7 +44,7 @@ import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.signout.hard.SignedOutActivity import im.vector.app.features.signout.hard.SignedOutActivity
import im.vector.app.features.signout.soft.SoftLogoutActivity import im.vector.app.features.signout.soft.SoftLogoutActivity
import im.vector.app.features.ui.UiStateRepository import im.vector.app.features.ui.UiStateRepository
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -66,7 +68,7 @@ data class MainActivityArgs(
* This Activity, when started with argument, is also doing some cleanup when user signs out, * 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 * clears cache, is logged out, or is soft logged out
*/ */
class MainActivity : VectorBaseActivity(), UnlockedActivity { class MainActivity : VectorBaseActivity<FragmentLoadingBinding>(), UnlockedActivity {
companion object { companion object {
private const val EXTRA_ARGS = "EXTRA_ARGS" private const val EXTRA_ARGS = "EXTRA_ARGS"
@ -83,6 +85,8 @@ class MainActivity : VectorBaseActivity(), UnlockedActivity {
} }
} }
override fun getBinding() = FragmentLoadingBinding.inflate(layoutInflater)
private lateinit var args: MainActivityArgs private lateinit var args: MainActivityArgs
@Inject lateinit var notificationDrawerManager: NotificationDrawerManager @Inject lateinit var notificationDrawerManager: NotificationDrawerManager

View file

@ -19,15 +19,17 @@ package im.vector.app.features.attachments.preview
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.view.LayoutInflater
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.ToolbarConfigurable import im.vector.app.core.platform.ToolbarConfigurable
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivitySimpleBinding
import im.vector.app.features.themes.ActivityOtherThemes import im.vector.app.features.themes.ActivityOtherThemes
import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.content.ContentAttachmentData
class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable { class AttachmentsPreviewActivity : VectorBaseActivity<ActivitySimpleBinding>(), ToolbarConfigurable {
companion object { companion object {
private const val EXTRA_FRAGMENT_ARGS = "EXTRA_FRAGMENT_ARGS" private const val EXTRA_FRAGMENT_ARGS = "EXTRA_FRAGMENT_ARGS"
@ -51,7 +53,7 @@ class AttachmentsPreviewActivity : VectorBaseActivity(), ToolbarConfigurable {
override fun getOtherThemes() = ActivityOtherThemes.AttachmentsPreview override fun getOtherThemes() = ActivityOtherThemes.AttachmentsPreview
override fun getLayoutRes() = R.layout.activity_simple override fun getBinding() = ActivitySimpleBinding.inflate(layoutInflater)
override fun initUiAndData() { override fun initUiAndData() {
if (isFirstCreation()) { if (isFirstCreation()) {

View file

@ -21,6 +21,7 @@ import android.app.Activity.RESULT_CANCELED
import android.app.Activity.RESULT_OK import android.app.Activity.RESULT_OK
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
@ -45,9 +46,10 @@ import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.utils.OnSnapPositionChangeListener import im.vector.app.core.utils.OnSnapPositionChangeListener
import im.vector.app.core.utils.SnapOnScrollListener import im.vector.app.core.utils.SnapOnScrollListener
import im.vector.app.core.utils.attachSnapHelperWithListener import im.vector.app.core.utils.attachSnapHelperWithListener
import im.vector.app.databinding.FragmentAttachmentsPreviewBinding
import im.vector.app.features.media.createUCropWithDefaultSettings import im.vector.app.features.media.createUCropWithDefaultSettings
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.android.synthetic.main.fragment_attachments_preview.*
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.content.ContentAttachmentData import org.matrix.android.sdk.api.session.content.ContentAttachmentData
import java.io.File import java.io.File
@ -62,19 +64,21 @@ class AttachmentsPreviewFragment @Inject constructor(
private val attachmentMiniaturePreviewController: AttachmentMiniaturePreviewController, private val attachmentMiniaturePreviewController: AttachmentMiniaturePreviewController,
private val attachmentBigPreviewController: AttachmentBigPreviewController, private val attachmentBigPreviewController: AttachmentBigPreviewController,
private val colorProvider: ColorProvider private val colorProvider: ColorProvider
) : VectorBaseFragment(), AttachmentMiniaturePreviewController.Callback { ) : VectorBaseFragment<FragmentAttachmentsPreviewBinding>(), AttachmentMiniaturePreviewController.Callback {
private val fragmentArgs: AttachmentsPreviewArgs by args() private val fragmentArgs: AttachmentsPreviewArgs by args()
private val viewModel: AttachmentsPreviewViewModel by fragmentViewModel() private val viewModel: AttachmentsPreviewViewModel by fragmentViewModel()
override fun getLayoutResId() = R.layout.fragment_attachments_preview override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentAttachmentsPreviewBinding {
return FragmentAttachmentsPreviewBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
applyInsets() applyInsets()
setupRecyclerViews() setupRecyclerViews()
setupToolbar(attachmentPreviewerToolbar) setupToolbar(views.attachmentPreviewerToolbar)
attachmentPreviewerSendButton.setOnClickListener { views.attachmentPreviewerSendButton.setOnClickListener {
setResultAndFinish() setResultAndFinish()
} }
} }
@ -120,8 +124,8 @@ class AttachmentsPreviewFragment @Inject constructor(
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
attachmentPreviewerMiniatureList.cleanup() views.attachmentPreviewerMiniatureList.cleanup()
attachmentPreviewerBigList.cleanup() views.attachmentPreviewerBigList.cleanup()
attachmentMiniaturePreviewController.callback = null attachmentMiniaturePreviewController.callback = null
} }
@ -133,9 +137,9 @@ class AttachmentsPreviewFragment @Inject constructor(
} else { } else {
attachmentMiniaturePreviewController.setData(state) attachmentMiniaturePreviewController.setData(state)
attachmentBigPreviewController.setData(state) attachmentBigPreviewController.setData(state)
attachmentPreviewerBigList.scrollToPosition(state.currentAttachmentIndex) views.attachmentPreviewerBigList.scrollToPosition(state.currentAttachmentIndex)
attachmentPreviewerMiniatureList.scrollToPosition(state.currentAttachmentIndex) views.attachmentPreviewerMiniatureList.scrollToPosition(state.currentAttachmentIndex)
attachmentPreviewerSendImageOriginalSize.text = resources.getQuantityString(R.plurals.send_images_with_original_size, state.attachments.size) views.attachmentPreviewerSendImageOriginalSize.text = resources.getQuantityString(R.plurals.send_images_with_original_size, state.attachments.size)
} }
} }
@ -146,17 +150,17 @@ class AttachmentsPreviewFragment @Inject constructor(
private fun setResultAndFinish() = withState(viewModel) { private fun setResultAndFinish() = withState(viewModel) {
(requireActivity() as? AttachmentsPreviewActivity)?.setResultAndFinish( (requireActivity() as? AttachmentsPreviewActivity)?.setResultAndFinish(
it.attachments, it.attachments,
attachmentPreviewerSendImageOriginalSize.isChecked views.attachmentPreviewerSendImageOriginalSize.isChecked
) )
} }
private fun applyInsets() { private fun applyInsets() {
view?.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION view?.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
ViewCompat.setOnApplyWindowInsetsListener(attachmentPreviewerBottomContainer) { v, insets -> ViewCompat.setOnApplyWindowInsetsListener(views.attachmentPreviewerBottomContainer) { v, insets ->
v.updatePadding(bottom = insets.systemWindowInsetBottom) v.updatePadding(bottom = insets.systemWindowInsetBottom)
insets insets
} }
ViewCompat.setOnApplyWindowInsetsListener(attachmentPreviewerToolbar) { v, insets -> ViewCompat.setOnApplyWindowInsetsListener(views.attachmentPreviewerToolbar) { v, insets ->
v.updateLayoutParams<ViewGroup.MarginLayoutParams> { v.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = insets.systemWindowInsetTop topMargin = insets.systemWindowInsetTop
} }
@ -180,13 +184,13 @@ class AttachmentsPreviewFragment @Inject constructor(
private fun setupRecyclerViews() { private fun setupRecyclerViews() {
attachmentMiniaturePreviewController.callback = this attachmentMiniaturePreviewController.callback = this
attachmentPreviewerMiniatureList.let { views.attachmentPreviewerMiniatureList.let {
it.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) it.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
it.setHasFixedSize(true) it.setHasFixedSize(true)
it.adapter = attachmentMiniaturePreviewController.adapter it.adapter = attachmentMiniaturePreviewController.adapter
} }
attachmentPreviewerBigList.let { views.attachmentPreviewerBigList.let {
it.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) it.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
it.attachSnapHelperWithListener( it.attachSnapHelperWithListener(
PagerSnapHelper(), PagerSnapHelper(),

View file

@ -17,18 +17,24 @@
package im.vector.app.features.call package im.vector.app.features.call
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.activityViewModel
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import kotlinx.android.synthetic.main.bottom_sheet_call_controls.* import im.vector.app.databinding.BottomSheetCallControlsBinding
import im.vector.app.databinding.BottomSheetGenericListBinding
import me.gujun.android.span.span import me.gujun.android.span.span
class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment() { class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetCallControlsBinding>() {
override fun getLayoutResId() = R.layout.bottom_sheet_call_controls override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetCallControlsBinding {
return BottomSheetCallControlsBinding.inflate(inflater, container, false)
}
private val callViewModel: VectorCallViewModel by activityViewModel() private val callViewModel: VectorCallViewModel by activityViewModel()
@ -39,16 +45,16 @@ class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment() {
renderState(it) renderState(it)
} }
callControlsSoundDevice.clickableView.debouncedClicks { views.callControlsSoundDevice.clickableView.debouncedClicks {
callViewModel.handle(VectorCallViewActions.SwitchSoundDevice) callViewModel.handle(VectorCallViewActions.SwitchSoundDevice)
} }
callControlsSwitchCamera.clickableView.debouncedClicks { views.callControlsSwitchCamera.clickableView.debouncedClicks {
callViewModel.handle(VectorCallViewActions.ToggleCamera) callViewModel.handle(VectorCallViewActions.ToggleCamera)
dismiss() dismiss()
} }
callControlsToggleSDHD.clickableView.debouncedClicks { views.callControlsToggleSDHD.clickableView.debouncedClicks {
callViewModel.handle(VectorCallViewActions.ToggleHDSD) callViewModel.handle(VectorCallViewActions.ToggleHDSD)
dismiss() dismiss()
} }
@ -109,30 +115,30 @@ class CallControlsBottomSheet : VectorBaseBottomSheetDialogFragment() {
} }
private fun renderState(state: VectorCallViewState) { private fun renderState(state: VectorCallViewState) {
callControlsSoundDevice.title = getString(R.string.call_select_sound_device) views.callControlsSoundDevice.title = getString(R.string.call_select_sound_device)
callControlsSoundDevice.subTitle = when (state.soundDevice) { views.callControlsSoundDevice.subTitle = when (state.soundDevice) {
CallAudioManager.SoundDevice.PHONE -> getString(R.string.sound_device_phone) CallAudioManager.SoundDevice.PHONE -> getString(R.string.sound_device_phone)
CallAudioManager.SoundDevice.SPEAKER -> getString(R.string.sound_device_speaker) CallAudioManager.SoundDevice.SPEAKER -> getString(R.string.sound_device_speaker)
CallAudioManager.SoundDevice.HEADSET -> getString(R.string.sound_device_headset) CallAudioManager.SoundDevice.HEADSET -> getString(R.string.sound_device_headset)
CallAudioManager.SoundDevice.WIRELESS_HEADSET -> getString(R.string.sound_device_wireless_headset) CallAudioManager.SoundDevice.WIRELESS_HEADSET -> getString(R.string.sound_device_wireless_headset)
} }
callControlsSwitchCamera.isVisible = state.isVideoCall && state.canSwitchCamera views.callControlsSwitchCamera.isVisible = state.isVideoCall && state.canSwitchCamera
callControlsSwitchCamera.subTitle = getString(if (state.isFrontCamera) R.string.call_camera_front else R.string.call_camera_back) views.callControlsSwitchCamera.subTitle = getString(if (state.isFrontCamera) R.string.call_camera_front else R.string.call_camera_back)
if (state.isVideoCall) { if (state.isVideoCall) {
callControlsToggleSDHD.isVisible = true views.callControlsToggleSDHD.isVisible = true
if (state.isHD) { if (state.isHD) {
callControlsToggleSDHD.title = getString(R.string.call_format_turn_hd_off) views.callControlsToggleSDHD.title = getString(R.string.call_format_turn_hd_off)
callControlsToggleSDHD.subTitle = null views.callControlsToggleSDHD.subTitle = null
callControlsToggleSDHD.leftIcon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_hd_disabled) views.callControlsToggleSDHD.leftIcon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_hd_disabled)
} else { } else {
callControlsToggleSDHD.title = getString(R.string.call_format_turn_hd_on) views.callControlsToggleSDHD.title = getString(R.string.call_format_turn_hd_on)
callControlsToggleSDHD.subTitle = null views.callControlsToggleSDHD.subTitle = null
callControlsToggleSDHD.leftIcon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_hd) views.callControlsToggleSDHD.leftIcon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_hd)
} }
} else { } else {
callControlsToggleSDHD.isVisible = false views.callControlsToggleSDHD.isVisible = false
} }
} }
} }

View file

@ -18,11 +18,13 @@ package im.vector.app.features.call
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible import androidx.core.view.isVisible
import im.vector.app.R import im.vector.app.R
import kotlinx.android.synthetic.main.view_call_controls.view.*
import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.session.call.CallState
import org.webrtc.PeerConnection import org.webrtc.PeerConnection
@ -30,19 +32,40 @@ class CallControlsView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) { ) : LinearLayout(context, attrs, defStyleAttr) {
private var ringingControls: View
private var connectedControls: View
private val ringingControlAccept: View
private var ringingControlDecline: View
private var iv_end_call: View
private var muteIcon: ImageView
private var videoToggleIcon: ImageView
private var iv_leftMiniControl: View
private var iv_more: View
var interactionListener: InteractionListener? = null var interactionListener: InteractionListener? = null
init { init {
ConstraintLayout.inflate(context, R.layout.view_call_controls, this) View.inflate(context, R.layout.view_call_controls, this)
// layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) // layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
ringingControlAccept.setOnClickListener { acceptIncomingCall() } ringingControlAccept = findViewById<View>(R.id.ringingControlAccept)
ringingControlDecline.setOnClickListener { declineIncomingCall() } .also { it.setOnClickListener { acceptIncomingCall() } }
iv_end_call.setOnClickListener { endOngoingCall() } ringingControlDecline = findViewById<View>(R.id.ringingControlDecline)
muteIcon.setOnClickListener { toggleMute() } .also { it.setOnClickListener { declineIncomingCall() } }
videoToggleIcon.setOnClickListener { toggleVideo() } iv_end_call = findViewById<View>(R.id.iv_end_call)
iv_leftMiniControl.setOnClickListener { returnToChat() } .also { it.setOnClickListener { endOngoingCall() } }
iv_more.setOnClickListener { moreControlOption() } muteIcon = findViewById<ImageView>(R.id.muteIcon)
.also { it.setOnClickListener { toggleMute() } }
videoToggleIcon = findViewById<ImageView>(R.id.videoToggleIcon)
.also { it.setOnClickListener { toggleVideo() } }
iv_leftMiniControl = findViewById<View>(R.id.iv_leftMiniControl)
.also { it.setOnClickListener { returnToChat() } }
iv_more = findViewById<View>(R.id.iv_more)
.also { it.setOnClickListener { moreControlOption() } }
ringingControls = findViewById(R.id.ringingControls)
connectedControls = findViewById(R.id.connectedControls)
} }
private fun acceptIncomingCall() { private fun acceptIncomingCall() {

View file

@ -23,6 +23,7 @@ import android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.Window import android.view.Window
import android.view.WindowManager import android.view.WindowManager
@ -44,12 +45,13 @@ import im.vector.app.core.utils.PERMISSIONS_FOR_AUDIO_IP_CALL
import im.vector.app.core.utils.PERMISSIONS_FOR_VIDEO_IP_CALL import im.vector.app.core.utils.PERMISSIONS_FOR_VIDEO_IP_CALL
import im.vector.app.core.utils.allGranted import im.vector.app.core.utils.allGranted
import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.checkPermissions
import im.vector.app.databinding.ActivityCallBinding
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailActivity
import im.vector.app.features.home.room.detail.RoomDetailArgs import im.vector.app.features.home.room.detail.RoomDetailArgs
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.android.synthetic.main.activity_call.*
import org.matrix.android.sdk.api.session.call.CallState import org.matrix.android.sdk.api.session.call.CallState
import org.matrix.android.sdk.api.session.call.EglUtils import org.matrix.android.sdk.api.session.call.EglUtils
import org.matrix.android.sdk.api.session.call.MxCallDetail import org.matrix.android.sdk.api.session.call.MxCallDetail
@ -70,9 +72,9 @@ data class CallArgs(
val isVideoCall: Boolean val isVideoCall: Boolean
) : Parcelable ) : Parcelable
class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionListener { class VectorCallActivity : VectorBaseActivity<ActivityCallBinding>(), CallControlsView.InteractionListener {
override fun getLayoutRes() = R.layout.activity_call override fun getBinding() = ActivityCallBinding.inflate(layoutInflater)
@Inject lateinit var avatarRenderer: AvatarRenderer @Inject lateinit var avatarRenderer: AvatarRenderer
@ -147,7 +149,7 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// This will need to be refined // This will need to be refined
ViewCompat.setOnApplyWindowInsetsListener(constraintLayout) { v, insets -> ViewCompat.setOnApplyWindowInsetsListener(views.constraintLayout) { v, insets ->
v.updatePadding(bottom = if (systemUiVisibility) insets.systemWindowInsetBottom else 0) v.updatePadding(bottom = if (systemUiVisibility) insets.systemWindowInsetBottom else 0)
insets insets
} }
@ -167,7 +169,7 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
turnScreenOnAndKeyguardOff() turnScreenOnAndKeyguardOff()
} }
constraintLayout.clicks() views.constraintLayout.clicks()
.throttleFirst(300, TimeUnit.MILLISECONDS) .throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe { toggleUiSystemVisibility() } .subscribe { toggleUiSystemVisibility() }
@ -199,10 +201,10 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
} }
override fun onDestroy() { override fun onDestroy() {
peerConnectionManager.detachRenderers(listOf(pipRenderer, fullscreenRenderer)) peerConnectionManager.detachRenderers(listOf(views.pipRenderer, views.fullscreenRenderer))
if (surfaceRenderersAreInitialized) { if (surfaceRenderersAreInitialized) {
pipRenderer.release() views.pipRenderer.release()
fullscreenRenderer.release() views.fullscreenRenderer.release()
} }
turnScreenOffAndKeyguardOn() turnScreenOffAndKeyguardOn()
super.onDestroy() super.onDestroy()
@ -217,54 +219,54 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
return return
} }
callControlsView.updateForState(state) views.callControlsView.updateForState(state)
val callState = state.callState.invoke() val callState = state.callState.invoke()
callConnectingProgress.isVisible = false views.callConnectingProgress.isVisible = false
when (callState) { when (callState) {
is CallState.Idle, is CallState.Idle,
is CallState.Dialing -> { is CallState.Dialing -> {
callVideoGroup.isInvisible = true views.callVideoGroup.isInvisible = true
callInfoGroup.isVisible = true views.callInfoGroup.isVisible = true
callStatusText.setText(R.string.call_ring) views.callStatusText.setText(R.string.call_ring)
configureCallInfo(state) configureCallInfo(state)
} }
is CallState.LocalRinging -> { is CallState.LocalRinging -> {
callVideoGroup.isInvisible = true views.callVideoGroup.isInvisible = true
callInfoGroup.isVisible = true views.callInfoGroup.isVisible = true
callStatusText.text = null views.callStatusText.text = null
configureCallInfo(state) configureCallInfo(state)
} }
is CallState.Answering -> { is CallState.Answering -> {
callVideoGroup.isInvisible = true views.callVideoGroup.isInvisible = true
callInfoGroup.isVisible = true views.callInfoGroup.isVisible = true
callStatusText.setText(R.string.call_connecting) views.callStatusText.setText(R.string.call_connecting)
callConnectingProgress.isVisible = true views.callConnectingProgress.isVisible = true
configureCallInfo(state) configureCallInfo(state)
} }
is CallState.Connected -> { is CallState.Connected -> {
if (callState.iceConnectionState == PeerConnection.PeerConnectionState.CONNECTED) { if (callState.iceConnectionState == PeerConnection.PeerConnectionState.CONNECTED) {
if (callArgs.isVideoCall) { if (callArgs.isVideoCall) {
callVideoGroup.isVisible = true views.callVideoGroup.isVisible = true
callInfoGroup.isVisible = false views.callInfoGroup.isVisible = false
pipRenderer.isVisible = !state.isVideoCaptureInError views.pipRenderer.isVisible = !state.isVideoCaptureInError
} else { } else {
callVideoGroup.isInvisible = true views.callVideoGroup.isInvisible = true
callInfoGroup.isVisible = true views.callInfoGroup.isVisible = true
configureCallInfo(state) configureCallInfo(state)
callStatusText.text = null views.callStatusText.text = null
} }
} else { } else {
// This state is not final, if you change network, new candidates will be sent // This state is not final, if you change network, new candidates will be sent
callVideoGroup.isInvisible = true views.callVideoGroup.isInvisible = true
callInfoGroup.isVisible = true views.callInfoGroup.isVisible = true
configureCallInfo(state) configureCallInfo(state)
callStatusText.setText(R.string.call_connecting) views.callStatusText.setText(R.string.call_connecting)
callConnectingProgress.isVisible = true views.callConnectingProgress.isVisible = true
} }
// ensure all attached? // ensure all attached?
peerConnectionManager.attachViewRenderers(pipRenderer, fullscreenRenderer, null) peerConnectionManager.attachViewRenderers(views.pipRenderer, views.fullscreenRenderer, null)
} }
is CallState.Terminated -> { is CallState.Terminated -> {
finish() finish()
@ -276,14 +278,14 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
private fun configureCallInfo(state: VectorCallViewState) { private fun configureCallInfo(state: VectorCallViewState) {
state.otherUserMatrixItem.invoke()?.let { state.otherUserMatrixItem.invoke()?.let {
avatarRenderer.render(it, otherMemberAvatar) avatarRenderer.render(it, views.otherMemberAvatar)
participantNameText.text = it.getBestName() views.participantNameText.text = it.getBestName()
callTypeText.setText(if (state.isVideoCall) R.string.action_video_call else R.string.action_voice_call) views.callTypeText.setText(if (state.isVideoCall) R.string.action_video_call else R.string.action_voice_call)
} }
} }
private fun configureCallViews() { private fun configureCallViews() {
callControlsView.interactionListener = this views.callControlsView.interactionListener = this
} }
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
@ -303,21 +305,24 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
} }
// Init Picture in Picture renderer // Init Picture in Picture renderer
pipRenderer.init(rootEglBase!!.eglBaseContext, null) views.pipRenderer.init(rootEglBase!!.eglBaseContext, null)
pipRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT) views.pipRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT)
// Init Full Screen renderer // Init Full Screen renderer
fullscreenRenderer.init(rootEglBase!!.eglBaseContext, null) views.fullscreenRenderer.init(rootEglBase!!.eglBaseContext, null)
fullscreenRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT) views.fullscreenRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT)
pipRenderer.setZOrderMediaOverlay(true) views.pipRenderer.setZOrderMediaOverlay(true)
pipRenderer.setEnableHardwareScaler(true /* enabled */) views.pipRenderer.setEnableHardwareScaler(true /* enabled */)
fullscreenRenderer.setEnableHardwareScaler(true /* enabled */) views.fullscreenRenderer.setEnableHardwareScaler(true /* enabled */)
peerConnectionManager.attachViewRenderers(pipRenderer, fullscreenRenderer, peerConnectionManager.attachViewRenderers(
intent.getStringExtra(EXTRA_MODE)?.takeIf { isFirstCreation() }) views.pipRenderer,
views.fullscreenRenderer,
intent.getStringExtra(EXTRA_MODE)?.takeIf { isFirstCreation() }
)
pipRenderer.setOnClickListener { views.pipRenderer.setOnClickListener {
callViewModel.handle(VectorCallViewActions.ToggleCamera) callViewModel.handle(VectorCallViewActions.ToggleCamera)
} }
surfaceRenderersAreInitialized = true surfaceRenderersAreInitialized = true

View file

@ -20,6 +20,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -31,8 +32,9 @@ import com.facebook.react.modules.core.PermissionListener
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ScreenComponent import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import kotlinx.android.parcel.Parcelize import im.vector.app.databinding.ActivityJitsiBinding
import kotlinx.android.synthetic.main.activity_jitsi.* import kotlinx.parcelize.Parcelize
import org.jitsi.meet.sdk.JitsiMeetActivityDelegate import org.jitsi.meet.sdk.JitsiMeetActivityDelegate
import org.jitsi.meet.sdk.JitsiMeetActivityInterface import org.jitsi.meet.sdk.JitsiMeetActivityInterface
import org.jitsi.meet.sdk.JitsiMeetConferenceOptions import org.jitsi.meet.sdk.JitsiMeetConferenceOptions
@ -42,7 +44,7 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
import java.net.URL import java.net.URL
import javax.inject.Inject import javax.inject.Inject
class VectorJitsiActivity : VectorBaseActivity(), JitsiMeetActivityInterface, JitsiMeetViewListener { class VectorJitsiActivity : VectorBaseActivity<ActivityJitsiBinding>(), JitsiMeetActivityInterface, JitsiMeetViewListener {
@Parcelize @Parcelize
data class Args( data class Args(
@ -51,7 +53,7 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMeetActivityInterface, Ji
val enableVideo: Boolean val enableVideo: Boolean
) : Parcelable ) : Parcelable
override fun getLayoutRes() = R.layout.activity_jitsi override fun getBinding() = ActivityJitsiBinding.inflate(layoutInflater)
@Inject lateinit var viewModelFactory: JitsiCallViewModel.Factory @Inject lateinit var viewModelFactory: JitsiCallViewModel.Factory
@ -76,7 +78,7 @@ class VectorJitsiActivity : VectorBaseActivity(), JitsiMeetActivityInterface, Ji
super.initUiAndData() super.initUiAndData()
jitsiMeetView = JitsiMeetView(this) jitsiMeetView = JitsiMeetView(this)
val params = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT) val params = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
jitsi_layout.addView(jitsiMeetView, params) views.jitsiLayout.addView(jitsiMeetView, params)
jitsiMeetView?.listener = this jitsiMeetView?.listener = this
} }

View file

@ -17,7 +17,9 @@
package im.vector.app.features.contactsbook package im.vector.app.features.contactsbook
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.activityViewModel
@ -29,12 +31,14 @@ import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentAttachmentsPreviewBinding
import im.vector.app.databinding.FragmentContactsBookBinding
import im.vector.app.features.userdirectory.PendingInvitee import im.vector.app.features.userdirectory.PendingInvitee
import im.vector.app.features.userdirectory.UserListAction import im.vector.app.features.userdirectory.UserListAction
import im.vector.app.features.userdirectory.UserListSharedAction import im.vector.app.features.userdirectory.UserListSharedAction
import im.vector.app.features.userdirectory.UserListSharedActionViewModel import im.vector.app.features.userdirectory.UserListSharedActionViewModel
import im.vector.app.features.userdirectory.UserListViewModel import im.vector.app.features.userdirectory.UserListViewModel
import kotlinx.android.synthetic.main.fragment_contacts_book.*
import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.session.user.model.User
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -43,9 +47,12 @@ import javax.inject.Inject
class ContactsBookFragment @Inject constructor( class ContactsBookFragment @Inject constructor(
val contactsBookViewModelFactory: ContactsBookViewModel.Factory, val contactsBookViewModelFactory: ContactsBookViewModel.Factory,
private val contactsBookController: ContactsBookController private val contactsBookController: ContactsBookController
) : VectorBaseFragment(), ContactsBookController.Callback { ) : VectorBaseFragment<FragmentContactsBookBinding>(), ContactsBookController.Callback {
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentContactsBookBinding {
return FragmentContactsBookBinding.inflate(inflater, container, false)
}
override fun getLayoutResId() = R.layout.fragment_contacts_book
private val viewModel: UserListViewModel by activityViewModel() private val viewModel: UserListViewModel by activityViewModel()
// Use activityViewModel to avoid loading several times the data // Use activityViewModel to avoid loading several times the data
@ -64,7 +71,7 @@ class ContactsBookFragment @Inject constructor(
} }
private fun setupConsentView() { private fun setupConsentView() {
phoneBookSearchForMatrixContacts.setOnClickListener { views.phoneBookSearchForMatrixContacts.setOnClickListener {
withState(contactsBookViewModel) { state -> withState(contactsBookViewModel) { state ->
AlertDialog.Builder(requireActivity()) AlertDialog.Builder(requireActivity())
.setTitle(R.string.identity_server_consent_dialog_title) .setTitle(R.string.identity_server_consent_dialog_title)
@ -79,7 +86,7 @@ class ContactsBookFragment @Inject constructor(
} }
private fun setupOnlyBoundContactsView() { private fun setupOnlyBoundContactsView() {
phoneBookOnlyBoundContacts.checkedChanges() views.phoneBookOnlyBoundContacts.checkedChanges()
.subscribe { .subscribe {
contactsBookViewModel.handle(ContactsBookAction.OnlyBoundContacts(it)) contactsBookViewModel.handle(ContactsBookAction.OnlyBoundContacts(it))
} }
@ -87,7 +94,7 @@ class ContactsBookFragment @Inject constructor(
} }
private fun setupFilterView() { private fun setupFilterView() {
phoneBookFilter views.phoneBookFilter
.textChanges() .textChanges()
.skipInitialValue() .skipInitialValue()
.debounce(300, TimeUnit.MILLISECONDS) .debounce(300, TimeUnit.MILLISECONDS)
@ -98,25 +105,25 @@ class ContactsBookFragment @Inject constructor(
} }
override fun onDestroyView() { override fun onDestroyView() {
phoneBookRecyclerView.cleanup() views.phoneBookRecyclerView.cleanup()
contactsBookController.callback = null contactsBookController.callback = null
super.onDestroyView() super.onDestroyView()
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {
contactsBookController.callback = this contactsBookController.callback = this
phoneBookRecyclerView.configureWith(contactsBookController) views.phoneBookRecyclerView.configureWith(contactsBookController)
} }
private fun setupCloseView() { private fun setupCloseView() {
phoneBookClose.debouncedClicks { views.phoneBookClose.debouncedClicks {
sharedActionViewModel.post(UserListSharedAction.GoBack) sharedActionViewModel.post(UserListSharedAction.GoBack)
} }
} }
override fun invalidate() = withState(contactsBookViewModel) { state -> override fun invalidate() = withState(contactsBookViewModel) { state ->
phoneBookSearchForMatrixContacts.isVisible = state.filteredMappedContacts.isNotEmpty() && state.identityServerUrl != null && !state.userConsent views.phoneBookSearchForMatrixContacts.isVisible = state.filteredMappedContacts.isNotEmpty() && state.identityServerUrl != null && !state.userConsent
phoneBookOnlyBoundContacts.isVisible = state.isBoundRetrieved views.phoneBookOnlyBoundContacts.isVisible = state.isBoundRetrieved
contactsBookController.setData(state) contactsBookController.setData(state)
} }

View file

@ -51,7 +51,7 @@ import im.vector.app.features.userdirectory.UserListSharedAction
import im.vector.app.features.userdirectory.UserListSharedActionViewModel import im.vector.app.features.userdirectory.UserListSharedActionViewModel
import im.vector.app.features.userdirectory.UserListViewModel import im.vector.app.features.userdirectory.UserListViewModel
import im.vector.app.features.userdirectory.UserListViewState import im.vector.app.features.userdirectory.UserListViewState
import kotlinx.android.synthetic.main.activity.*
import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure import org.matrix.android.sdk.api.session.room.failure.CreateRoomFailure
import java.net.HttpURLConnection import java.net.HttpURLConnection

View file

@ -16,6 +16,8 @@
package im.vector.app.features.createdirect package im.vector.app.features.createdirect
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.Toast import android.widget.Toast
import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.activityViewModel
import com.google.zxing.Result import com.google.zxing.Result
@ -26,19 +28,23 @@ import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.databinding.FragmentAttachmentsPreviewBinding
import im.vector.app.databinding.FragmentQrCodeScannerBinding
import im.vector.app.features.userdirectory.PendingInvitee import im.vector.app.features.userdirectory.PendingInvitee
import kotlinx.android.synthetic.main.fragment_qr_code_scanner.*
import me.dm7.barcodescanner.zxing.ZXingScannerView import me.dm7.barcodescanner.zxing.ZXingScannerView
import org.matrix.android.sdk.api.session.permalinks.PermalinkData import org.matrix.android.sdk.api.session.permalinks.PermalinkData
import org.matrix.android.sdk.api.session.permalinks.PermalinkParser import org.matrix.android.sdk.api.session.permalinks.PermalinkParser
import org.matrix.android.sdk.api.session.user.model.User import org.matrix.android.sdk.api.session.user.model.User
import javax.inject.Inject import javax.inject.Inject
class CreateDirectRoomByQrCodeFragment @Inject constructor() : VectorBaseFragment(), ZXingScannerView.ResultHandler { class CreateDirectRoomByQrCodeFragment @Inject constructor() : VectorBaseFragment<FragmentQrCodeScannerBinding>(), ZXingScannerView.ResultHandler {
private val viewModel: CreateDirectRoomViewModel by activityViewModel() private val viewModel: CreateDirectRoomViewModel by activityViewModel()
override fun getLayoutResId() = R.layout.fragment_qr_code_scanner override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentQrCodeScannerBinding {
return FragmentQrCodeScannerBinding.inflate(inflater, container, false)
}
private val openCameraActivityResultLauncher = registerForPermissionsResult { allGranted -> private val openCameraActivityResultLauncher = registerForPermissionsResult { allGranted ->
if (allGranted) { if (allGranted) {
@ -48,14 +54,14 @@ class CreateDirectRoomByQrCodeFragment @Inject constructor() : VectorBaseFragmen
private fun startCamera() { private fun startCamera() {
// Start camera on resume // Start camera on resume
scannerView.startCamera() views.scannerView.startCamera()
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
view?.hideKeyboard() view?.hideKeyboard()
// Register ourselves as a handler for scan results. // Register ourselves as a handler for scan results.
scannerView.setResultHandler(this) views.scannerView.setResultHandler(this)
// Start camera on resume // Start camera on resume
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), openCameraActivityResultLauncher)) { if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), openCameraActivityResultLauncher)) {
startCamera() startCamera()
@ -65,9 +71,9 @@ class CreateDirectRoomByQrCodeFragment @Inject constructor() : VectorBaseFragmen
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
// Unregister ourselves as a handler for scan results. // Unregister ourselves as a handler for scan results.
scannerView.setResultHandler(null) views.scannerView.setResultHandler(null)
// Stop camera on pause // Stop camera on pause
scannerView.stopCamera() views.scannerView.stopCamera()
} }
// Copied from https://github.com/markusfisch/BinaryEye/blob/ // Copied from https://github.com/markusfisch/BinaryEye/blob/

View file

@ -17,7 +17,9 @@ package im.vector.app.features.crypto.keysbackup.restore
import android.app.Activity import android.app.Activity
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
@ -25,14 +27,18 @@ import im.vector.app.R
import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.startImportTextFromFileIntent import im.vector.app.core.utils.startImportTextFromFileIntent
import kotlinx.android.synthetic.main.fragment_keys_backup_restore_from_key.* import im.vector.app.databinding.FragmentAttachmentsPreviewBinding
import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import javax.inject.Inject import javax.inject.Inject
class KeysBackupRestoreFromKeyFragment @Inject constructor() class KeysBackupRestoreFromKeyFragment @Inject constructor()
: VectorBaseFragment() { : VectorBaseFragment<FragmentKeysBackupRestoreFromKeyBinding>() {
override fun getLayoutResId() = R.layout.fragment_keys_backup_restore_from_key override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentKeysBackupRestoreFromKeyBinding {
return FragmentKeysBackupRestoreFromKeyBinding.inflate(inflater, container, false)
}
private lateinit var viewModel: KeysBackupRestoreFromKeyViewModel private lateinit var viewModel: KeysBackupRestoreFromKeyViewModel
private lateinit var sharedViewModel: KeysBackupRestoreSharedViewModel private lateinit var sharedViewModel: KeysBackupRestoreSharedViewModel
@ -41,8 +47,8 @@ class KeysBackupRestoreFromKeyFragment @Inject constructor()
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
viewModel = fragmentViewModelProvider.get(KeysBackupRestoreFromKeyViewModel::class.java) viewModel = fragmentViewModelProvider.get(KeysBackupRestoreFromKeyViewModel::class.java)
sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java) sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java)
mKeyTextEdit.setText(viewModel.recoveryCode.value) views.mKeyTextEdit.setText(viewModel.recoveryCode.value)
mKeyTextEdit.setOnEditorActionListener { _, actionId, _ -> views.mKeyTextEdit.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) { if (actionId == EditorInfo.IME_ACTION_DONE) {
onRestoreFromKey() onRestoreFromKey()
return@setOnEditorActionListener true return@setOnEditorActionListener true
@ -50,14 +56,14 @@ class KeysBackupRestoreFromKeyFragment @Inject constructor()
return@setOnEditorActionListener false return@setOnEditorActionListener false
} }
mKeyInputLayout.error = viewModel.recoveryCodeErrorText.value views.mKeyInputLayout.error = viewModel.recoveryCodeErrorText.value
viewModel.recoveryCodeErrorText.observe(viewLifecycleOwner, Observer { newValue -> viewModel.recoveryCodeErrorText.observe(viewLifecycleOwner, Observer { newValue ->
mKeyInputLayout.error = newValue views.mKeyInputLayout.error = newValue
}) })
keys_restore_button.setOnClickListener { onRestoreFromKey() } views.keysRestoreButton.setOnClickListener { onRestoreFromKey() }
keys_backup_import.setOnClickListener { onImport() } views.keysBackupImport.setOnClickListener { onImport() }
mKeyTextEdit.doOnTextChanged { text, _, _, _ -> onRestoreKeyTextEditChange(text) } views.mKeyTextEdit.doOnTextChanged { text, _, _, _ -> onRestoreKeyTextEditChange(text) }
} }
private fun onRestoreKeyTextEditChange(s: CharSequence?) { private fun onRestoreKeyTextEditChange(s: CharSequence?) {
@ -89,8 +95,8 @@ class KeysBackupRestoreFromKeyFragment @Inject constructor()
?.bufferedReader() ?.bufferedReader()
?.use { it.readText() } ?.use { it.readText() }
?.let { ?.let {
mKeyTextEdit.setText(it) views.mKeyTextEdit.setText(it)
mKeyTextEdit.setSelection(it.length) views.mKeyTextEdit.setSelection(it.length)
} }
} }
} }

View file

@ -18,7 +18,9 @@ package im.vector.app.features.crypto.keysbackup.restore
import android.os.Bundle import android.os.Bundle
import android.text.SpannableString import android.text.SpannableString
import android.text.style.ClickableSpan import android.text.style.ClickableSpan
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import androidx.core.text.set import androidx.core.text.set
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
@ -26,12 +28,16 @@ import androidx.lifecycle.Observer
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.showPassword import im.vector.app.core.extensions.showPassword
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import kotlinx.android.synthetic.main.fragment_keys_backup_restore_from_passphrase.* import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import im.vector.app.databinding.FragmentKeysBackupRestoreFromPassphraseBinding
import javax.inject.Inject import javax.inject.Inject
class KeysBackupRestoreFromPassphraseFragment @Inject constructor() : VectorBaseFragment() { class KeysBackupRestoreFromPassphraseFragment @Inject constructor() : VectorBaseFragment<FragmentKeysBackupRestoreFromPassphraseBinding>() {
override fun getLayoutResId() = R.layout.fragment_keys_backup_restore_from_passphrase override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentKeysBackupRestoreFromPassphraseBinding {
return FragmentKeysBackupRestoreFromPassphraseBinding.inflate(inflater, container, false)
}
private lateinit var viewModel: KeysBackupRestoreFromPassphraseViewModel private lateinit var viewModel: KeysBackupRestoreFromPassphraseViewModel
private lateinit var sharedViewModel: KeysBackupRestoreSharedViewModel private lateinit var sharedViewModel: KeysBackupRestoreSharedViewModel
@ -47,18 +53,18 @@ class KeysBackupRestoreFromPassphraseFragment @Inject constructor() : VectorBase
sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java) sharedViewModel = activityViewModelProvider.get(KeysBackupRestoreSharedViewModel::class.java)
viewModel.passphraseErrorText.observe(viewLifecycleOwner, Observer { newValue -> viewModel.passphraseErrorText.observe(viewLifecycleOwner, Observer { newValue ->
keys_backup_passphrase_enter_til.error = newValue views.keysBackupPassphraseEnterTil.error = newValue
}) })
helperTextWithLink.text = spannableStringForHelperText() views.helperTextWithLink.text = spannableStringForHelperText()
viewModel.showPasswordMode.observe(viewLifecycleOwner, Observer { viewModel.showPasswordMode.observe(viewLifecycleOwner, Observer {
val shouldBeVisible = it ?: false val shouldBeVisible = it ?: false
keys_backup_passphrase_enter_edittext.showPassword(shouldBeVisible) views.keysBackupPassphraseEnterEdittext.showPassword(shouldBeVisible)
keys_backup_view_show_password.setImageResource(if (shouldBeVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye) views.keysBackupViewShowPassword.setImageResource(if (shouldBeVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
}) })
keys_backup_passphrase_enter_edittext.setOnEditorActionListener { _, actionId, _ -> views.keysBackupPassphraseEnterEdittext.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) { if (actionId == EditorInfo.IME_ACTION_DONE) {
onRestoreBackup() onRestoreBackup()
return@setOnEditorActionListener true return@setOnEditorActionListener true
@ -66,10 +72,10 @@ class KeysBackupRestoreFromPassphraseFragment @Inject constructor() : VectorBase
return@setOnEditorActionListener false return@setOnEditorActionListener false
} }
keys_backup_view_show_password.setOnClickListener { toggleVisibilityMode() } views.keysBackupViewShowPassword.setOnClickListener { toggleVisibilityMode() }
helperTextWithLink.setOnClickListener { onUseRecoveryKey() } views.helperTextWithLink.setOnClickListener { onUseRecoveryKey() }
keys_backup_restore_with_passphrase_submit.setOnClickListener { onRestoreBackup() } views.keysBackupRestoreWithPassphraseSubmit.setOnClickListener { onRestoreBackup() }
keys_backup_passphrase_enter_edittext.doOnTextChanged { text, _, _, _ -> onPassphraseTextEditChange(text) } views.keysBackupPassphraseEnterEdittext.doOnTextChanged { text, _, _, _ -> onPassphraseTextEditChange(text) }
} }
private fun spannableStringForHelperText(): SpannableString { private fun spannableStringForHelperText(): SpannableString {

View file

@ -16,17 +16,23 @@
package im.vector.app.features.crypto.keysbackup.restore package im.vector.app.features.crypto.keysbackup.restore
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.LiveEvent import im.vector.app.core.utils.LiveEvent
import kotlinx.android.synthetic.main.fragment_keys_backup_restore_success.* import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import im.vector.app.databinding.FragmentKeysBackupRestoreSuccessBinding
import javax.inject.Inject import javax.inject.Inject
class KeysBackupRestoreSuccessFragment @Inject constructor() : VectorBaseFragment() { class KeysBackupRestoreSuccessFragment @Inject constructor() : VectorBaseFragment<FragmentKeysBackupRestoreSuccessBinding>() {
override fun getLayoutResId() = R.layout.fragment_keys_backup_restore_success override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentKeysBackupRestoreSuccessBinding {
return FragmentKeysBackupRestoreSuccessBinding.inflate(inflater, container, false)
}
private lateinit var sharedViewModel: KeysBackupRestoreSharedViewModel private lateinit var sharedViewModel: KeysBackupRestoreSharedViewModel
@ -40,15 +46,15 @@ class KeysBackupRestoreSuccessFragment @Inject constructor() : VectorBaseFragmen
it.totalNumberOfKeys, it.totalNumberOfKeys) it.totalNumberOfKeys, it.totalNumberOfKeys)
val part2 = resources.getQuantityString(R.plurals.keys_backup_restore_success_description_part2, val part2 = resources.getQuantityString(R.plurals.keys_backup_restore_success_description_part2,
it.successfullyNumberOfImportedKeys, it.successfullyNumberOfImportedKeys) it.successfullyNumberOfImportedKeys, it.successfullyNumberOfImportedKeys)
mSuccessDetailsText.text = String.format("%s\n%s", part1, part2) views.mSuccessDetailsText.text = String.format("%s\n%s", part1, part2)
} }
// We don't put emoji in string xml as it will crash on old devices // We don't put emoji in string xml as it will crash on old devices
mSuccessText.text = context?.getString(R.string.keys_backup_restore_success_title, "🎉") views.mSuccessText.text = context?.getString(R.string.keys_backup_restore_success_title, "🎉")
} else { } else {
mSuccessText.text = context?.getString(R.string.keys_backup_restore_success_title_already_up_to_date) views.mSuccessText.text = context?.getString(R.string.keys_backup_restore_success_title_already_up_to_date)
mSuccessDetailsText.isVisible = false views.mSuccessDetailsText.isVisible = false
} }
keys_backup_setup_done_button.setOnClickListener { onDone() } views.keysBackupSetupDoneButton.setOnClickListener { onDone() }
} }
private fun onDone() { private fun onDone() {

View file

@ -16,7 +16,9 @@
package im.vector.app.features.crypto.keysbackup.settings package im.vector.app.features.crypto.keysbackup.settings
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
@ -24,28 +26,32 @@ import im.vector.app.R
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import im.vector.app.databinding.FragmentKeysBackupSettingsBinding
import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity
import im.vector.app.features.crypto.keysbackup.setup.KeysBackupSetupActivity import im.vector.app.features.crypto.keysbackup.setup.KeysBackupSetupActivity
import kotlinx.android.synthetic.main.fragment_keys_backup_settings.*
import javax.inject.Inject import javax.inject.Inject
class KeysBackupSettingsFragment @Inject constructor(private val keysBackupSettingsRecyclerViewController: KeysBackupSettingsRecyclerViewController) class KeysBackupSettingsFragment @Inject constructor(private val keysBackupSettingsRecyclerViewController: KeysBackupSettingsRecyclerViewController)
: VectorBaseFragment(), : VectorBaseFragment<FragmentKeysBackupSettingsBinding>(),
KeysBackupSettingsRecyclerViewController.Listener { KeysBackupSettingsRecyclerViewController.Listener {
override fun getLayoutResId() = R.layout.fragment_keys_backup_settings override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentKeysBackupSettingsBinding {
return FragmentKeysBackupSettingsBinding.inflate(inflater, container, false)
}
private val viewModel: KeysBackupSettingsViewModel by activityViewModel() private val viewModel: KeysBackupSettingsViewModel by activityViewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
keysBackupSettingsRecyclerView.configureWith(keysBackupSettingsRecyclerViewController) views.keysBackupSettingsRecyclerView.configureWith(keysBackupSettingsRecyclerViewController)
keysBackupSettingsRecyclerViewController.listener = this keysBackupSettingsRecyclerViewController.listener = this
} }
override fun onDestroyView() { override fun onDestroyView() {
keysBackupSettingsRecyclerViewController.listener = null keysBackupSettingsRecyclerViewController.listener = null
keysBackupSettingsRecyclerView.cleanup() views.keysBackupSettingsRecyclerView.cleanup()
super.onDestroyView() super.onDestroyView()
} }

View file

@ -17,17 +17,23 @@
package im.vector.app.features.crypto.keysbackup.setup package im.vector.app.features.crypto.keysbackup.setup
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.LiveEvent import im.vector.app.core.utils.LiveEvent
import kotlinx.android.synthetic.main.fragment_keys_backup_setup_step1.* import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import im.vector.app.databinding.FragmentKeysBackupSetupStep1Binding
import javax.inject.Inject import javax.inject.Inject
class KeysBackupSetupStep1Fragment @Inject constructor() : VectorBaseFragment() { class KeysBackupSetupStep1Fragment @Inject constructor() : VectorBaseFragment<FragmentKeysBackupSetupStep1Binding>() {
override fun getLayoutResId() = R.layout.fragment_keys_backup_setup_step1 override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentKeysBackupSetupStep1Binding {
return FragmentKeysBackupSetupStep1Binding.inflate(inflater, container, false)
}
private lateinit var viewModel: KeysBackupSetupSharedViewModel private lateinit var viewModel: KeysBackupSetupSharedViewModel
@ -39,12 +45,12 @@ class KeysBackupSetupStep1Fragment @Inject constructor() : VectorBaseFragment()
viewModel.showManualExport.observe(viewLifecycleOwner, Observer { viewModel.showManualExport.observe(viewLifecycleOwner, Observer {
val showOption = it ?: false val showOption = it ?: false
// Can't use isVisible because the kotlin compiler will crash with Back-end (JVM) Internal error: wrong code generated // Can't use isVisible because the kotlin compiler will crash with Back-end (JVM) Internal error: wrong code generated
advancedOptionText.visibility = if (showOption) View.VISIBLE else View.GONE views.advancedOptionText.visibility = if (showOption) View.VISIBLE else View.GONE
manualExportButton.visibility = if (showOption) View.VISIBLE else View.GONE views.manualExportButton.visibility = if (showOption) View.VISIBLE else View.GONE
}) })
keys_backup_setup_step1_button.setOnClickListener { onButtonClick() } views.keysBackupSetupStep1Button.setOnClickListener { onButtonClick() }
manualExportButton.setOnClickListener { onManualExportClick() } views.manualExportButton.setOnClickListener { onManualExportClick() }
} }
private fun onButtonClick() { private fun onButtonClick() {

View file

@ -16,7 +16,9 @@
package im.vector.app.features.crypto.keysbackup.setup package im.vector.app.features.crypto.keysbackup.setup
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import androidx.core.widget.doOnTextChanged import androidx.core.widget.doOnTextChanged
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
@ -26,15 +28,19 @@ import com.nulabinc.zxcvbn.Zxcvbn
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.showPassword import im.vector.app.core.extensions.showPassword
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import im.vector.app.databinding.FragmentKeysBackupSetupStep2Binding
import im.vector.app.features.settings.VectorLocale import im.vector.app.features.settings.VectorLocale
import kotlinx.android.synthetic.main.fragment_keys_backup_setup_step2.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
class KeysBackupSetupStep2Fragment @Inject constructor() : VectorBaseFragment() { class KeysBackupSetupStep2Fragment @Inject constructor() : VectorBaseFragment<FragmentKeysBackupSetupStep2Binding>() {
override fun getLayoutResId() = R.layout.fragment_keys_backup_setup_step2 override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentKeysBackupSetupStep2Binding {
return FragmentKeysBackupSetupStep2Binding.inflate(inflater, container, false)
}
private val zxcvbn = Zxcvbn() private val zxcvbn = Zxcvbn()

View file

@ -18,7 +18,9 @@ package im.vector.app.features.crypto.keysbackup.setup
import android.app.Activity import android.app.Activity
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
@ -33,7 +35,9 @@ import im.vector.app.core.utils.LiveEvent
import im.vector.app.core.utils.copyToClipboard import im.vector.app.core.utils.copyToClipboard
import im.vector.app.core.utils.selectTxtFileToWrite import im.vector.app.core.utils.selectTxtFileToWrite
import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.core.utils.startSharePlainTextIntent
import kotlinx.android.synthetic.main.fragment_keys_backup_setup_step3.* import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import im.vector.app.databinding.FragmentKeysBackupSetupStep3Binding
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -44,9 +48,11 @@ import java.util.Date
import java.util.Locale import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment() { class KeysBackupSetupStep3Fragment @Inject constructor() : VectorBaseFragment<FragmentKeysBackupSetupStep3Binding>() {
override fun getLayoutResId() = R.layout.fragment_keys_backup_setup_step3 override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentKeysBackupSetupStep3Binding {
return FragmentKeysBackupSetupStep3Binding.inflate(inflater, container, false)
}
private lateinit var viewModel: KeysBackupSetupSharedViewModel private lateinit var viewModel: KeysBackupSetupSharedViewModel

View file

@ -35,8 +35,8 @@ import im.vector.app.core.extensions.commitTransaction
import im.vector.app.core.platform.SimpleFragmentActivity import im.vector.app.core.platform.SimpleFragmentActivity
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.crypto.recover.SetupMode
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.android.synthetic.main.activity.*
import javax.inject.Inject import javax.inject.Inject
import kotlin.reflect.KClass import kotlin.reflect.KClass

View file

@ -18,7 +18,9 @@ package im.vector.app.features.crypto.quads
import android.app.Activity import android.app.Activity
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.activityViewModel
import com.jakewharton.rxbinding3.widget.editorActionEvents import com.jakewharton.rxbinding3.widget.editorActionEvents
@ -27,15 +29,19 @@ import im.vector.app.R
import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.startImportTextFromFileIntent import im.vector.app.core.utils.startImportTextFromFileIntent
import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import im.vector.app.databinding.FragmentSsssAccessFromKeyBinding
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.synthetic.main.fragment_ssss_access_from_key.*
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
class SharedSecuredStorageKeyFragment @Inject constructor() : VectorBaseFragment() { class SharedSecuredStorageKeyFragment @Inject constructor() : VectorBaseFragment<FragmentSsssAccessFromKeyBinding>() {
override fun getLayoutResId() = R.layout.fragment_ssss_access_from_key override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSsssAccessFromKeyBinding {
return FragmentSsssAccessFromKeyBinding.inflate(inflater, container, false)
}
val sharedViewModel: SharedSecureStorageViewModel by activityViewModel() val sharedViewModel: SharedSecureStorageViewModel by activityViewModel()

View file

@ -17,7 +17,9 @@
package im.vector.app.features.crypto.quads package im.vector.app.features.crypto.quads
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import androidx.core.text.toSpannable import androidx.core.text.toSpannable
import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.activityViewModel
@ -29,16 +31,20 @@ import im.vector.app.core.extensions.showPassword
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.utils.colorizeMatchingText import im.vector.app.core.utils.colorizeMatchingText
import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import im.vector.app.databinding.FragmentSsssAccessFromPassphraseBinding
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.synthetic.main.fragment_ssss_access_from_passphrase.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
class SharedSecuredStoragePassphraseFragment @Inject constructor( class SharedSecuredStoragePassphraseFragment @Inject constructor(
private val colorProvider: ColorProvider private val colorProvider: ColorProvider
) : VectorBaseFragment() { ) : VectorBaseFragment<FragmentSsssAccessFromPassphraseBinding>() {
override fun getLayoutResId() = R.layout.fragment_ssss_access_from_passphrase override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSsssAccessFromPassphraseBinding {
return FragmentSsssAccessFromPassphraseBinding.inflate(inflater, container, false)
}
val sharedViewModel: SharedSecureStorageViewModel by activityViewModel() val sharedViewModel: SharedSecureStorageViewModel by activityViewModel()
@ -48,7 +54,7 @@ class SharedSecuredStoragePassphraseFragment @Inject constructor(
// If has passphrase // If has passphrase
val pass = getString(R.string.recovery_passphrase) val pass = getString(R.string.recovery_passphrase)
val key = getString(R.string.recovery_key) val key = getString(R.string.recovery_key)
ssss_restore_with_passphrase_warning_text.text = getString( views.ssssRestoreWithPassphraseWarningText.text = getString(
R.string.enter_secret_storage_passphrase_or_key, R.string.enter_secret_storage_passphrase_or_key,
pass, pass,
key key
@ -57,7 +63,7 @@ class SharedSecuredStoragePassphraseFragment @Inject constructor(
.colorizeMatchingText(pass, colorProvider.getColorFromAttribute(android.R.attr.textColorLink)) .colorizeMatchingText(pass, colorProvider.getColorFromAttribute(android.R.attr.textColorLink))
.colorizeMatchingText(key, colorProvider.getColorFromAttribute(android.R.attr.textColorLink)) .colorizeMatchingText(key, colorProvider.getColorFromAttribute(android.R.attr.textColorLink))
ssss_passphrase_enter_edittext.editorActionEvents() views.ssssPassphraseEnterEdittext.editorActionEvents()
.throttleFirst(300, TimeUnit.MILLISECONDS) .throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe { .subscribe {
@ -67,40 +73,40 @@ class SharedSecuredStoragePassphraseFragment @Inject constructor(
} }
.disposeOnDestroyView() .disposeOnDestroyView()
ssss_passphrase_enter_edittext.textChanges() views.ssssPassphraseEnterEdittext.textChanges()
.subscribe { .subscribe {
ssss_passphrase_enter_til.error = null views.ssssPassphraseEnterTil.error = null
ssss_passphrase_submit.isEnabled = it.isNotBlank() views.ssssPassphraseSubmit.isEnabled = it.isNotBlank()
} }
.disposeOnDestroyView() .disposeOnDestroyView()
ssss_passphrase_reset.clickableView.debouncedClicks { views.ssssPassphraseReset.clickableView.debouncedClicks {
sharedViewModel.handle(SharedSecureStorageAction.ForgotResetAll) sharedViewModel.handle(SharedSecureStorageAction.ForgotResetAll)
} }
sharedViewModel.observeViewEvents { sharedViewModel.observeViewEvents {
when (it) { when (it) {
is SharedSecureStorageViewEvent.InlineError -> { is SharedSecureStorageViewEvent.InlineError -> {
ssss_passphrase_enter_til.error = it.message views.ssssPassphraseEnterTil.error = it.message
} }
} }
} }
ssss_passphrase_submit.debouncedClicks { submit() } views.ssssPassphraseSubmit.debouncedClicks { submit() }
ssss_passphrase_use_key.debouncedClicks { sharedViewModel.handle(SharedSecureStorageAction.UseKey) } views.ssssPassphraseUseKey.debouncedClicks { sharedViewModel.handle(SharedSecureStorageAction.UseKey) }
ssss_view_show_password.debouncedClicks { sharedViewModel.handle(SharedSecureStorageAction.TogglePasswordVisibility) } views.ssssViewShowPassword.debouncedClicks { sharedViewModel.handle(SharedSecureStorageAction.TogglePasswordVisibility) }
} }
fun submit() { fun submit() {
val text = ssss_passphrase_enter_edittext.text.toString() val text = views.ssssPassphraseEnterEdittext.text.toString()
if (text.isBlank()) return // Should not reach this point as button disabled if (text.isBlank()) return // Should not reach this point as button disabled
ssss_passphrase_submit.isEnabled = false views.ssssPassphraseSubmit.isEnabled = false
sharedViewModel.handle(SharedSecureStorageAction.SubmitPassphrase(text)) sharedViewModel.handle(SharedSecureStorageAction.SubmitPassphrase(text))
} }
override fun invalidate() = withState(sharedViewModel) { state -> override fun invalidate() = withState(sharedViewModel) { state ->
val shouldBeVisible = state.passphraseVisible val shouldBeVisible = state.passphraseVisible
ssss_passphrase_enter_edittext.showPassword(shouldBeVisible) views.ssssPassphraseEnterEdittext.showPassword(shouldBeVisible)
ssss_view_show_password.setImageResource(if (shouldBeVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye) views.ssssViewShowPassword.setImageResource(if (shouldBeVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
} }
} }

View file

@ -17,41 +17,48 @@
package im.vector.app.features.crypto.quads package im.vector.app.features.crypto.quads
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.activityViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import im.vector.app.databinding.FragmentSsssResetAllBinding
import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet
import kotlinx.android.synthetic.main.fragment_ssss_reset_all.*
import javax.inject.Inject import javax.inject.Inject
class SharedSecuredStorageResetAllFragment @Inject constructor() : VectorBaseFragment() { class SharedSecuredStorageResetAllFragment @Inject constructor()
: VectorBaseFragment<FragmentSsssResetAllBinding>() {
override fun getLayoutResId() = R.layout.fragment_ssss_reset_all override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSsssResetAllBinding {
return FragmentSsssResetAllBinding.inflate(inflater, container, false)
}
val sharedViewModel: SharedSecureStorageViewModel by activityViewModel() val sharedViewModel: SharedSecureStorageViewModel by activityViewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
ssss_reset_button_reset.debouncedClicks { views.ssssResetButtonReset.debouncedClicks {
sharedViewModel.handle(SharedSecureStorageAction.DoResetAll) sharedViewModel.handle(SharedSecureStorageAction.DoResetAll)
} }
ssss_reset_button_cancel.debouncedClicks { views.ssssResetButtonCancel.debouncedClicks {
sharedViewModel.handle(SharedSecureStorageAction.Back) sharedViewModel.handle(SharedSecureStorageAction.Back)
} }
ssss_reset_other_devices.debouncedClicks { views.ssssResetOtherDevices.debouncedClicks {
withState(sharedViewModel) { withState(sharedViewModel) {
DeviceListBottomSheet.newInstance(it.userId, false).show(childFragmentManager, "DEV_LIST") DeviceListBottomSheet.newInstance(it.userId, false).show(childFragmentManager, "DEV_LIST")
} }
} }
sharedViewModel.subscribe(this) { state -> sharedViewModel.subscribe(this) { state ->
ssss_reset_other_devices.setTextOrHide( views.ssssResetOtherDevices.setTextOrHide(
state.activeDeviceCount state.activeDeviceCount
.takeIf { it > 0 } .takeIf { it > 0 }
?.let { resources.getQuantityString(R.plurals.secure_backup_reset_devices_you_can_verify, it, it) } ?.let { resources.getQuantityString(R.plurals.secure_backup_reset_devices_you_can_verify, it, it) }

View file

@ -17,7 +17,9 @@
package im.vector.app.features.crypto.recover package im.vector.app.features.crypto.recover
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import androidx.core.text.toSpannable import androidx.core.text.toSpannable
import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.parentFragmentViewModel
@ -30,18 +32,22 @@ import im.vector.app.core.extensions.showPassword
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.utils.colorizeMatchingText import im.vector.app.core.utils.colorizeMatchingText
import im.vector.app.databinding.FragmentBootstrapEnterAccountPasswordBinding
import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.synthetic.main.fragment_bootstrap_enter_account_password.*
import kotlinx.android.synthetic.main.fragment_bootstrap_enter_passphrase.bootstrapDescriptionText
import kotlinx.android.synthetic.main.fragment_bootstrap_enter_passphrase.ssss_view_show_password
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
class BootstrapAccountPasswordFragment @Inject constructor( class BootstrapAccountPasswordFragment @Inject constructor(
private val colorProvider: ColorProvider private val colorProvider: ColorProvider
) : VectorBaseFragment() { ) : VectorBaseFragment<FragmentBootstrapEnterAccountPasswordBinding>() {
override fun getLayoutResId() = R.layout.fragment_bootstrap_enter_account_password override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBootstrapEnterAccountPasswordBinding {
return FragmentBootstrapEnterAccountPasswordBinding.inflate(inflater, container, false)
}
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel() val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()

View file

@ -36,12 +36,14 @@ import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.commitTransaction
import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import kotlinx.android.parcel.Parcelize import im.vector.app.databinding.BottomSheetBootstrapBinding
import kotlinx.android.synthetic.main.bottom_sheet_bootstrap.* import im.vector.app.databinding.BottomSheetGenericListBinding
import kotlinx.parcelize.Parcelize
import javax.inject.Inject import javax.inject.Inject
import kotlin.reflect.KClass import kotlin.reflect.KClass
class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment() { class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetBootstrapBinding>() {
@Parcelize @Parcelize
data class Args( data class Args(
@ -59,7 +61,9 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment() {
injector.inject(this) injector.inject(this)
} }
override fun getLayoutResId() = R.layout.bottom_sheet_bootstrap override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetBootstrapBinding {
return BottomSheetBootstrapBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -120,60 +124,60 @@ class BootstrapBottomSheet : VectorBaseBottomSheetDialogFragment() {
override fun invalidate() = withState(viewModel) { state -> override fun invalidate() = withState(viewModel) { state ->
when (state.step) { when (state.step) {
is BootstrapStep.CheckingMigration -> { is BootstrapStep.CheckingMigration -> {
bootstrapIcon.isVisible = false views.bootstrapIcon.isVisible = false
bootstrapTitleText.text = getString(R.string.bottom_sheet_setup_secure_backup_title) views.bootstrapTitleText.text = getString(R.string.bottom_sheet_setup_secure_backup_title)
showFragment(BootstrapWaitingFragment::class, Bundle()) showFragment(BootstrapWaitingFragment::class, Bundle())
} }
is BootstrapStep.FirstForm -> { is BootstrapStep.FirstForm -> {
bootstrapIcon.isVisible = false views.bootstrapIcon.isVisible = false
bootstrapTitleText.text = getString(R.string.bottom_sheet_setup_secure_backup_title) views.bootstrapTitleText.text = getString(R.string.bottom_sheet_setup_secure_backup_title)
showFragment(BootstrapSetupRecoveryKeyFragment::class, Bundle()) showFragment(BootstrapSetupRecoveryKeyFragment::class, Bundle())
} }
is BootstrapStep.SetupPassphrase -> { is BootstrapStep.SetupPassphrase -> {
bootstrapIcon.isVisible = true views.bootstrapIcon.isVisible = true
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_security_phrase_24dp)) views.bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_security_phrase_24dp))
bootstrapTitleText.text = getString(R.string.set_a_security_phrase_title) views.bootstrapTitleText.text = getString(R.string.set_a_security_phrase_title)
showFragment(BootstrapEnterPassphraseFragment::class, Bundle()) showFragment(BootstrapEnterPassphraseFragment::class, Bundle())
} }
is BootstrapStep.ConfirmPassphrase -> { is BootstrapStep.ConfirmPassphrase -> {
bootstrapIcon.isVisible = true views.bootstrapIcon.isVisible = true
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_security_phrase_24dp)) views.bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_security_phrase_24dp))
bootstrapTitleText.text = getString(R.string.set_a_security_phrase_title) views.bootstrapTitleText.text = getString(R.string.set_a_security_phrase_title)
showFragment(BootstrapConfirmPassphraseFragment::class, Bundle()) showFragment(BootstrapConfirmPassphraseFragment::class, Bundle())
} }
is BootstrapStep.AccountPassword -> { is BootstrapStep.AccountPassword -> {
bootstrapIcon.isVisible = true views.bootstrapIcon.isVisible = true
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_user)) views.bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_user))
bootstrapTitleText.text = getString(R.string.account_password) views.bootstrapTitleText.text = getString(R.string.account_password)
showFragment(BootstrapAccountPasswordFragment::class, Bundle()) showFragment(BootstrapAccountPasswordFragment::class, Bundle())
} }
is BootstrapStep.Initializing -> { is BootstrapStep.Initializing -> {
bootstrapIcon.isVisible = true views.bootstrapIcon.isVisible = true
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_security_key_24dp)) views.bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_security_key_24dp))
bootstrapTitleText.text = getString(R.string.bootstrap_loading_title) views.bootstrapTitleText.text = getString(R.string.bootstrap_loading_title)
showFragment(BootstrapWaitingFragment::class, Bundle()) showFragment(BootstrapWaitingFragment::class, Bundle())
} }
is BootstrapStep.SaveRecoveryKey -> { is BootstrapStep.SaveRecoveryKey -> {
bootstrapIcon.isVisible = true views.bootstrapIcon.isVisible = true
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_security_key_24dp)) views.bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_security_key_24dp))
bootstrapTitleText.text = getString(R.string.bottom_sheet_save_your_recovery_key_title) views.bootstrapTitleText.text = getString(R.string.bottom_sheet_save_your_recovery_key_title)
showFragment(BootstrapSaveRecoveryKeyFragment::class, Bundle()) showFragment(BootstrapSaveRecoveryKeyFragment::class, Bundle())
} }
is BootstrapStep.DoneSuccess -> { is BootstrapStep.DoneSuccess -> {
bootstrapIcon.isVisible = true views.bootstrapIcon.isVisible = true
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_security_key_24dp)) views.bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_security_key_24dp))
bootstrapTitleText.text = getString(R.string.bootstrap_finish_title) views.bootstrapTitleText.text = getString(R.string.bootstrap_finish_title)
showFragment(BootstrapConclusionFragment::class, Bundle()) showFragment(BootstrapConclusionFragment::class, Bundle())
} }
is BootstrapStep.GetBackupSecretForMigration -> { is BootstrapStep.GetBackupSecretForMigration -> {
val isKey = state.step.useKey() val isKey = state.step.useKey()
val drawableRes = if (isKey) R.drawable.ic_security_key_24dp else R.drawable.ic_security_phrase_24dp val drawableRes = if (isKey) R.drawable.ic_security_key_24dp else R.drawable.ic_security_phrase_24dp
bootstrapIcon.isVisible = true views.bootstrapIcon.isVisible = true
bootstrapIcon.setImageDrawable(ContextCompat.getDrawable( views.bootstrapIcon.setImageDrawable(ContextCompat.getDrawable(
requireContext(), requireContext(),
drawableRes) drawableRes)
) )
bootstrapTitleText.text = getString(R.string.upgrade_security) views.bootstrapTitleText.text = getString(R.string.upgrade_security)
showFragment(BootstrapMigrateBackupFragment::class, Bundle()) showFragment(BootstrapMigrateBackupFragment::class, Bundle())
} }
}.exhaustive }.exhaustive

View file

@ -17,7 +17,9 @@
package im.vector.app.features.crypto.recover package im.vector.app.features.crypto.recover
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.core.text.toSpannable import androidx.core.text.toSpannable
import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
@ -25,27 +27,31 @@ import im.vector.app.R
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.utils.colorizeMatchingText import im.vector.app.core.utils.colorizeMatchingText
import kotlinx.android.synthetic.main.fragment_bootstrap_conclusion.* import im.vector.app.databinding.FragmentBootstrapConclusionBinding
import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import javax.inject.Inject import javax.inject.Inject
class BootstrapConclusionFragment @Inject constructor( class BootstrapConclusionFragment @Inject constructor(
private val colorProvider: ColorProvider private val colorProvider: ColorProvider
) : VectorBaseFragment() { ) : VectorBaseFragment<FragmentBootstrapConclusionBinding>() {
override fun getLayoutResId() = R.layout.fragment_bootstrap_conclusion override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBootstrapConclusionBinding {
return FragmentBootstrapConclusionBinding.inflate(inflater, container, false)
}
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel() val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
bootstrapConclusionContinue.clickableView.debouncedClicks { sharedViewModel.handle(BootstrapActions.Completed) } views.bootstrapConclusionContinue.clickableView.debouncedClicks { sharedViewModel.handle(BootstrapActions.Completed) }
} }
override fun invalidate() = withState(sharedViewModel) { state -> override fun invalidate() = withState(sharedViewModel) { state ->
if (state.step !is BootstrapStep.DoneSuccess) return@withState if (state.step !is BootstrapStep.DoneSuccess) return@withState
bootstrapConclusionText.text = getString( views.bootstrapConclusionText.text = getString(
R.string.bootstrap_cross_signing_success, R.string.bootstrap_cross_signing_success,
getString(R.string.recovery_passphrase), getString(R.string.recovery_passphrase),
getString(R.string.message_key) getString(R.string.message_key)

View file

@ -17,7 +17,9 @@
package im.vector.app.features.crypto.recover package im.vector.app.features.crypto.recover
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import androidx.core.view.isGone import androidx.core.view.isGone
import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.parentFragmentViewModel
@ -28,14 +30,19 @@ import im.vector.app.R
import im.vector.app.core.extensions.hideKeyboard import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.showPassword import im.vector.app.core.extensions.showPassword
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentBootstrapEnterPassphraseBinding
import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.synthetic.main.fragment_bootstrap_enter_passphrase.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
class BootstrapConfirmPassphraseFragment @Inject constructor() : VectorBaseFragment() { class BootstrapConfirmPassphraseFragment @Inject constructor()
: VectorBaseFragment<FragmentBootstrapEnterPassphraseBinding>() {
override fun getLayoutResId() = R.layout.fragment_bootstrap_enter_passphrase override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBootstrapEnterPassphraseBinding {
return FragmentBootstrapEnterPassphraseBinding.inflate(inflater, container, false)
}
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel() val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()

View file

@ -17,7 +17,9 @@
package im.vector.app.features.crypto.recover package im.vector.app.features.crypto.recover
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
@ -26,29 +28,34 @@ import com.jakewharton.rxbinding3.widget.textChanges
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.showPassword import im.vector.app.core.extensions.showPassword
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentBootstrapEnterPassphraseBinding
import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import im.vector.app.features.settings.VectorLocale import im.vector.app.features.settings.VectorLocale
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.synthetic.main.fragment_bootstrap_enter_passphrase.*
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
class BootstrapEnterPassphraseFragment @Inject constructor() : VectorBaseFragment() { class BootstrapEnterPassphraseFragment @Inject constructor()
: VectorBaseFragment<FragmentBootstrapEnterPassphraseBinding>() {
override fun getLayoutResId() = R.layout.fragment_bootstrap_enter_passphrase override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBootstrapEnterPassphraseBinding {
return FragmentBootstrapEnterPassphraseBinding.inflate(inflater, container, false)
}
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel() val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
bootstrapDescriptionText.text = getString(R.string.set_a_security_phrase_notice) views.bootstrapDescriptionText.text = getString(R.string.set_a_security_phrase_notice)
ssss_passphrase_enter_edittext.hint = getString(R.string.set_a_security_phrase_hint) views.ssssPassphraseEnterEdittext.hint = getString(R.string.set_a_security_phrase_hint)
withState(sharedViewModel) { withState(sharedViewModel) {
// set initial value (useful when coming back) // set initial value (useful when coming back)
ssss_passphrase_enter_edittext.setText(it.passphrase ?: "") views.ssssPassphraseEnterEdittext.setText(it.passphrase ?: "")
} }
ssss_passphrase_enter_edittext.editorActionEvents() views.ssssPassphraseEnterEdittext.editorActionEvents()
.throttleFirst(300, TimeUnit.MILLISECONDS) .throttleFirst(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe { .subscribe {
@ -58,7 +65,7 @@ class BootstrapEnterPassphraseFragment @Inject constructor() : VectorBaseFragmen
} }
.disposeOnDestroyView() .disposeOnDestroyView()
ssss_passphrase_enter_edittext.textChanges() views.ssssPassphraseEnterEdittext.textChanges()
.subscribe { .subscribe {
// ssss_passphrase_enter_til.error = null // ssss_passphrase_enter_til.error = null
sharedViewModel.handle(BootstrapActions.UpdateCandidatePassphrase(it?.toString() ?: "")) sharedViewModel.handle(BootstrapActions.UpdateCandidatePassphrase(it?.toString() ?: ""))
@ -74,8 +81,8 @@ class BootstrapEnterPassphraseFragment @Inject constructor() : VectorBaseFragmen
// } // }
} }
ssss_view_show_password.debouncedClicks { sharedViewModel.handle(BootstrapActions.TogglePasswordVisibility) } views.ssssViewShowPassword.debouncedClicks { sharedViewModel.handle(BootstrapActions.TogglePasswordVisibility) }
bootstrapSubmit.debouncedClicks { submit() } views.bootstrapSubmit.debouncedClicks { submit() }
} }
private fun submit() = withState(sharedViewModel) { state -> private fun submit() = withState(sharedViewModel) { state ->
@ -83,11 +90,11 @@ class BootstrapEnterPassphraseFragment @Inject constructor() : VectorBaseFragmen
return@withState return@withState
} }
val score = state.passphraseStrength.invoke()?.score val score = state.passphraseStrength.invoke()?.score
val passphrase = ssss_passphrase_enter_edittext.text?.toString() val passphrase = views.ssssPassphraseEnterEdittext.text?.toString()
if (passphrase.isNullOrBlank()) { if (passphrase.isNullOrBlank()) {
ssss_passphrase_enter_til.error = getString(R.string.passphrase_empty_error_message) views.ssssPassphraseEnterTil.error = getString(R.string.passphrase_empty_error_message)
} else if (score != 4) { } else if (score != 4) {
ssss_passphrase_enter_til.error = getString(R.string.passphrase_passphrase_too_weak) views.ssssPassphraseEnterTil.error = getString(R.string.passphrase_passphrase_too_weak)
} else { } else {
sharedViewModel.handle(BootstrapActions.GoToConfirmPassphrase(passphrase)) sharedViewModel.handle(BootstrapActions.GoToConfirmPassphrase(passphrase))
} }
@ -96,21 +103,21 @@ class BootstrapEnterPassphraseFragment @Inject constructor() : VectorBaseFragmen
override fun invalidate() = withState(sharedViewModel) { state -> override fun invalidate() = withState(sharedViewModel) { state ->
if (state.step is BootstrapStep.SetupPassphrase) { if (state.step is BootstrapStep.SetupPassphrase) {
val isPasswordVisible = state.step.isPasswordVisible val isPasswordVisible = state.step.isPasswordVisible
ssss_passphrase_enter_edittext.showPassword(isPasswordVisible, updateCursor = false) views.ssssPassphraseEnterEdittext.showPassword(isPasswordVisible, updateCursor = false)
ssss_view_show_password.setImageResource(if (isPasswordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye) views.ssssViewShowPassword.setImageResource(if (isPasswordVisible) R.drawable.ic_eye_closed else R.drawable.ic_eye)
state.passphraseStrength.invoke()?.let { strength -> state.passphraseStrength.invoke()?.let { strength ->
val score = strength.score val score = strength.score
ssss_passphrase_security_progress.strength = score views.ssssPassphraseSecurityProgress.strength = score
if (score in 1..3) { if (score in 1..3) {
val hint = val hint =
strength.feedback?.getWarning(VectorLocale.applicationLocale)?.takeIf { it.isNotBlank() } strength.feedback?.getWarning(VectorLocale.applicationLocale)?.takeIf { it.isNotBlank() }
?: strength.feedback?.getSuggestions(VectorLocale.applicationLocale)?.firstOrNull() ?: strength.feedback?.getSuggestions(VectorLocale.applicationLocale)?.firstOrNull()
if (hint != null && hint != ssss_passphrase_enter_til.error.toString()) { if (hint != null && hint != views.ssssPassphraseEnterTil.error.toString()) {
ssss_passphrase_enter_til.error = hint views.ssssPassphraseEnterTil.error = hint
} }
} else { } else {
ssss_passphrase_enter_til.error = null views.ssssPassphraseEnterTil.error = null
} }
} }
} }

View file

@ -21,7 +21,9 @@ import android.os.Bundle
import android.text.InputType.TYPE_CLASS_TEXT import android.text.InputType.TYPE_CLASS_TEXT
import android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE import android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE
import android.text.InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD import android.text.InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import androidx.core.text.toSpannable import androidx.core.text.toSpannable
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -37,9 +39,11 @@ import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.utils.colorizeMatchingText import im.vector.app.core.utils.colorizeMatchingText
import im.vector.app.core.utils.startImportTextFromFileIntent import im.vector.app.core.utils.startImportTextFromFileIntent
import im.vector.app.databinding.FragmentBootstrapMigrateBackupBinding
import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.synthetic.main.fragment_bootstrap_enter_passphrase.bootstrapDescriptionText
import kotlinx.android.synthetic.main.fragment_bootstrap_migrate_backup.*
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.internal.crypto.keysbackup.util.isValidRecoveryKey import org.matrix.android.sdk.internal.crypto.keysbackup.util.isValidRecoveryKey
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -47,9 +51,11 @@ import javax.inject.Inject
class BootstrapMigrateBackupFragment @Inject constructor( class BootstrapMigrateBackupFragment @Inject constructor(
private val colorProvider: ColorProvider private val colorProvider: ColorProvider
) : VectorBaseFragment() { ) : VectorBaseFragment<FragmentBootstrapMigrateBackupBinding>() {
override fun getLayoutResId() = R.layout.fragment_bootstrap_migrate_backup override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBootstrapMigrateBackupBinding {
return FragmentBootstrapMigrateBackupBinding.inflate(inflater, container, false)
}
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel() val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()

View file

@ -20,7 +20,9 @@ import android.app.Activity
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
@ -30,7 +32,9 @@ import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.core.utils.toast import im.vector.app.core.utils.toast
import kotlinx.android.synthetic.main.fragment_bootstrap_save_key.* import im.vector.app.databinding.FragmentBootstrapSaveKeyBinding
import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -38,18 +42,20 @@ import javax.inject.Inject
class BootstrapSaveRecoveryKeyFragment @Inject constructor( class BootstrapSaveRecoveryKeyFragment @Inject constructor(
private val colorProvider: ColorProvider private val colorProvider: ColorProvider
) : VectorBaseFragment() { ) : VectorBaseFragment<FragmentBootstrapSaveKeyBinding>() {
override fun getLayoutResId() = R.layout.fragment_bootstrap_save_key override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBootstrapSaveKeyBinding {
return FragmentBootstrapSaveKeyBinding.inflate(inflater, container, false)
}
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel() val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
recoverySave.clickableView.debouncedClicks { downloadRecoveryKey() } views.recoverySave.clickableView.debouncedClicks { downloadRecoveryKey() }
recoveryCopy.clickableView.debouncedClicks { shareRecoveryKey() } views.recoveryCopy.clickableView.debouncedClicks { shareRecoveryKey() }
recoveryContinue.clickableView.debouncedClicks { views.recoveryContinue.clickableView.debouncedClicks {
// We do not display the final Fragment anymore // We do not display the final Fragment anymore
// TODO Do some cleanup // TODO Do some cleanup
// sharedViewModel.handle(BootstrapActions.GoToCompleted) // sharedViewModel.handle(BootstrapActions.GoToCompleted)
@ -112,7 +118,7 @@ class BootstrapSaveRecoveryKeyFragment @Inject constructor(
val step = state.step val step = state.step
if (step !is BootstrapStep.SaveRecoveryKey) return@withState if (step !is BootstrapStep.SaveRecoveryKey) return@withState
recoveryContinue.isVisible = step.isSaved views.recoveryContinue.isVisible = step.isSaved
bootstrapRecoveryKeyText.text = state.recoveryKeyCreationInfo?.recoveryKey?.formatRecoveryKey() views.bootstrapRecoveryKeyText.text = state.recoveryKeyCreationInfo?.recoveryKey?.formatRecoveryKey()
} }
} }

View file

@ -17,18 +17,25 @@
package im.vector.app.features.crypto.recover package im.vector.app.features.crypto.recover
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import kotlinx.android.synthetic.main.fragment_bootstrap_setup_recovery.* import im.vector.app.databinding.FragmentBootstrapSetupRecoveryBinding
import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import javax.inject.Inject import javax.inject.Inject
class BootstrapSetupRecoveryKeyFragment @Inject constructor() : VectorBaseFragment() { class BootstrapSetupRecoveryKeyFragment @Inject constructor()
: VectorBaseFragment<FragmentBootstrapSetupRecoveryBinding>() {
override fun getLayoutResId() = R.layout.fragment_bootstrap_setup_recovery override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBootstrapSetupRecoveryBinding {
return FragmentBootstrapSetupRecoveryBinding.inflate(inflater, container, false)
}
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel() val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
@ -36,15 +43,15 @@ class BootstrapSetupRecoveryKeyFragment @Inject constructor() : VectorBaseFragme
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
// Actions when a key backup exist // Actions when a key backup exist
bootstrapSetupSecureSubmit.clickableView.debouncedClicks { views.bootstrapSetupSecureSubmit.clickableView.debouncedClicks {
sharedViewModel.handle(BootstrapActions.StartKeyBackupMigration) sharedViewModel.handle(BootstrapActions.StartKeyBackupMigration)
} }
// Actions when there is no key backup // Actions when there is no key backup
bootstrapSetupSecureUseSecurityKey.clickableView.debouncedClicks { views.bootstrapSetupSecureUseSecurityKey.clickableView.debouncedClicks {
sharedViewModel.handle(BootstrapActions.Start(userWantsToEnterPassphrase = false)) sharedViewModel.handle(BootstrapActions.Start(userWantsToEnterPassphrase = false))
} }
bootstrapSetupSecureUseSecurityPassphrase.clickableView.debouncedClicks { views.bootstrapSetupSecureUseSecurityPassphrase.clickableView.debouncedClicks {
sharedViewModel.handle(BootstrapActions.Start(userWantsToEnterPassphrase = true)) sharedViewModel.handle(BootstrapActions.Start(userWantsToEnterPassphrase = true))
} }
} }
@ -53,23 +60,23 @@ class BootstrapSetupRecoveryKeyFragment @Inject constructor() : VectorBaseFragme
if (state.step is BootstrapStep.FirstForm) { if (state.step is BootstrapStep.FirstForm) {
if (state.step.keyBackUpExist) { if (state.step.keyBackUpExist) {
// Display the set up action // Display the set up action
bootstrapSetupSecureSubmit.isVisible = true views.bootstrapSetupSecureSubmit.isVisible = true
bootstrapSetupSecureUseSecurityKey.isVisible = false views.bootstrapSetupSecureUseSecurityKey.isVisible = false
bootstrapSetupSecureUseSecurityPassphrase.isVisible = false views.bootstrapSetupSecureUseSecurityPassphrase.isVisible = false
bootstrapSetupSecureUseSecurityPassphraseSeparator.isVisible = false views.bootstrapSetupSecureUseSecurityPassphraseSeparator.isVisible = false
} else { } else {
if (state.step.reset) { if (state.step.reset) {
bootstrapSetupSecureText.text = getString(R.string.reset_secure_backup_title) views.bootstrapSetupSecureText.text = getString(R.string.reset_secure_backup_title)
bootstrapSetupWarningTextView.isVisible = true views.bootstrapSetupWarningTextView.isVisible = true
} else { } else {
bootstrapSetupSecureText.text = getString(R.string.bottom_sheet_setup_secure_backup_subtitle) views.bootstrapSetupSecureText.text = getString(R.string.bottom_sheet_setup_secure_backup_subtitle)
bootstrapSetupWarningTextView.isVisible = false views.bootstrapSetupWarningTextView.isVisible = false
} }
// Choose between create a passphrase or use a recovery key // Choose between create a passphrase or use a recovery key
bootstrapSetupSecureSubmit.isVisible = false views.bootstrapSetupSecureSubmit.isVisible = false
bootstrapSetupSecureUseSecurityKey.isVisible = true views.bootstrapSetupSecureUseSecurityKey.isVisible = true
bootstrapSetupSecureUseSecurityPassphrase.isVisible = true views.bootstrapSetupSecureUseSecurityPassphrase.isVisible = true
bootstrapSetupSecureUseSecurityPassphraseSeparator.isVisible = true views.bootstrapSetupSecureUseSecurityPassphraseSeparator.isVisible = true
} }
} }
} }

View file

@ -16,26 +16,33 @@
package im.vector.app.features.crypto.recover package im.vector.app.features.crypto.recover
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import kotlinx.android.synthetic.main.fragment_bootstrap_waiting.* import im.vector.app.databinding.FragmentBootstrapWaitingBinding
import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import javax.inject.Inject import javax.inject.Inject
class BootstrapWaitingFragment @Inject constructor() : VectorBaseFragment() { class BootstrapWaitingFragment @Inject constructor()
: VectorBaseFragment<FragmentBootstrapWaitingBinding>() {
override fun getLayoutResId() = R.layout.fragment_bootstrap_waiting override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBootstrapWaitingBinding {
return FragmentBootstrapWaitingBinding.inflate(inflater, container, false)
}
val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel() val sharedViewModel: BootstrapSharedViewModel by parentFragmentViewModel()
override fun invalidate() = withState(sharedViewModel) { state -> override fun invalidate() = withState(sharedViewModel) { state ->
when (state.step) { when (state.step) {
is BootstrapStep.Initializing -> { is BootstrapStep.Initializing -> {
bootstrapLoadingStatusText.isVisible = true views.bootstrapLoadingStatusText.isVisible = true
bootstrapDescriptionText.isVisible = true views.bootstrapDescriptionText.isVisible = true
bootstrapLoadingStatusText.text = state.initializationWaitingViewData?.message views.bootstrapLoadingStatusText.text = state.initializationWaitingViewData?.message
} }
// is BootstrapStep.CheckingMigration -> { // is BootstrapStep.CheckingMigration -> {
// bootstrapLoadingStatusText.isVisible = false // bootstrapLoadingStatusText.isVisible = false
@ -43,8 +50,8 @@ class BootstrapWaitingFragment @Inject constructor() : VectorBaseFragment() {
// } // }
else -> { else -> {
// just show the spinner // just show the spinner
bootstrapLoadingStatusText.isVisible = false views.bootstrapLoadingStatusText.isVisible = false
bootstrapDescriptionText.isVisible = false views.bootstrapDescriptionText.isVisible = false
} }
} }
} }

View file

@ -16,10 +16,16 @@
package im.vector.app.features.crypto.verification package im.vector.app.features.crypto.verification
import android.view.LayoutInflater
import android.view.ViewGroup
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import im.vector.app.databinding.FragmentProgressBinding
import javax.inject.Inject import javax.inject.Inject
class QuadSLoadingFragment @Inject constructor() : VectorBaseFragment() { class QuadSLoadingFragment @Inject constructor() : VectorBaseFragment<FragmentProgressBinding>() {
override fun getLayoutResId() = R.layout.fragment_progress override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentProgressBinding {
return FragmentProgressBinding.inflate(inflater, container, false)
}
} }

View file

@ -20,7 +20,9 @@ import android.app.Dialog
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.KeyEvent import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@ -34,6 +36,8 @@ import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.extensions.registerStartForActivityResult import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.BottomSheetGenericListBinding
import im.vector.app.databinding.BottomSheetVerificationBinding
import im.vector.app.features.crypto.quads.SharedSecureStorageActivity import im.vector.app.features.crypto.quads.SharedSecureStorageActivity
import im.vector.app.features.crypto.verification.cancel.VerificationCancelFragment 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.cancel.VerificationNotMeFragment
@ -45,8 +49,8 @@ import im.vector.app.features.crypto.verification.qrconfirmation.VerificationQrS
import im.vector.app.features.crypto.verification.request.VerificationRequestFragment import im.vector.app.features.crypto.verification.request.VerificationRequestFragment
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.settings.VectorSettingsActivity
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.android.synthetic.main.bottom_sheet_verification.*
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
@ -58,7 +62,7 @@ import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import kotlin.reflect.KClass import kotlin.reflect.KClass
class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetVerificationBinding>() {
@Parcelize @Parcelize
data class VerificationArgs( data class VerificationArgs(
@ -84,7 +88,9 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
injector.inject(this) injector.inject(this)
} }
override fun getLayoutResId() = R.layout.bottom_sheet_verification override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationBinding {
return BottomSheetVerificationBinding.inflate(inflater, container, false)
}
init { init {
isCancelable = false isCancelable = false
@ -115,7 +121,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
} }
VerificationBottomSheetViewEvents.GoToSettings -> { VerificationBottomSheetViewEvents.GoToSettings -> {
dismiss() dismiss()
(activity as? VectorBaseActivity)?.navigator?.openSettings(requireContext(), VectorSettingsActivity.EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY) (activity as? VectorBaseActivity<*>)?.navigator?.openSettings(requireContext(), VectorSettingsActivity.EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY)
} }
}.exhaustive }.exhaustive
} }
@ -152,27 +158,27 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
override fun invalidate() = withState(viewModel) { state -> override fun invalidate() = withState(viewModel) { state ->
state.otherUserMxItem?.let { matrixItem -> state.otherUserMxItem?.let { matrixItem ->
if (state.isMe) { if (state.isMe) {
avatarRenderer.render(matrixItem, otherUserAvatarImageView) avatarRenderer.render(matrixItem, views.otherUserAvatarImageView)
if (state.sasTransactionState == VerificationTxState.Verified if (state.sasTransactionState == VerificationTxState.Verified
|| state.qrTransactionState == VerificationTxState.Verified || state.qrTransactionState == VerificationTxState.Verified
|| state.verifiedFromPrivateKeys) { || state.verifiedFromPrivateKeys) {
otherUserShield.setImageResource(R.drawable.ic_shield_trusted) views.otherUserShield.setImageResource(R.drawable.ic_shield_trusted)
} else { } else {
otherUserShield.setImageResource(R.drawable.ic_shield_warning) views.otherUserShield.setImageResource(R.drawable.ic_shield_warning)
} }
otherUserNameText.text = getString( views.otherUserNameText.text = getString(
if (state.selfVerificationMode) R.string.crosssigning_verify_this_session else R.string.crosssigning_verify_session if (state.selfVerificationMode) R.string.crosssigning_verify_this_session else R.string.crosssigning_verify_session
) )
otherUserShield.isVisible = true views.otherUserShield.isVisible = true
} else { } else {
avatarRenderer.render(matrixItem, otherUserAvatarImageView) avatarRenderer.render(matrixItem, views.otherUserAvatarImageView)
if (state.sasTransactionState == VerificationTxState.Verified || state.qrTransactionState == VerificationTxState.Verified) { if (state.sasTransactionState == VerificationTxState.Verified || state.qrTransactionState == VerificationTxState.Verified) {
otherUserNameText.text = getString(R.string.verification_verified_user, matrixItem.getBestName()) views.otherUserNameText.text = getString(R.string.verification_verified_user, matrixItem.getBestName())
otherUserShield.isVisible = true views.otherUserShield.isVisible = true
} else { } else {
otherUserNameText.text = getString(R.string.verification_verify_user, matrixItem.getBestName()) views.otherUserNameText.text = getString(R.string.verification_verify_user, matrixItem.getBestName())
otherUserShield.isVisible = false views.otherUserShield.isVisible = false
} }
} }
} }
@ -189,13 +195,13 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
} }
if (state.userThinkItsNotHim) { if (state.userThinkItsNotHim) {
otherUserNameText.text = getString(R.string.dialog_title_warning) views.otherUserNameText.text = getString(R.string.dialog_title_warning)
showFragment(VerificationNotMeFragment::class, Bundle()) showFragment(VerificationNotMeFragment::class, Bundle())
return@withState return@withState
} }
if (state.userWantsToCancel) { if (state.userWantsToCancel) {
otherUserNameText.text = getString(R.string.are_you_sure) views.otherUserNameText.text = getString(R.string.are_you_sure)
showFragment(VerificationCancelFragment::class, Bundle()) showFragment(VerificationCancelFragment::class, Bundle())
return@withState return@withState
} }
@ -287,7 +293,7 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
// Transaction has not yet started // Transaction has not yet started
if (state.pendingRequest.invoke()?.cancelConclusion != null) { if (state.pendingRequest.invoke()?.cancelConclusion != null) {
// The request has been declined, we should dismiss // The request has been declined, we should dismiss
otherUserNameText.text = getString(R.string.verification_cancelled) views.otherUserNameText.text = getString(R.string.verification_cancelled)
showFragment(VerificationConclusionFragment::class, Bundle().apply { showFragment(VerificationConclusionFragment::class, Bundle().apply {
putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args( putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(
false, false,

View file

@ -17,24 +17,31 @@
package im.vector.app.features.crypto.verification.cancel package im.vector.app.features.crypto.verification.cancel
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.*
import javax.inject.Inject import javax.inject.Inject
class VerificationCancelFragment @Inject constructor( class VerificationCancelFragment @Inject constructor(
val controller: VerificationCancelController val controller: VerificationCancelController
) : VectorBaseFragment(), VerificationCancelController.Listener { ) : VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(),
VerificationCancelController.Listener {
private val viewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class) private val viewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -42,13 +49,13 @@ class VerificationCancelFragment @Inject constructor(
} }
override fun onDestroyView() { override fun onDestroyView() {
bottomSheetVerificationRecyclerView.cleanup() views.bottomSheetVerificationRecyclerView.cleanup()
controller.listener = null controller.listener = null
super.onDestroyView() super.onDestroyView()
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {
bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true) views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
controller.listener = this controller.listener = this
} }

View file

@ -17,24 +17,31 @@
package im.vector.app.features.crypto.verification.cancel package im.vector.app.features.crypto.verification.cancel
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.databinding.FragmentKeysBackupRestoreFromKeyBinding
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.*
import javax.inject.Inject import javax.inject.Inject
class VerificationNotMeFragment @Inject constructor( class VerificationNotMeFragment @Inject constructor(
val controller: VerificationNotMeController val controller: VerificationNotMeController
) : VectorBaseFragment(), VerificationNotMeController.Listener { ) : VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(),
VerificationNotMeController.Listener {
private val viewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class) private val viewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -42,13 +49,13 @@ class VerificationNotMeFragment @Inject constructor(
} }
override fun onDestroyView() { override fun onDestroyView() {
bottomSheetVerificationRecyclerView.cleanup() views.bottomSheetVerificationRecyclerView.cleanup()
controller.listener = null controller.listener = null
super.onDestroyView() super.onDestroyView()
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {
bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true) views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
controller.listener = this controller.listener = this
} }

View file

@ -17,7 +17,9 @@ package im.vector.app.features.crypto.verification.choose
import android.app.Activity import android.app.Activity
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
@ -29,23 +31,27 @@ import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
import im.vector.app.core.utils.checkPermissions import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.registerForPermissionsResult import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.features.crypto.verification.VerificationAction import im.vector.app.features.crypto.verification.VerificationAction
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
import im.vector.app.features.qrcode.QrCodeScannerActivity import im.vector.app.features.qrcode.QrCodeScannerActivity
import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.*
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class VerificationChooseMethodFragment @Inject constructor( class VerificationChooseMethodFragment @Inject constructor(
val verificationChooseMethodViewModelFactory: VerificationChooseMethodViewModel.Factory, val verificationChooseMethodViewModelFactory: VerificationChooseMethodViewModel.Factory,
val controller: VerificationChooseMethodController val controller: VerificationChooseMethodController
) : VectorBaseFragment(), VerificationChooseMethodController.Listener { ) : VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(),
VerificationChooseMethodController.Listener {
private val viewModel by fragmentViewModel(VerificationChooseMethodViewModel::class) private val viewModel by fragmentViewModel(VerificationChooseMethodViewModel::class)
private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class) private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -54,13 +60,13 @@ class VerificationChooseMethodFragment @Inject constructor(
} }
override fun onDestroyView() { override fun onDestroyView() {
bottomSheetVerificationRecyclerView.cleanup() views.bottomSheetVerificationRecyclerView.cleanup()
controller.listener = null controller.listener = null
super.onDestroyView() super.onDestroyView()
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {
bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true) views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
controller.listener = this controller.listener = this
} }

View file

@ -17,7 +17,9 @@ package im.vector.app.features.crypto.verification.conclusion
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
@ -25,15 +27,17 @@ import im.vector.app.R
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.features.crypto.verification.VerificationAction import im.vector.app.features.crypto.verification.VerificationAction
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.*
import javax.inject.Inject import javax.inject.Inject
class VerificationConclusionFragment @Inject constructor( class VerificationConclusionFragment @Inject constructor(
val controller: VerificationConclusionController val controller: VerificationConclusionController
) : VectorBaseFragment(), VerificationConclusionController.Listener { ) : VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(),
VerificationConclusionController.Listener {
@Parcelize @Parcelize
data class Args( data class Args(
@ -46,7 +50,9 @@ class VerificationConclusionFragment @Inject constructor(
private val viewModel by fragmentViewModel(VerificationConclusionViewModel::class) private val viewModel by fragmentViewModel(VerificationConclusionViewModel::class)
override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -55,13 +61,13 @@ class VerificationConclusionFragment @Inject constructor(
} }
override fun onDestroyView() { override fun onDestroyView() {
bottomSheetVerificationRecyclerView.cleanup() views.bottomSheetVerificationRecyclerView.cleanup()
controller.listener = null controller.listener = null
super.onDestroyView() super.onDestroyView()
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {
bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true) views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
controller.listener = this controller.listener = this
} }

View file

@ -16,7 +16,9 @@
package im.vector.app.features.crypto.verification.emoji package im.vector.app.features.crypto.verification.emoji
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
@ -24,21 +26,25 @@ import im.vector.app.R
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.features.crypto.verification.VerificationAction import im.vector.app.features.crypto.verification.VerificationAction
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.*
import javax.inject.Inject import javax.inject.Inject
class VerificationEmojiCodeFragment @Inject constructor( class VerificationEmojiCodeFragment @Inject constructor(
val viewModelFactory: VerificationEmojiCodeViewModel.Factory, val viewModelFactory: VerificationEmojiCodeViewModel.Factory,
val controller: VerificationEmojiCodeController val controller: VerificationEmojiCodeController
) : VectorBaseFragment(), VerificationEmojiCodeController.Listener { ) : VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(),
VerificationEmojiCodeController.Listener {
private val viewModel by fragmentViewModel(VerificationEmojiCodeViewModel::class) private val viewModel by fragmentViewModel(VerificationEmojiCodeViewModel::class)
private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class) private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -47,13 +53,13 @@ class VerificationEmojiCodeFragment @Inject constructor(
} }
override fun onDestroyView() { override fun onDestroyView() {
bottomSheetVerificationRecyclerView.cleanup() views.bottomSheetVerificationRecyclerView.cleanup()
controller.listener = null controller.listener = null
super.onDestroyView() super.onDestroyView()
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {
bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true) views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
controller.listener = this controller.listener = this
} }

View file

@ -18,19 +18,22 @@ package im.vector.app.features.crypto.verification.qrconfirmation
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MvRx
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import kotlinx.android.parcel.Parcelize import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.* import kotlinx.parcelize.Parcelize
import javax.inject.Inject import javax.inject.Inject
class VerificationQRWaitingFragment @Inject constructor( class VerificationQRWaitingFragment @Inject constructor(
val controller: VerificationQRWaitingController val controller: VerificationQRWaitingController
) : VectorBaseFragment() { ) : VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>() {
@Parcelize @Parcelize
data class Args( data class Args(
@ -38,7 +41,9 @@ class VerificationQRWaitingFragment @Inject constructor(
val otherUserName: String val otherUserName: String
) : Parcelable ) : Parcelable
override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -49,11 +54,11 @@ class VerificationQRWaitingFragment @Inject constructor(
} }
override fun onDestroyView() { override fun onDestroyView() {
bottomSheetVerificationRecyclerView.cleanup() views.bottomSheetVerificationRecyclerView.cleanup()
super.onDestroyView() super.onDestroyView()
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {
bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true) views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
} }
} }

View file

@ -16,25 +16,31 @@
package im.vector.app.features.crypto.verification.qrconfirmation package im.vector.app.features.crypto.verification.qrconfirmation
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.features.crypto.verification.VerificationAction import im.vector.app.features.crypto.verification.VerificationAction
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.*
import javax.inject.Inject import javax.inject.Inject
class VerificationQrScannedByOtherFragment @Inject constructor( class VerificationQrScannedByOtherFragment @Inject constructor(
val controller: VerificationQrScannedByOtherController val controller: VerificationQrScannedByOtherController
) : VectorBaseFragment(), VerificationQrScannedByOtherController.Listener { ) : VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(),
VerificationQrScannedByOtherController.Listener {
private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class) private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -46,13 +52,13 @@ class VerificationQrScannedByOtherFragment @Inject constructor(
} }
override fun onDestroyView() { override fun onDestroyView() {
bottomSheetVerificationRecyclerView.cleanup() views.bottomSheetVerificationRecyclerView.cleanup()
controller.listener = null controller.listener = null
super.onDestroyView() super.onDestroyView()
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {
bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true) views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
controller.listener = this controller.listener = this
} }

View file

@ -16,25 +16,31 @@
package im.vector.app.features.crypto.verification.request package im.vector.app.features.crypto.verification.request
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.features.crypto.verification.VerificationAction import im.vector.app.features.crypto.verification.VerificationAction
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.*
import javax.inject.Inject import javax.inject.Inject
class VerificationRequestFragment @Inject constructor( class VerificationRequestFragment @Inject constructor(
val controller: VerificationRequestController val controller: VerificationRequestController
) : VectorBaseFragment(), VerificationRequestController.Listener { ) : VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(),
VerificationRequestController.Listener {
private val viewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class) private val viewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -42,13 +48,13 @@ class VerificationRequestFragment @Inject constructor(
} }
override fun onDestroyView() { override fun onDestroyView() {
bottomSheetVerificationRecyclerView.cleanup() views.bottomSheetVerificationRecyclerView.cleanup()
controller.listener = null controller.listener = null
super.onDestroyView() super.onDestroyView()
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {
bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true) views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
controller.listener = this controller.listener = this
} }

View file

@ -17,8 +17,11 @@ package im.vector.app.features.discovery
import android.app.Activity import android.app.Activity
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.app.R import im.vector.app.R
@ -30,9 +33,11 @@ import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.ensureProtocol import im.vector.app.core.utils.ensureProtocol
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.databinding.FragmentGenericRecyclerBinding
import im.vector.app.features.discovery.change.SetIdentityServerFragment import im.vector.app.features.discovery.change.SetIdentityServerFragment
import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.settings.VectorSettingsActivity
import kotlinx.android.synthetic.main.fragment_generic_recycler.*
import org.matrix.android.sdk.api.session.identity.SharedState import org.matrix.android.sdk.api.session.identity.SharedState
import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.identity.ThreePid
import org.matrix.android.sdk.api.session.terms.TermsService import org.matrix.android.sdk.api.session.terms.TermsService
@ -41,9 +46,12 @@ import javax.inject.Inject
class DiscoverySettingsFragment @Inject constructor( class DiscoverySettingsFragment @Inject constructor(
private val controller: DiscoverySettingsController, private val controller: DiscoverySettingsController,
val viewModelFactory: DiscoverySettingsViewModel.Factory val viewModelFactory: DiscoverySettingsViewModel.Factory
) : VectorBaseFragment(), DiscoverySettingsController.Listener { ) : VectorBaseFragment<FragmentGenericRecyclerBinding>(),
DiscoverySettingsController.Listener {
override fun getLayoutResId() = R.layout.fragment_generic_recycler override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentGenericRecyclerBinding {
return FragmentGenericRecyclerBinding.inflate(inflater, container, false)
}
private val viewModel by fragmentViewModel(DiscoverySettingsViewModel::class) private val viewModel by fragmentViewModel(DiscoverySettingsViewModel::class)
@ -55,7 +63,7 @@ class DiscoverySettingsFragment @Inject constructor(
sharedViewModel = activityViewModelProvider.get(DiscoverySharedViewModel::class.java) sharedViewModel = activityViewModelProvider.get(DiscoverySharedViewModel::class.java)
controller.listener = this controller.listener = this
genericRecyclerView.configureWith(controller) views.genericRecyclerView.configureWith(controller)
sharedViewModel.navigateEvent.observeEvent(this) { sharedViewModel.navigateEvent.observeEvent(this) {
when (it) { when (it) {
@ -74,7 +82,7 @@ class DiscoverySettingsFragment @Inject constructor(
} }
override fun onDestroyView() { override fun onDestroyView() {
genericRecyclerView.cleanup() views.genericRecyclerView.cleanup()
controller.listener = null controller.listener = null
super.onDestroyView() super.onDestroyView()
} }
@ -85,7 +93,7 @@ class DiscoverySettingsFragment @Inject constructor(
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_discovery_category) (activity as? AppCompatActivity)?.supportActionBar?.setTitle(R.string.settings_discovery_category)
// If some 3pids are pending, we can try to check if they have been verified here // If some 3pids are pending, we can try to check if they have been verified here
viewModel.handle(DiscoverySettingsAction.Refresh) viewModel.handle(DiscoverySettingsAction.Refresh)

View file

@ -17,7 +17,9 @@ package im.vector.app.features.discovery.change
import android.app.Activity import android.app.Activity
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.text.toSpannable import androidx.core.text.toSpannable
@ -33,17 +35,21 @@ import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.utils.colorizeMatchingText import im.vector.app.core.utils.colorizeMatchingText
import im.vector.app.databinding.FragmentGenericRecyclerBinding
import im.vector.app.databinding.FragmentSetIdentityServerBinding
import im.vector.app.features.discovery.DiscoverySharedViewModel import im.vector.app.features.discovery.DiscoverySharedViewModel
import kotlinx.android.synthetic.main.fragment_set_identity_server.*
import org.matrix.android.sdk.api.session.terms.TermsService import org.matrix.android.sdk.api.session.terms.TermsService
import javax.inject.Inject import javax.inject.Inject
class SetIdentityServerFragment @Inject constructor( class SetIdentityServerFragment @Inject constructor(
val viewModelFactory: SetIdentityServerViewModel.Factory, val viewModelFactory: SetIdentityServerViewModel.Factory,
val colorProvider: ColorProvider val colorProvider: ColorProvider
) : VectorBaseFragment() { ) : VectorBaseFragment<FragmentSetIdentityServerBinding>() {
override fun getLayoutResId() = R.layout.fragment_set_identity_server override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSetIdentityServerBinding {
return FragmentSetIdentityServerBinding.inflate(inflater, container, false)
}
private val viewModel by fragmentViewModel(SetIdentityServerViewModel::class) private val viewModel by fragmentViewModel(SetIdentityServerViewModel::class)
@ -147,7 +153,7 @@ class SetIdentityServerFragment @Inject constructor(
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.identity_server) (activity as? AppCompatActivity)?.supportActionBar?.setTitle(R.string.identity_server)
} }
private val termsActivityResultLauncher = registerStartForActivityResult { private val termsActivityResultLauncher = registerStartForActivityResult {

View file

@ -18,7 +18,9 @@
package im.vector.app.features.grouplist package im.vector.app.features.grouplist
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.Incomplete import com.airbnb.mvrx.Incomplete
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
@ -29,28 +31,33 @@ import im.vector.app.core.extensions.configureWith
import im.vector.app.core.extensions.exhaustive import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.StateView import im.vector.app.core.platform.StateView
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentGenericRecyclerBinding
import im.vector.app.databinding.FragmentGroupListBinding
import im.vector.app.features.home.HomeActivitySharedAction import im.vector.app.features.home.HomeActivitySharedAction
import im.vector.app.features.home.HomeSharedActionViewModel import im.vector.app.features.home.HomeSharedActionViewModel
import kotlinx.android.synthetic.main.fragment_group_list.*
import org.matrix.android.sdk.api.session.group.model.GroupSummary import org.matrix.android.sdk.api.session.group.model.GroupSummary
import javax.inject.Inject import javax.inject.Inject
class GroupListFragment @Inject constructor( class GroupListFragment @Inject constructor(
val groupListViewModelFactory: GroupListViewModel.Factory, val groupListViewModelFactory: GroupListViewModel.Factory,
private val groupController: GroupSummaryController private val groupController: GroupSummaryController
) : VectorBaseFragment(), GroupSummaryController.Callback { ) : VectorBaseFragment<FragmentGroupListBinding>(),
GroupSummaryController.Callback {
private lateinit var sharedActionViewModel: HomeSharedActionViewModel private lateinit var sharedActionViewModel: HomeSharedActionViewModel
private val viewModel: GroupListViewModel by fragmentViewModel() private val viewModel: GroupListViewModel by fragmentViewModel()
override fun getLayoutResId() = R.layout.fragment_group_list override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentGroupListBinding {
return FragmentGroupListBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java) sharedActionViewModel = activityViewModelProvider.get(HomeSharedActionViewModel::class.java)
groupController.callback = this groupController.callback = this
stateView.contentView = groupListView views.stateView.contentView = views.groupListView
groupListView.configureWith(groupController) views.groupListView.configureWith(groupController)
viewModel.observeViewEvents { viewModel.observeViewEvents {
when (it) { when (it) {
is GroupListViewEvents.OpenGroupSummary -> sharedActionViewModel.post(HomeActivitySharedAction.OpenGroup) is GroupListViewEvents.OpenGroupSummary -> sharedActionViewModel.post(HomeActivitySharedAction.OpenGroup)
@ -60,14 +67,14 @@ class GroupListFragment @Inject constructor(
override fun onDestroyView() { override fun onDestroyView() {
groupController.callback = null groupController.callback = null
groupListView.cleanup() views.groupListView.cleanup()
super.onDestroyView() super.onDestroyView()
} }
override fun invalidate() = withState(viewModel) { state -> override fun invalidate() = withState(viewModel) { state ->
when (state.asyncGroups) { when (state.asyncGroups) {
is Incomplete -> stateView.state = StateView.State.Loading is Incomplete -> views.stateView.state = StateView.State.Loading
is Success -> stateView.state = StateView.State.Content is Success -> views.stateView.state = StateView.State.Content
} }
groupController.update(state) groupController.update(state)
} }

View file

@ -21,8 +21,10 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
@ -40,6 +42,7 @@ import im.vector.app.core.platform.ToolbarConfigurable
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.pushers.PushersManager import im.vector.app.core.pushers.PushersManager
import im.vector.app.core.utils.toast import im.vector.app.core.utils.toast
import im.vector.app.databinding.ActivityHomeBinding
import im.vector.app.features.disclaimer.showDisclaimerDialog import im.vector.app.features.disclaimer.showDisclaimerDialog
import im.vector.app.features.matrixto.MatrixToBottomSheet import im.vector.app.features.matrixto.MatrixToBottomSheet
import im.vector.app.features.notifications.NotificationDrawerManager import im.vector.app.features.notifications.NotificationDrawerManager
@ -56,9 +59,9 @@ import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
import im.vector.app.features.workers.signout.ServerBackupStatusViewState import im.vector.app.features.workers.signout.ServerBackupStatusViewState
import im.vector.app.push.fcm.FcmHelper import im.vector.app.push.fcm.FcmHelper
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.android.synthetic.main.activity_home.*
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
import org.matrix.android.sdk.api.session.InitialSyncProgressService import org.matrix.android.sdk.api.session.InitialSyncProgressService
import org.matrix.android.sdk.api.session.permalinks.PermalinkService import org.matrix.android.sdk.api.session.permalinks.PermalinkService
import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.MatrixItem
@ -71,7 +74,11 @@ data class HomeActivityArgs(
val accountCreation: Boolean val accountCreation: Boolean
) : Parcelable ) : Parcelable
class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDetectorSharedViewModel.Factory, ServerBackupStatusViewModel.Factory, class HomeActivity :
VectorBaseActivity<ActivityHomeBinding>(),
ToolbarConfigurable,
UnknownDeviceDetectorSharedViewModel.Factory,
ServerBackupStatusViewModel.Factory,
NavigationInterceptor { NavigationInterceptor {
private lateinit var sharedActionViewModel: HomeSharedActionViewModel private lateinit var sharedActionViewModel: HomeSharedActionViewModel
@ -98,7 +105,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
} }
} }
override fun getLayoutRes() = R.layout.activity_home override fun getBinding() = ActivityHomeBinding.inflate(layoutInflater)
override fun injectWith(injector: ScreenComponent) { override fun injectWith(injector: ScreenComponent) {
injector.inject(this) injector.inject(this)
@ -116,7 +123,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
FcmHelper.ensureFcmTokenIsRetrieved(this, pushManager, vectorPreferences.areNotificationEnabledForDevice()) FcmHelper.ensureFcmTokenIsRetrieved(this, pushManager, vectorPreferences.areNotificationEnabledForDevice())
sharedActionViewModel = viewModelProvider.get(HomeSharedActionViewModel::class.java) sharedActionViewModel = viewModelProvider.get(HomeSharedActionViewModel::class.java)
drawerLayout.addDrawerListener(drawerListener) views.drawerLayout.addDrawerListener(drawerListener)
if (isFirstCreation()) { if (isFirstCreation()) {
replaceFragment(R.id.homeDetailFragmentContainer, LoadingFragment::class.java) replaceFragment(R.id.homeDetailFragmentContainer, LoadingFragment::class.java)
replaceFragment(R.id.homeDrawerFragmentContainer, HomeDrawerFragment::class.java) replaceFragment(R.id.homeDrawerFragmentContainer, HomeDrawerFragment::class.java)
@ -126,10 +133,10 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
.observe() .observe()
.subscribe { sharedAction -> .subscribe { sharedAction ->
when (sharedAction) { when (sharedAction) {
is HomeActivitySharedAction.OpenDrawer -> drawerLayout.openDrawer(GravityCompat.START) is HomeActivitySharedAction.OpenDrawer -> views.drawerLayout.openDrawer(GravityCompat.START)
is HomeActivitySharedAction.CloseDrawer -> drawerLayout.closeDrawer(GravityCompat.START) is HomeActivitySharedAction.CloseDrawer -> views.drawerLayout.closeDrawer(GravityCompat.START)
is HomeActivitySharedAction.OpenGroup -> { is HomeActivitySharedAction.OpenGroup -> {
drawerLayout.closeDrawer(GravityCompat.START) views.drawerLayout.closeDrawer(GravityCompat.START)
replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java, allowStateLoss = true) replaceFragment(R.id.homeDetailFragmentContainer, HomeDetailFragment::class.java, allowStateLoss = true)
} }
}.exhaustive }.exhaustive
@ -197,24 +204,24 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
private fun renderState(state: HomeActivityViewState) { private fun renderState(state: HomeActivityViewState) {
when (val status = state.initialSyncProgressServiceStatus) { when (val status = state.initialSyncProgressServiceStatus) {
is InitialSyncProgressService.Status.Idle -> { is InitialSyncProgressService.Status.Idle -> {
waiting_view.isVisible = false views.waitingView.root.isVisible = false
} }
is InitialSyncProgressService.Status.Progressing -> { is InitialSyncProgressService.Status.Progressing -> {
Timber.v("${getString(status.statusText)} ${status.percentProgress}") Timber.v("${getString(status.statusText)} ${status.percentProgress}")
waiting_view.setOnClickListener { views.waitingView.root.setOnClickListener {
// block interactions // block interactions
} }
waitingHorizontalProgress.apply { views.waitingView.waitingHorizontalProgress.apply {
isIndeterminate = false isIndeterminate = false
max = 100 max = 100
progress = status.percentProgress progress = status.percentProgress
isVisible = true isVisible = true
} }
waitingStatusText.apply { views.waitingView.waitingStatusText.apply {
text = getString(status.statusText) text = getString(status.statusText)
isVisible = true isVisible = true
} }
waiting_view.isVisible = true views.waitingView.root.isVisible = true
} }
}.exhaustive }.exhaustive
} }
@ -269,7 +276,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
).apply { ).apply {
colorInt = ThemeUtils.getColor(this@HomeActivity, R.attr.vctr_notice_secondary) colorInt = ThemeUtils.getColor(this@HomeActivity, R.attr.vctr_notice_secondary)
contentAction = Runnable { contentAction = Runnable {
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let { (weakCurrentActivity?.get() as? VectorBaseActivity<*>)?.let {
// action(it) // action(it)
homeActivityViewModel.handle(HomeActivityViewActions.PushPromptHasBeenReviewed) homeActivityViewModel.handle(HomeActivityViewActions.PushPromptHasBeenReviewed)
it.navigator.openSettings(it, VectorSettingsActivity.EXTRA_DIRECT_ACCESS_NOTIFICATIONS) it.navigator.openSettings(it, VectorSettingsActivity.EXTRA_DIRECT_ACCESS_NOTIFICATIONS)
@ -282,7 +289,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
homeActivityViewModel.handle(HomeActivityViewActions.PushPromptHasBeenReviewed) homeActivityViewModel.handle(HomeActivityViewActions.PushPromptHasBeenReviewed)
}, true) }, true)
addButton(getString(R.string.settings), Runnable { addButton(getString(R.string.settings), Runnable {
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let { (weakCurrentActivity?.get() as? VectorBaseActivity<*>)?.let {
// action(it) // action(it)
homeActivityViewModel.handle(HomeActivityViewActions.PushPromptHasBeenReviewed) homeActivityViewModel.handle(HomeActivityViewActions.PushPromptHasBeenReviewed)
it.navigator.openSettings(it, VectorSettingsActivity.EXTRA_DIRECT_ACCESS_NOTIFICATIONS) it.navigator.openSettings(it, VectorSettingsActivity.EXTRA_DIRECT_ACCESS_NOTIFICATIONS)
@ -292,7 +299,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
) )
} }
private fun promptSecurityEvent(userItem: MatrixItem.UserItem?, titleRes: Int, descRes: Int, action: ((VectorBaseActivity) -> Unit)) { private fun promptSecurityEvent(userItem: MatrixItem.UserItem?, titleRes: Int, descRes: Int, action: ((VectorBaseActivity<*>) -> Unit)) {
popupAlertManager.postVectorAlert( popupAlertManager.postVectorAlert(
VerificationVectorAlert( VerificationVectorAlert(
uid = "upgradeSecurity", uid = "upgradeSecurity",
@ -303,7 +310,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
).apply { ).apply {
colorInt = ContextCompat.getColor(this@HomeActivity, R.color.riotx_positive_accent) colorInt = ContextCompat.getColor(this@HomeActivity, R.color.riotx_positive_accent)
contentAction = Runnable { contentAction = Runnable {
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let { (weakCurrentActivity?.get() as? VectorBaseActivity<*>)?.let {
action(it) action(it)
} }
} }
@ -321,7 +328,7 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
} }
override fun onDestroy() { override fun onDestroy() {
drawerLayout.removeDrawerListener(drawerListener) views.drawerLayout.removeDrawerListener(drawerListener)
super.onDestroy() super.onDestroy()
} }
@ -375,8 +382,8 @@ class HomeActivity : VectorBaseActivity(), ToolbarConfigurable, UnknownDeviceDet
} }
override fun onBackPressed() { override fun onBackPressed() {
if (drawerLayout.isDrawerOpen(GravityCompat.START)) { if (views.drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawerLayout.closeDrawer(GravityCompat.START) views.drawerLayout.closeDrawer(GravityCompat.START)
} else { } else {
super.onBackPressed() super.onBackPressed()
} }

View file

@ -17,7 +17,9 @@
package im.vector.app.features.home package im.vector.app.features.home
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import com.airbnb.mvrx.activityViewModel import com.airbnb.mvrx.activityViewModel
@ -33,6 +35,8 @@ import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.ui.views.ActiveCallView import im.vector.app.core.ui.views.ActiveCallView
import im.vector.app.core.ui.views.ActiveCallViewHolder import im.vector.app.core.ui.views.ActiveCallViewHolder
import im.vector.app.core.ui.views.KeysBackupBanner import im.vector.app.core.ui.views.KeysBackupBanner
import im.vector.app.databinding.FragmentGenericRecyclerBinding
import im.vector.app.databinding.FragmentHomeDetailBinding
import im.vector.app.features.call.SharedActiveCallViewModel import im.vector.app.features.call.SharedActiveCallViewModel
import im.vector.app.features.call.VectorCallActivity import im.vector.app.features.call.VectorCallActivity
import im.vector.app.features.call.WebRtcPeerConnectionManager import im.vector.app.features.call.WebRtcPeerConnectionManager
@ -46,7 +50,7 @@ import im.vector.app.features.themes.ThemeUtils
import im.vector.app.features.workers.signout.BannerState import im.vector.app.features.workers.signout.BannerState
import im.vector.app.features.workers.signout.ServerBackupStatusViewModel import im.vector.app.features.workers.signout.ServerBackupStatusViewModel
import im.vector.app.features.workers.signout.ServerBackupStatusViewState import im.vector.app.features.workers.signout.ServerBackupStatusViewState
import kotlinx.android.synthetic.main.fragment_home_detail.*
import org.matrix.android.sdk.api.session.group.model.GroupSummary import org.matrix.android.sdk.api.session.group.model.GroupSummary
import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toMatrixItem
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
@ -64,7 +68,10 @@ class HomeDetailFragment @Inject constructor(
private val alertManager: PopupAlertManager, private val alertManager: PopupAlertManager,
private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager, private val webRtcPeerConnectionManager: WebRtcPeerConnectionManager,
private val vectorPreferences: VectorPreferences private val vectorPreferences: VectorPreferences
) : VectorBaseFragment(), KeysBackupBanner.Delegate, ActiveCallView.Callback, ServerBackupStatusViewModel.Factory { ) : VectorBaseFragment<FragmentHomeDetailBinding>(),
KeysBackupBanner.Delegate,
ActiveCallView.Callback,
ServerBackupStatusViewModel.Factory {
private val viewModel: HomeDetailViewModel by fragmentViewModel() private val viewModel: HomeDetailViewModel by fragmentViewModel()
private val unknownDeviceDetectorSharedViewModel: UnknownDeviceDetectorSharedViewModel by activityViewModel() private val unknownDeviceDetectorSharedViewModel: UnknownDeviceDetectorSharedViewModel by activityViewModel()
@ -73,7 +80,9 @@ class HomeDetailFragment @Inject constructor(
private lateinit var sharedActionViewModel: HomeSharedActionViewModel private lateinit var sharedActionViewModel: HomeSharedActionViewModel
private lateinit var sharedCallActionViewModel: SharedActiveCallViewModel private lateinit var sharedCallActionViewModel: SharedActiveCallViewModel
override fun getLayoutResId() = R.layout.fragment_home_detail override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentHomeDetailBinding {
return FragmentHomeDetailBinding.inflate(inflater, container, false)
}
private val activeCallViewHolder = ActiveCallViewHolder() private val activeCallViewHolder = ActiveCallViewHolder()
@ -89,7 +98,7 @@ class HomeDetailFragment @Inject constructor(
withState(viewModel) { withState(viewModel) {
// Update the navigation view if needed (for when we restore the tabs) // Update the navigation view if needed (for when we restore the tabs)
bottomNavigationView.selectedItemId = it.displayMode.toMenuId() views.bottomNavigationView.selectedItemId = it.displayMode.toMenuId()
} }
viewModel.selectSubscribe(this, HomeDetailViewState::groupSummary) { groupSummary -> viewModel.selectSubscribe(this, HomeDetailViewState::groupSummary) { groupSummary ->
@ -132,8 +141,8 @@ class HomeDetailFragment @Inject constructor(
} }
private fun checkNotificationTabStatus() { private fun checkNotificationTabStatus() {
val wasVisible = bottomNavigationView.menu.findItem(R.id.bottom_action_notification).isVisible val wasVisible = views.bottomNavigationView.menu.findItem(R.id.bottom_action_notification).isVisible
bottomNavigationView.menu.findItem(R.id.bottom_action_notification).isVisible = vectorPreferences.labAddNotificationTab() views.bottomNavigationView.menu.findItem(R.id.bottom_action_notification).isVisible = vectorPreferences.labAddNotificationTab()
if (wasVisible && !vectorPreferences.labAddNotificationTab()) { if (wasVisible && !vectorPreferences.labAddNotificationTab()) {
// As we hide it check if it's not the current item! // As we hide it check if it's not the current item!
withState(viewModel) { withState(viewModel) {
@ -156,7 +165,7 @@ class HomeDetailFragment @Inject constructor(
).apply { ).apply {
colorInt = ContextCompat.getColor(requireActivity(), R.color.riotx_accent) colorInt = ContextCompat.getColor(requireActivity(), R.color.riotx_accent)
contentAction = Runnable { contentAction = Runnable {
(weakCurrentActivity?.get() as? VectorBaseActivity) (weakCurrentActivity?.get() as? VectorBaseActivity<*>)
?.navigator ?.navigator
?.requestSessionVerification(requireContext(), newest.deviceId ?: "") ?.requestSessionVerification(requireContext(), newest.deviceId ?: "")
unknownDeviceDetectorSharedViewModel.handle( unknownDeviceDetectorSharedViewModel.handle(
@ -184,7 +193,7 @@ class HomeDetailFragment @Inject constructor(
).apply { ).apply {
colorInt = ContextCompat.getColor(requireActivity(), R.color.riotx_accent) colorInt = ContextCompat.getColor(requireActivity(), R.color.riotx_accent)
contentAction = Runnable { contentAction = Runnable {
(weakCurrentActivity?.get() as? VectorBaseActivity)?.let { (weakCurrentActivity?.get() as? VectorBaseActivity<*>)?.let {
// mark as ignored to avoid showing it again // mark as ignored to avoid showing it again
unknownDeviceDetectorSharedViewModel.handle( unknownDeviceDetectorSharedViewModel.handle(
UnknownDeviceDetectorSharedViewModel.Action.IgnoreDevice(oldUnverified.mapNotNull { it.deviceId }) UnknownDeviceDetectorSharedViewModel.Action.IgnoreDevice(oldUnverified.mapNotNull { it.deviceId })
@ -204,7 +213,7 @@ class HomeDetailFragment @Inject constructor(
private fun onGroupChange(groupSummary: GroupSummary?) { private fun onGroupChange(groupSummary: GroupSummary?) {
groupSummary?.let { groupSummary?.let {
// Use GlideApp with activity context to avoid the glideRequests to be paused // Use GlideApp with activity context to avoid the glideRequests to be paused
avatarRenderer.render(it.toMatrixItem(), groupToolbarAvatarImageView, GlideApp.with(requireActivity())) avatarRenderer.render(it.toMatrixItem(), views.groupToolbarAvatarImageView, GlideApp.with(requireActivity()))
} }
} }
@ -212,20 +221,20 @@ class HomeDetailFragment @Inject constructor(
serverBackupStatusViewModel serverBackupStatusViewModel
.subscribe(this) { .subscribe(this) {
when (val banState = it.bannerState.invoke()) { when (val banState = it.bannerState.invoke()) {
is BannerState.Setup -> homeKeysBackupBanner.render(KeysBackupBanner.State.Setup(banState.numberOfKeys), false) is BannerState.Setup -> views.homeKeysBackupBanner.render(KeysBackupBanner.State.Setup(banState.numberOfKeys), false)
BannerState.BackingUp -> homeKeysBackupBanner.render(KeysBackupBanner.State.BackingUp, false) BannerState.BackingUp -> views.homeKeysBackupBanner.render(KeysBackupBanner.State.BackingUp, false)
null, null,
BannerState.Hidden -> homeKeysBackupBanner.render(KeysBackupBanner.State.Hidden, false) BannerState.Hidden -> views.homeKeysBackupBanner.render(KeysBackupBanner.State.Hidden, false)
} }
} }
homeKeysBackupBanner.delegate = this views.homeKeysBackupBanner.delegate = this
} }
private fun setupActiveCallView() { private fun setupActiveCallView() {
activeCallViewHolder.bind( activeCallViewHolder.bind(
activeCallPiP, views.activeCallPiP,
activeCallView, views.activeCallView,
activeCallPiPWrap, views.activeCallPiPWrap,
this this
) )
} }
@ -233,17 +242,17 @@ class HomeDetailFragment @Inject constructor(
private fun setupToolbar() { private fun setupToolbar() {
val parentActivity = vectorBaseActivity val parentActivity = vectorBaseActivity
if (parentActivity is ToolbarConfigurable) { if (parentActivity is ToolbarConfigurable) {
parentActivity.configure(groupToolbar) parentActivity.configure(views.groupToolbar)
} }
groupToolbar.title = "" views.groupToolbar.title = ""
groupToolbarAvatarImageView.debouncedClicks { views.groupToolbarAvatarImageView.debouncedClicks {
sharedActionViewModel.post(HomeActivitySharedAction.OpenDrawer) sharedActionViewModel.post(HomeActivitySharedAction.OpenDrawer)
} }
} }
private fun setupBottomNavigationView() { private fun setupBottomNavigationView() {
bottomNavigationView.menu.findItem(R.id.bottom_action_notification).isVisible = vectorPreferences.labAddNotificationTab() views.bottomNavigationView.menu.findItem(R.id.bottom_action_notification).isVisible = vectorPreferences.labAddNotificationTab()
bottomNavigationView.setOnNavigationItemSelectedListener { views.bottomNavigationView.setOnNavigationItemSelectedListener {
val displayMode = when (it.itemId) { val displayMode = when (it.itemId) {
R.id.bottom_action_people -> RoomListDisplayMode.PEOPLE R.id.bottom_action_people -> RoomListDisplayMode.PEOPLE
R.id.bottom_action_rooms -> RoomListDisplayMode.ROOMS R.id.bottom_action_rooms -> RoomListDisplayMode.ROOMS
@ -266,7 +275,7 @@ class HomeDetailFragment @Inject constructor(
} }
private fun switchDisplayMode(displayMode: RoomListDisplayMode) { private fun switchDisplayMode(displayMode: RoomListDisplayMode) {
groupToolbarTitleView.setText(displayMode.titleRes) views.groupToolbarTitleView.setText(displayMode.titleRes)
updateSelectedFragment(displayMode) updateSelectedFragment(displayMode)
} }
@ -302,10 +311,10 @@ class HomeDetailFragment @Inject constructor(
override fun invalidate() = withState(viewModel) { override fun invalidate() = withState(viewModel) {
Timber.v(it.toString()) Timber.v(it.toString())
bottomNavigationView.getOrCreateBadge(R.id.bottom_action_people).render(it.notificationCountPeople, it.notificationHighlightPeople) views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_people).render(it.notificationCountPeople, it.notificationHighlightPeople)
bottomNavigationView.getOrCreateBadge(R.id.bottom_action_rooms).render(it.notificationCountRooms, it.notificationHighlightRooms) views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_rooms).render(it.notificationCountRooms, it.notificationHighlightRooms)
bottomNavigationView.getOrCreateBadge(R.id.bottom_action_notification).render(it.notificationCountCatchup, it.notificationHighlightCatchup) views.bottomNavigationView.getOrCreateBadge(R.id.bottom_action_notification).render(it.notificationCountCatchup, it.notificationHighlightCatchup)
syncStateView.render(it.syncState) views.syncStateView.render(it.syncState)
} }
private fun BadgeDrawable.render(count: Int, highlight: Boolean) { private fun BadgeDrawable.render(count: Int, highlight: Boolean) {

View file

@ -17,7 +17,9 @@
package im.vector.app.features.home package im.vector.app.features.home
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.core.app.ActivityOptionsCompat import androidx.core.app.ActivityOptionsCompat
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -27,12 +29,14 @@ import im.vector.app.core.extensions.observeK
import im.vector.app.core.extensions.replaceChildFragment import im.vector.app.core.extensions.replaceChildFragment
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.startSharePlainTextIntent import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.databinding.FragmentGenericRecyclerBinding
import im.vector.app.databinding.FragmentHomeDrawerBinding
import im.vector.app.features.grouplist.GroupListFragment import im.vector.app.features.grouplist.GroupListFragment
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import im.vector.app.features.settings.VectorSettingsActivity import im.vector.app.features.settings.VectorSettingsActivity
import im.vector.app.features.usercode.UserCodeActivity import im.vector.app.features.usercode.UserCodeActivity
import im.vector.app.features.workers.signout.SignOutUiWorker import im.vector.app.features.workers.signout.SignOutUiWorker
import kotlinx.android.synthetic.main.fragment_home_drawer.*
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toMatrixItem
import javax.inject.Inject import javax.inject.Inject
@ -41,11 +45,13 @@ class HomeDrawerFragment @Inject constructor(
private val session: Session, private val session: Session,
private val vectorPreferences: VectorPreferences, private val vectorPreferences: VectorPreferences,
private val avatarRenderer: AvatarRenderer private val avatarRenderer: AvatarRenderer
) : VectorBaseFragment() { ) : VectorBaseFragment<FragmentHomeDrawerBinding>() {
private lateinit var sharedActionViewModel: HomeSharedActionViewModel private lateinit var sharedActionViewModel: HomeSharedActionViewModel
override fun getLayoutResId() = R.layout.fragment_home_drawer override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentHomeDrawerBinding {
return FragmentHomeDrawerBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -58,40 +64,40 @@ class HomeDrawerFragment @Inject constructor(
session.getUserLive(session.myUserId).observeK(viewLifecycleOwner) { optionalUser -> session.getUserLive(session.myUserId).observeK(viewLifecycleOwner) { optionalUser ->
val user = optionalUser?.getOrNull() val user = optionalUser?.getOrNull()
if (user != null) { if (user != null) {
avatarRenderer.render(user.toMatrixItem(), homeDrawerHeaderAvatarView) avatarRenderer.render(user.toMatrixItem(), views.homeDrawerHeaderAvatarView)
homeDrawerUsernameView.text = user.displayName views.homeDrawerUsernameView.text = user.displayName
homeDrawerUserIdView.text = user.userId views.homeDrawerUserIdView.text = user.userId
} }
} }
// Profile // Profile
homeDrawerHeader.debouncedClicks { views.homeDrawerHeader.debouncedClicks {
sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer) sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer)
navigator.openSettings(requireActivity(), directAccess = VectorSettingsActivity.EXTRA_DIRECT_ACCESS_GENERAL) navigator.openSettings(requireActivity(), directAccess = VectorSettingsActivity.EXTRA_DIRECT_ACCESS_GENERAL)
} }
// Settings // Settings
homeDrawerHeaderSettingsView.debouncedClicks { views.homeDrawerHeaderSettingsView.debouncedClicks {
sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer) sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer)
navigator.openSettings(requireActivity()) navigator.openSettings(requireActivity())
} }
// Sign out // Sign out
homeDrawerHeaderSignoutView.debouncedClicks { views.homeDrawerHeaderSignoutView.debouncedClicks {
sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer) sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer)
SignOutUiWorker(requireActivity()).perform() SignOutUiWorker(requireActivity()).perform()
} }
homeDrawerQRCodeButton.debouncedClicks { views.homeDrawerQRCodeButton.debouncedClicks {
UserCodeActivity.newIntent(requireContext(), sharedActionViewModel.session.myUserId).let { UserCodeActivity.newIntent(requireContext(), sharedActionViewModel.session.myUserId).let {
val options = val options =
ActivityOptionsCompat.makeSceneTransitionAnimation( ActivityOptionsCompat.makeSceneTransitionAnimation(
requireActivity(), requireActivity(),
homeDrawerHeaderAvatarView, views.homeDrawerHeaderAvatarView,
ViewCompat.getTransitionName(homeDrawerHeaderAvatarView) ?: "" ViewCompat.getTransitionName(views.homeDrawerHeaderAvatarView) ?: ""
) )
startActivity(it, options.toBundle()) startActivity(it, options.toBundle())
} }
} }
homeDrawerInviteFriendButton.debouncedClicks { views.homeDrawerInviteFriendButton.debouncedClicks {
session.permalinkService().createPermalink(sharedActionViewModel.session.myUserId)?.let { permalink -> session.permalinkService().createPermalink(sharedActionViewModel.session.myUserId)?.let { permalink ->
val text = getString(R.string.invite_friends_text, permalink) val text = getString(R.string.invite_friends_text, permalink)
@ -106,8 +112,8 @@ class HomeDrawerFragment @Inject constructor(
} }
// Debug menu // Debug menu
homeDrawerHeaderDebugView.isVisible = BuildConfig.DEBUG && vectorPreferences.developerMode() views.homeDrawerHeaderDebugView.isVisible = BuildConfig.DEBUG && vectorPreferences.developerMode()
homeDrawerHeaderDebugView.debouncedClicks { views.homeDrawerHeaderDebugView.debouncedClicks {
sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer) sharedActionViewModel.post(HomeActivitySharedAction.CloseDrawer)
navigator.openDebug(requireActivity()) navigator.openDebug(requireActivity())
} }

View file

@ -18,20 +18,26 @@ package im.vector.app.features.home
import android.graphics.drawable.AnimationDrawable import android.graphics.drawable.AnimationDrawable
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import kotlinx.android.synthetic.main.fragment_loading.* import im.vector.app.databinding.FragmentGenericRecyclerBinding
import im.vector.app.databinding.FragmentLoadingBinding
import javax.inject.Inject import javax.inject.Inject
class LoadingFragment @Inject constructor() : VectorBaseFragment() { class LoadingFragment @Inject constructor() : VectorBaseFragment<FragmentLoadingBinding>() {
override fun getLayoutResId() = R.layout.fragment_loading override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLoadingBinding {
return FragmentLoadingBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val background = animatedLogoImageView.background val background = views.animatedLogoImageView.background
if (background is AnimationDrawable) { if (background is AnimationDrawable) {
background.start() background.start()
} }

View file

@ -17,27 +17,34 @@
package im.vector.app.features.home.room.breadcrumbs package im.vector.app.features.home.room.breadcrumbs
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentBreadcrumbsBinding
import im.vector.app.databinding.FragmentGenericRecyclerBinding
import im.vector.app.features.home.room.detail.RoomDetailSharedAction import im.vector.app.features.home.room.detail.RoomDetailSharedAction
import im.vector.app.features.home.room.detail.RoomDetailSharedActionViewModel import im.vector.app.features.home.room.detail.RoomDetailSharedActionViewModel
import kotlinx.android.synthetic.main.fragment_breadcrumbs.*
import javax.inject.Inject import javax.inject.Inject
class BreadcrumbsFragment @Inject constructor( class BreadcrumbsFragment @Inject constructor(
private val breadcrumbsController: BreadcrumbsController, private val breadcrumbsController: BreadcrumbsController,
val breadcrumbsViewModelFactory: BreadcrumbsViewModel.Factory val breadcrumbsViewModelFactory: BreadcrumbsViewModel.Factory
) : VectorBaseFragment(), BreadcrumbsController.Listener { ) : VectorBaseFragment<FragmentBreadcrumbsBinding>(),
BreadcrumbsController.Listener {
private lateinit var sharedActionViewModel: RoomDetailSharedActionViewModel private lateinit var sharedActionViewModel: RoomDetailSharedActionViewModel
private val breadcrumbsViewModel: BreadcrumbsViewModel by fragmentViewModel() private val breadcrumbsViewModel: BreadcrumbsViewModel by fragmentViewModel()
override fun getLayoutResId() = R.layout.fragment_breadcrumbs override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBreadcrumbsBinding {
return FragmentBreadcrumbsBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -46,13 +53,13 @@ class BreadcrumbsFragment @Inject constructor(
} }
override fun onDestroyView() { override fun onDestroyView() {
breadcrumbsRecyclerView.cleanup() views.breadcrumbsRecyclerView.cleanup()
breadcrumbsController.listener = null breadcrumbsController.listener = null
super.onDestroyView() super.onDestroyView()
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {
breadcrumbsRecyclerView.configureWith(breadcrumbsController, BreadcrumbsAnimator(), hasFixedSize = false) views.breadcrumbsRecyclerView.configureWith(breadcrumbsController, BreadcrumbsAnimator(), hasFixedSize = false)
breadcrumbsController.listener = this breadcrumbsController.listener = this
} }
@ -67,6 +74,6 @@ class BreadcrumbsFragment @Inject constructor(
} }
fun scrollToTop() { fun scrollToTop() {
breadcrumbsRecyclerView.scrollToPosition(0) views.breadcrumbsRecyclerView.scrollToPosition(0)
} }
} }

View file

@ -37,8 +37,8 @@ import im.vector.app.features.room.RequireActiveMembershipViewModel
import im.vector.app.features.room.RequireActiveMembershipViewState import im.vector.app.features.room.RequireActiveMembershipViewState
import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewModel import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewModel
import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewState import im.vector.app.features.widgets.permissions.RoomWidgetPermissionViewState
import kotlinx.android.synthetic.main.activity_room_detail.*
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
import javax.inject.Inject import javax.inject.Inject
class RoomDetailActivity : class RoomDetailActivity :

View file

@ -167,10 +167,10 @@ import im.vector.app.features.widgets.WidgetKind
import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet import im.vector.app.features.widgets.permissions.RoomWidgetPermissionBottomSheet
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.android.synthetic.main.fragment_room_detail.*
import kotlinx.android.synthetic.main.composer_layout.view.*
import kotlinx.android.synthetic.main.merge_overlay_waiting_view.*
import nl.dionsegijn.konfetti.models.Shape import nl.dionsegijn.konfetti.models.Shape
import nl.dionsegijn.konfetti.models.Size import nl.dionsegijn.konfetti.models.Size
import org.billcarsonfr.jsonviewer.JSonViewerDialog import org.billcarsonfr.jsonviewer.JSonViewerDialog

View file

@ -31,7 +31,7 @@ import androidx.transition.Transition
import androidx.transition.TransitionManager import androidx.transition.TransitionManager
import androidx.transition.TransitionSet import androidx.transition.TransitionSet
import im.vector.app.R import im.vector.app.R
import kotlinx.android.synthetic.main.composer_layout.view.*
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
/** /**

View file

@ -18,7 +18,9 @@ package im.vector.app.features.home.room.detail.readreceipts
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.args import com.airbnb.mvrx.args
import im.vector.app.R import im.vector.app.R
@ -26,11 +28,13 @@ import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.BottomSheetGenericListBinding
import im.vector.app.databinding.BottomSheetGenericListWithTitleBinding
import im.vector.app.features.home.room.detail.timeline.action.EventSharedAction import im.vector.app.features.home.room.detail.timeline.action.EventSharedAction
import im.vector.app.features.home.room.detail.timeline.action.MessageSharedActionViewModel import im.vector.app.features.home.room.detail.timeline.action.MessageSharedActionViewModel
import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData import im.vector.app.features.home.room.detail.timeline.item.ReadReceiptData
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.android.synthetic.main.bottom_sheet_generic_list_with_title.*
import javax.inject.Inject import javax.inject.Inject
@Parcelize @Parcelize
@ -41,7 +45,9 @@ data class DisplayReadReceiptArgs(
/** /**
* Bottom sheet displaying list of read receipts for a given event ordered by descending timestamp * Bottom sheet displaying list of read receipts for a given event ordered by descending timestamp
*/ */
class DisplayReadReceiptsBottomSheet : VectorBaseBottomSheetDialogFragment(), DisplayReadReceiptsController.Listener { class DisplayReadReceiptsBottomSheet :
VectorBaseBottomSheetDialogFragment<BottomSheetGenericListWithTitleBinding>(),
DisplayReadReceiptsController.Listener {
@Inject lateinit var epoxyController: DisplayReadReceiptsController @Inject lateinit var epoxyController: DisplayReadReceiptsController
@ -53,19 +59,21 @@ class DisplayReadReceiptsBottomSheet : VectorBaseBottomSheetDialogFragment(), Di
injector.inject(this) injector.inject(this)
} }
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetGenericListWithTitleBinding {
return BottomSheetGenericListWithTitleBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
bottomSheetRecyclerView.configureWith(epoxyController, hasFixedSize = false) views.bottomSheetRecyclerView.configureWith(epoxyController, hasFixedSize = false)
bottomSheetTitle.text = getString(R.string.seen_by) views.bottomSheetTitle.text = getString(R.string.seen_by)
epoxyController.listener = this epoxyController.listener = this
epoxyController.setData(displayReadReceiptArgs.readReceipts) epoxyController.setData(displayReadReceiptArgs.readReceipts)
} }
override fun onDestroyView() { override fun onDestroyView() {
bottomSheetRecyclerView.cleanup() views.bottomSheetRecyclerView.cleanup()
epoxyController.listener = null epoxyController.listener = null
super.onDestroyView() super.onDestroyView()
} }

View file

@ -19,22 +19,23 @@ package im.vector.app.features.home.room.detail.search
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MvRx
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ScreenComponent import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.addFragment import im.vector.app.core.extensions.addFragment
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import kotlinx.android.synthetic.main.activity_search.* import im.vector.app.databinding.ActivitySearchBinding
class SearchActivity : VectorBaseActivity() { class SearchActivity : VectorBaseActivity<ActivitySearchBinding>() {
private val searchFragment: SearchFragment? private val searchFragment: SearchFragment?
get() { get() {
return supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? SearchFragment return supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? SearchFragment
} }
override fun getLayoutRes() = R.layout.activity_search override fun getBinding() = ActivitySearchBinding.inflate(layoutInflater)
override fun injectWith(injector: ScreenComponent) { override fun injectWith(injector: ScreenComponent) {
super.injectWith(injector) super.injectWith(injector)
@ -43,7 +44,7 @@ class SearchActivity : VectorBaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
configureToolbar(searchToolbar) configureToolbar(views.searchToolbar)
} }
override fun initUiAndData() { override fun initUiAndData() {
@ -51,7 +52,7 @@ class SearchActivity : VectorBaseActivity() {
val fragmentArgs: SearchArgs = intent?.extras?.getParcelable(MvRx.KEY_ARG) ?: return val fragmentArgs: SearchArgs = intent?.extras?.getParcelable(MvRx.KEY_ARG) ?: return
addFragment(R.id.searchFragmentContainer, SearchFragment::class.java, fragmentArgs, FRAGMENT_TAG) addFragment(R.id.searchFragmentContainer, SearchFragment::class.java, fragmentArgs, FRAGMENT_TAG)
} }
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { views.searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean { override fun onQueryTextSubmit(query: String): Boolean {
searchFragment?.search(query) searchFragment?.search(query)
return true return true
@ -62,7 +63,7 @@ class SearchActivity : VectorBaseActivity() {
} }
}) })
// Open the keyboard immediately // Open the keyboard immediately
searchView.requestFocus() views.searchView.requestFocus()
} }
companion object { companion object {

View file

@ -18,7 +18,9 @@ package im.vector.app.features.home.room.detail.search
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.airbnb.mvrx.Fail import com.airbnb.mvrx.Fail
@ -34,8 +36,10 @@ import im.vector.app.core.extensions.hideKeyboard
import im.vector.app.core.extensions.trackItemsVisibilityChange import im.vector.app.core.extensions.trackItemsVisibilityChange
import im.vector.app.core.platform.StateView import im.vector.app.core.platform.StateView
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import kotlinx.android.parcel.Parcelize import im.vector.app.databinding.FragmentGenericRecyclerBinding
import kotlinx.android.synthetic.main.fragment_search.* import im.vector.app.databinding.FragmentSearchBinding
import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import javax.inject.Inject import javax.inject.Inject
@ -47,32 +51,36 @@ data class SearchArgs(
class SearchFragment @Inject constructor( class SearchFragment @Inject constructor(
val viewModelFactory: SearchViewModel.Factory, val viewModelFactory: SearchViewModel.Factory,
private val controller: SearchResultController private val controller: SearchResultController
) : VectorBaseFragment(), StateView.EventCallback, SearchResultController.Listener { ) : VectorBaseFragment<FragmentSearchBinding>(),
StateView.EventCallback,
SearchResultController.Listener {
private val fragmentArgs: SearchArgs by args() private val fragmentArgs: SearchArgs by args()
private val searchViewModel: SearchViewModel by fragmentViewModel() private val searchViewModel: SearchViewModel by fragmentViewModel()
override fun getLayoutResId() = R.layout.fragment_search override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSearchBinding {
return FragmentSearchBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
stateView.contentView = searchResultRecycler views.stateView.contentView = views.searchResultRecycler
stateView.eventCallback = this views.stateView.eventCallback = this
configureRecyclerView() configureRecyclerView()
} }
private fun configureRecyclerView() { private fun configureRecyclerView() {
searchResultRecycler.trackItemsVisibilityChange() views.searchResultRecycler.trackItemsVisibilityChange()
searchResultRecycler.configureWith(controller, showDivider = false) views.searchResultRecycler.configureWith(controller, showDivider = false)
(searchResultRecycler.layoutManager as? LinearLayoutManager)?.stackFromEnd = true (views.searchResultRecycler.layoutManager as? LinearLayoutManager)?.stackFromEnd = true
controller.listener = this controller.listener = this
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
searchResultRecycler?.cleanup() views.searchResultRecycler?.cleanup()
controller.listener = null controller.listener = null
} }
@ -80,20 +88,20 @@ class SearchFragment @Inject constructor(
if (state.searchResult.isNullOrEmpty()) { if (state.searchResult.isNullOrEmpty()) {
when (state.asyncSearchRequest) { when (state.asyncSearchRequest) {
is Loading -> { is Loading -> {
stateView.state = StateView.State.Loading views.stateView.state = StateView.State.Loading
} }
is Fail -> { is Fail -> {
stateView.state = StateView.State.Error(errorFormatter.toHumanReadable(state.asyncSearchRequest.error)) views.stateView.state = StateView.State.Error(errorFormatter.toHumanReadable(state.asyncSearchRequest.error))
} }
is Success -> { is Success -> {
stateView.state = StateView.State.Empty( views.stateView.state = StateView.State.Empty(
title = getString(R.string.search_no_results), title = getString(R.string.search_no_results),
image = ContextCompat.getDrawable(requireContext(), R.drawable.ic_search_no_results)) image = ContextCompat.getDrawable(requireContext(), R.drawable.ic_search_no_results))
} }
} }
} else { } else {
controller.setData(state) controller.setData(state)
stateView.state = StateView.State.Content views.stateView.state = StateView.State.Content
} }
} }

View file

@ -16,7 +16,9 @@
package im.vector.app.features.home.room.detail.timeline.action package im.vector.app.features.home.room.detail.timeline.action
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.app.R import im.vector.app.R
@ -24,14 +26,17 @@ import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.BottomSheetGenericListBinding
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
import kotlinx.android.synthetic.main.bottom_sheet_generic_list.*
import javax.inject.Inject import javax.inject.Inject
/** /**
* Bottom sheet fragment that shows a message preview with list of contextual actions * Bottom sheet fragment that shows a message preview with list of contextual actions
*/ */
class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), MessageActionsEpoxyController.MessageActionsEpoxyControllerListener { class MessageActionsBottomSheet :
VectorBaseBottomSheetDialogFragment<BottomSheetGenericListBinding>(),
MessageActionsEpoxyController.MessageActionsEpoxyControllerListener {
@Inject lateinit var messageActionViewModelFactory: MessageActionsViewModel.Factory @Inject lateinit var messageActionViewModelFactory: MessageActionsViewModel.Factory
@Inject lateinit var messageActionsEpoxyController: MessageActionsEpoxyController @Inject lateinit var messageActionsEpoxyController: MessageActionsEpoxyController
@ -46,17 +51,19 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message
injector.inject(this) injector.inject(this)
} }
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetGenericListBinding {
return BottomSheetGenericListBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
bottomSheetRecyclerView.configureWith(messageActionsEpoxyController, hasFixedSize = false, disableItemAnimation = true) views.bottomSheetRecyclerView.configureWith(messageActionsEpoxyController, hasFixedSize = false, disableItemAnimation = true)
messageActionsEpoxyController.listener = this messageActionsEpoxyController.listener = this
} }
override fun onDestroyView() { override fun onDestroyView() {
bottomSheetRecyclerView.cleanup() views.bottomSheetRecyclerView.cleanup()
super.onDestroyView() super.onDestroyView()
} }
@ -76,8 +83,8 @@ class MessageActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), Message
if (eventAction is EventSharedAction.ReportContent) { if (eventAction is EventSharedAction.ReportContent) {
// Toggle report menu // Toggle report menu
// Enable item animation // Enable item animation
if (bottomSheetRecyclerView.itemAnimator == null) { if (views.bottomSheetRecyclerView.itemAnimator == null) {
bottomSheetRecyclerView.itemAnimator = MessageActionsAnimator() views.bottomSheetRecyclerView.itemAnimator = MessageActionsAnimator()
} }
viewModel.handle(MessageActionsAction.ToggleReportMenu) viewModel.handle(MessageActionsAction.ToggleReportMenu)
} else { } else {

View file

@ -18,7 +18,7 @@ package im.vector.app.features.home.room.detail.timeline.action
import android.os.Parcelable import android.os.Parcelable
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
data class TimelineEventFragmentArgs( data class TimelineEventFragmentArgs(

View file

@ -16,7 +16,9 @@
package im.vector.app.features.home.room.detail.timeline.edithistory package im.vector.app.features.home.room.detail.timeline.edithistory
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
@ -25,16 +27,19 @@ import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.BottomSheetGenericListBinding
import im.vector.app.databinding.BottomSheetGenericListWithTitleBinding
import im.vector.app.features.home.room.detail.timeline.action.TimelineEventFragmentArgs import im.vector.app.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
import im.vector.app.features.html.EventHtmlRenderer import im.vector.app.features.html.EventHtmlRenderer
import kotlinx.android.synthetic.main.bottom_sheet_generic_list_with_title.*
import javax.inject.Inject import javax.inject.Inject
/** /**
* Bottom sheet displaying list of edits for a given event ordered by timestamp * Bottom sheet displaying list of edits for a given event ordered by timestamp
*/ */
class ViewEditHistoryBottomSheet : VectorBaseBottomSheetDialogFragment() { class ViewEditHistoryBottomSheet :
VectorBaseBottomSheetDialogFragment<BottomSheetGenericListWithTitleBinding>() {
private val viewModel: ViewEditHistoryViewModel by fragmentViewModel(ViewEditHistoryViewModel::class) private val viewModel: ViewEditHistoryViewModel by fragmentViewModel(ViewEditHistoryViewModel::class)
@ -49,19 +54,21 @@ class ViewEditHistoryBottomSheet : VectorBaseBottomSheetDialogFragment() {
injector.inject(this) injector.inject(this)
} }
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetGenericListWithTitleBinding {
return BottomSheetGenericListWithTitleBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
bottomSheetRecyclerView.configureWith( views.bottomSheetRecyclerView.configureWith(
epoxyController, epoxyController,
showDivider = true, showDivider = true,
hasFixedSize = false) hasFixedSize = false)
bottomSheetTitle.text = context?.getString(R.string.message_edits) views.bottomSheetTitle.text = context?.getString(R.string.message_edits)
} }
override fun onDestroyView() { override fun onDestroyView() {
bottomSheetRecyclerView.cleanup() views.bottomSheetRecyclerView.cleanup()
super.onDestroyView() super.onDestroyView()
} }

View file

@ -17,7 +17,7 @@
package im.vector.app.features.home.room.detail.timeline.item package im.vector.app.features.home.room.detail.timeline.item
import android.os.Parcelable import android.os.Parcelable
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.session.room.send.SendState import org.matrix.android.sdk.api.session.room.send.SendState
import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.MatrixItem
import org.matrix.android.sdk.internal.session.room.VerificationState import org.matrix.android.sdk.internal.session.room.VerificationState

View file

@ -23,7 +23,7 @@ import android.widget.LinearLayout
import androidx.core.content.withStyledAttributes import androidx.core.content.withStyledAttributes
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.setTextOrHide
import kotlinx.android.synthetic.main.item_timeline_event_poll_result_item.view.*
class PollResultLineView @JvmOverloads constructor( class PollResultLineView @JvmOverloads constructor(
context: Context, context: Context,

View file

@ -17,7 +17,9 @@
package im.vector.app.features.home.room.detail.timeline.reactions package im.vector.app.features.home.room.detail.timeline.reactions
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.MvRx import com.airbnb.mvrx.MvRx
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
@ -26,17 +28,21 @@ import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.BottomSheetGenericListBinding
import im.vector.app.databinding.BottomSheetGenericListWithTitleBinding
import im.vector.app.features.home.room.detail.timeline.action.EventSharedAction import im.vector.app.features.home.room.detail.timeline.action.EventSharedAction
import im.vector.app.features.home.room.detail.timeline.action.MessageSharedActionViewModel import im.vector.app.features.home.room.detail.timeline.action.MessageSharedActionViewModel
import im.vector.app.features.home.room.detail.timeline.action.TimelineEventFragmentArgs import im.vector.app.features.home.room.detail.timeline.action.TimelineEventFragmentArgs
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
import kotlinx.android.synthetic.main.bottom_sheet_generic_list_with_title.*
import javax.inject.Inject import javax.inject.Inject
/** /**
* Bottom sheet displaying list of reactions for a given event ordered by timestamp * Bottom sheet displaying list of reactions for a given event ordered by timestamp
*/ */
class ViewReactionsBottomSheet : VectorBaseBottomSheetDialogFragment(), ViewReactionsEpoxyController.Listener { class ViewReactionsBottomSheet :
VectorBaseBottomSheetDialogFragment<BottomSheetGenericListWithTitleBinding>(),
ViewReactionsEpoxyController.Listener {
private val viewModel: ViewReactionsViewModel by fragmentViewModel(ViewReactionsViewModel::class) private val viewModel: ViewReactionsViewModel by fragmentViewModel(ViewReactionsViewModel::class)
@ -49,18 +55,20 @@ class ViewReactionsBottomSheet : VectorBaseBottomSheetDialogFragment(), ViewReac
injector.inject(this) injector.inject(this)
} }
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetGenericListWithTitleBinding {
return BottomSheetGenericListWithTitleBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java) sharedActionViewModel = activityViewModelProvider.get(MessageSharedActionViewModel::class.java)
bottomSheetRecyclerView.configureWith(epoxyController, hasFixedSize = false, showDivider = true) views.bottomSheetRecyclerView.configureWith(epoxyController, hasFixedSize = false, showDivider = true)
bottomSheetTitle.text = context?.getString(R.string.reactions) views.bottomSheetTitle.text = context?.getString(R.string.reactions)
epoxyController.listener = this epoxyController.listener = this
} }
override fun onDestroyView() { override fun onDestroyView() {
bottomSheetRecyclerView.cleanup() views.bottomSheetRecyclerView.cleanup()
epoxyController.listener = null epoxyController.listener = null
super.onDestroyView() super.onDestroyView()
} }

View file

@ -25,7 +25,7 @@ import im.vector.app.R
import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.features.home.room.detail.timeline.TimelineEventController import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.media.ImageContentRenderer
import kotlinx.android.synthetic.main.url_preview.view.*
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.media.PreviewUrlData import org.matrix.android.sdk.api.session.media.PreviewUrlData

View file

@ -21,7 +21,7 @@ import android.util.AttributeSet
import android.view.View import android.view.View
import android.widget.RelativeLayout import android.widget.RelativeLayout
import im.vector.app.R import im.vector.app.R
import kotlinx.android.synthetic.main.view_room_widgets_banner.view.*
import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.api.session.widgets.model.Widget
class RoomWidgetsBannerView @JvmOverloads constructor( class RoomWidgetsBannerView @JvmOverloads constructor(

View file

@ -17,7 +17,9 @@
package im.vector.app.features.home.room.detail.widget package im.vector.app.features.home.room.detail.widget
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import im.vector.app.R import im.vector.app.R
@ -26,18 +28,22 @@ import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.core.resources.ColorProvider import im.vector.app.core.resources.ColorProvider
import im.vector.app.databinding.BottomSheetGenericListBinding
import im.vector.app.databinding.BottomSheetGenericListWithTitleBinding
import im.vector.app.features.home.room.detail.RoomDetailAction import im.vector.app.features.home.room.detail.RoomDetailAction
import im.vector.app.features.home.room.detail.RoomDetailViewModel import im.vector.app.features.home.room.detail.RoomDetailViewModel
import im.vector.app.features.home.room.detail.RoomDetailViewState import im.vector.app.features.home.room.detail.RoomDetailViewState
import im.vector.app.features.navigation.Navigator import im.vector.app.features.navigation.Navigator
import kotlinx.android.synthetic.main.bottom_sheet_generic_list_with_title.*
import org.matrix.android.sdk.api.session.widgets.model.Widget import org.matrix.android.sdk.api.session.widgets.model.Widget
import javax.inject.Inject import javax.inject.Inject
/** /**
* Bottom sheet displaying active widgets in a room * Bottom sheet displaying active widgets in a room
*/ */
class RoomWidgetsBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomWidgetsController.Listener { class RoomWidgetsBottomSheet :
VectorBaseBottomSheetDialogFragment<BottomSheetGenericListWithTitleBinding>(),
RoomWidgetsController.Listener {
@Inject lateinit var epoxyController: RoomWidgetsController @Inject lateinit var epoxyController: RoomWidgetsController
@Inject lateinit var colorProvider: ColorProvider @Inject lateinit var colorProvider: ColorProvider
@ -49,14 +55,16 @@ class RoomWidgetsBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomWidget
injector.inject(this) injector.inject(this)
} }
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetGenericListWithTitleBinding {
return BottomSheetGenericListWithTitleBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
bottomSheetRecyclerView.configureWith(epoxyController, hasFixedSize = false) views.bottomSheetRecyclerView.configureWith(epoxyController, hasFixedSize = false)
bottomSheetTitle.text = getString(R.string.active_widgets_title) views.bottomSheetTitle.text = getString(R.string.active_widgets_title)
bottomSheetTitle.textSize = 20f views.bottomSheetTitle.textSize = 20f
bottomSheetTitle.setTextColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary)) views.bottomSheetTitle.setTextColor(colorProvider.getColorFromAttribute(R.attr.riotx_text_primary))
epoxyController.listener = this epoxyController.listener = this
roomDetailViewModel.asyncSubscribe(this, RoomDetailViewState::activeRoomWidgets) { roomDetailViewModel.asyncSubscribe(this, RoomDetailViewState::activeRoomWidgets) {
epoxyController.setData(it) epoxyController.setData(it)
@ -64,7 +72,7 @@ class RoomWidgetsBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomWidget
} }
override fun onDestroyView() { override fun onDestroyView() {
bottomSheetRecyclerView.cleanup() views.bottomSheetRecyclerView.cleanup()
epoxyController.listener = null epoxyController.listener = null
super.onDestroyView() super.onDestroyView()
} }

View file

@ -19,24 +19,26 @@ package im.vector.app.features.home.room.filtered
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.ScreenComponent import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.replaceFragment import im.vector.app.core.extensions.replaceFragment
import im.vector.app.core.platform.VectorBaseActivity import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.databinding.ActivityFilteredRoomsBinding
import im.vector.app.features.home.RoomListDisplayMode import im.vector.app.features.home.RoomListDisplayMode
import im.vector.app.features.home.room.list.RoomListFragment import im.vector.app.features.home.room.list.RoomListFragment
import im.vector.app.features.home.room.list.RoomListParams import im.vector.app.features.home.room.list.RoomListParams
import kotlinx.android.synthetic.main.activity_filtered_rooms.*
class FilteredRoomsActivity : VectorBaseActivity() {
class FilteredRoomsActivity : VectorBaseActivity<ActivityFilteredRoomsBinding>() {
private val roomListFragment: RoomListFragment? private val roomListFragment: RoomListFragment?
get() { get() {
return supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? RoomListFragment return supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) as? RoomListFragment
} }
override fun getLayoutRes() = R.layout.activity_filtered_rooms override fun getBinding() = ActivityFilteredRoomsBinding.inflate(layoutInflater)
override fun injectWith(injector: ScreenComponent) { override fun injectWith(injector: ScreenComponent) {
injector.inject(this) injector.inject(this)
@ -44,12 +46,12 @@ class FilteredRoomsActivity : VectorBaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
configureToolbar(filteredRoomsToolbar) configureToolbar(views.filteredRoomsToolbar)
if (isFirstCreation()) { if (isFirstCreation()) {
val params = RoomListParams(RoomListDisplayMode.FILTERED) val params = RoomListParams(RoomListDisplayMode.FILTERED)
replaceFragment(R.id.filteredRoomsFragmentContainer, RoomListFragment::class.java, params, FRAGMENT_TAG) replaceFragment(R.id.filteredRoomsFragmentContainer, RoomListFragment::class.java, params, FRAGMENT_TAG)
} }
filteredRoomsSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { views.filteredRoomsSearchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean { override fun onQueryTextSubmit(query: String): Boolean {
return true return true
} }
@ -60,7 +62,7 @@ class FilteredRoomsActivity : VectorBaseActivity() {
} }
}) })
// Open the keyboard immediately // Open the keyboard immediately
filteredRoomsSearchView.requestFocus() views.filteredRoomsSearchView.requestFocus()
} }
companion object { companion object {

View file

@ -18,9 +18,11 @@ package im.vector.app.features.home.room.list
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -40,6 +42,8 @@ import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.OnBackPressed import im.vector.app.core.platform.OnBackPressed
import im.vector.app.core.platform.StateView import im.vector.app.core.platform.StateView
import im.vector.app.core.platform.VectorBaseFragment import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.FragmentGenericRecyclerBinding
import im.vector.app.databinding.FragmentRoomListBinding
import im.vector.app.features.home.RoomListDisplayMode import im.vector.app.features.home.RoomListDisplayMode
import im.vector.app.features.home.room.list.actions.RoomListActionsArgs import im.vector.app.features.home.room.list.actions.RoomListActionsArgs
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet import im.vector.app.features.home.room.list.actions.RoomListQuickActionsBottomSheet
@ -47,8 +51,8 @@ import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedA
import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel import im.vector.app.features.home.room.list.actions.RoomListQuickActionsSharedActionViewModel
import im.vector.app.features.home.room.list.widget.NotifsFabMenuView import im.vector.app.features.home.room.list.widget.NotifsFabMenuView
import im.vector.app.features.notifications.NotificationDrawerManager import im.vector.app.features.notifications.NotificationDrawerManager
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.android.synthetic.main.fragment_room_list.*
import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.Failure
import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.Membership
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
@ -66,7 +70,10 @@ class RoomListFragment @Inject constructor(
val roomListViewModelFactory: RoomListViewModel.Factory, val roomListViewModelFactory: RoomListViewModel.Factory,
private val notificationDrawerManager: NotificationDrawerManager, private val notificationDrawerManager: NotificationDrawerManager,
private val sharedViewPool: RecyclerView.RecycledViewPool private val sharedViewPool: RecyclerView.RecycledViewPool
) : VectorBaseFragment(), RoomSummaryController.Listener, OnBackPressed, NotifsFabMenuView.Listener { ) : VectorBaseFragment<FragmentRoomListBinding>(),
RoomSummaryController.Listener,
OnBackPressed,
NotifsFabMenuView.Listener {
private var modelBuildListener: OnModelBuildFinishedListener? = null private var modelBuildListener: OnModelBuildFinishedListener? = null
private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel
@ -74,7 +81,9 @@ class RoomListFragment @Inject constructor(
private val roomListViewModel: RoomListViewModel by fragmentViewModel() private val roomListViewModel: RoomListViewModel by fragmentViewModel()
private lateinit var stateRestorer: LayoutManagerStateRestorer private lateinit var stateRestorer: LayoutManagerStateRestorer
override fun getLayoutResId() = R.layout.fragment_room_list override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRoomListBinding {
return FragmentRoomListBinding.inflate(inflater, container, false)
}
private var hasUnreadRooms = false private var hasUnreadRooms = false

View file

@ -18,7 +18,9 @@ package im.vector.app.features.home.room.list.actions
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
@ -27,9 +29,10 @@ import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.cleanup import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.BottomSheetGenericListBinding
import im.vector.app.features.navigation.Navigator import im.vector.app.features.navigation.Navigator
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.android.synthetic.main.bottom_sheet_generic_list.*
import javax.inject.Inject import javax.inject.Inject
@Parcelize @Parcelize
@ -47,7 +50,9 @@ data class RoomListActionsArgs(
/** /**
* Bottom sheet fragment that shows room information with list of contextual actions * Bottom sheet fragment that shows room information with list of contextual actions
*/ */
class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), RoomListQuickActionsEpoxyController.Listener { class RoomListQuickActionsBottomSheet :
VectorBaseBottomSheetDialogFragment<BottomSheetGenericListBinding>(),
RoomListQuickActionsEpoxyController.Listener {
private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel private lateinit var sharedActionViewModel: RoomListQuickActionsSharedActionViewModel
@Inject lateinit var sharedViewPool: RecyclerView.RecycledViewPool @Inject lateinit var sharedViewPool: RecyclerView.RecycledViewPool
@ -63,17 +68,19 @@ class RoomListQuickActionsBottomSheet : VectorBaseBottomSheetDialogFragment(), R
injector.inject(this) injector.inject(this)
} }
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetGenericListBinding {
return BottomSheetGenericListBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java) sharedActionViewModel = activityViewModelProvider.get(RoomListQuickActionsSharedActionViewModel::class.java)
bottomSheetRecyclerView.configureWith(roomListActionsEpoxyController, viewPool = sharedViewPool, hasFixedSize = false, disableItemAnimation = true) views.views.bottomSheetRecyclerView.configureWith(roomListActionsEpoxyController, viewPool = sharedViewPool, hasFixedSize = false, disableItemAnimation = true)
roomListActionsEpoxyController.listener = this roomListActionsEpoxyController.listener = this
} }
override fun onDestroyView() { override fun onDestroyView() {
bottomSheetRecyclerView.cleanup() views.views.bottomSheetRecyclerView.cleanup()
roomListActionsEpoxyController.listener = null roomListActionsEpoxyController.listener = null
super.onDestroyView() super.onDestroyView()
} }

View file

@ -22,7 +22,7 @@ import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.core.view.isVisible import androidx.core.view.isVisible
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import im.vector.app.R import im.vector.app.R
import kotlinx.android.synthetic.main.motion_notifs_fab_menu_merge.view.*
class NotifsFabMenuView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, class NotifsFabMenuView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null,
defStyleAttr: Int = 0) : MotionLayout(context, attrs, defStyleAttr) { defStyleAttr: Int = 0) : MotionLayout(context, attrs, defStyleAttr) {

View file

@ -45,8 +45,8 @@ import im.vector.app.features.userdirectory.UserListSharedAction
import im.vector.app.features.userdirectory.UserListSharedActionViewModel import im.vector.app.features.userdirectory.UserListSharedActionViewModel
import im.vector.app.features.userdirectory.UserListViewModel import im.vector.app.features.userdirectory.UserListViewModel
import im.vector.app.features.userdirectory.UserListViewState import im.vector.app.features.userdirectory.UserListViewState
import kotlinx.android.parcel.Parcelize import kotlinx.parcelize.Parcelize
import kotlinx.android.synthetic.main.activity.*
import org.matrix.android.sdk.api.failure.Failure import org.matrix.android.sdk.api.failure.Failure
import java.net.HttpURLConnection import java.net.HttpURLConnection
import javax.inject.Inject import javax.inject.Inject

View file

@ -19,13 +19,15 @@ package im.vector.app.features.invite
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.view.View import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.di.HasScreenInjector import im.vector.app.core.di.HasScreenInjector
import im.vector.app.core.platform.ButtonStateView import im.vector.app.core.platform.ButtonStateView
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import kotlinx.android.synthetic.main.vector_invite_view.view.*
import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState import org.matrix.android.sdk.api.session.room.members.ChangeMembershipState
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toMatrixItem
@ -44,6 +46,13 @@ class VectorInviteView @JvmOverloads constructor(context: Context, attrs: Attrib
SMALL SMALL
} }
private val inviteAvatarView: ImageView
private val inviteLabelView: TextView
private val inviteNameView: TextView
private val inviteIdentifierView: TextView
private val inviteAcceptView: ButtonStateView
private val inviteRejectView: ButtonStateView
@Inject lateinit var avatarRenderer: AvatarRenderer @Inject lateinit var avatarRenderer: AvatarRenderer
var callback: Callback? = null var callback: Callback? = null
@ -52,6 +61,7 @@ class VectorInviteView @JvmOverloads constructor(context: Context, attrs: Attrib
context.injector().inject(this) context.injector().inject(this)
} }
View.inflate(context, R.layout.vector_invite_view, this) View.inflate(context, R.layout.vector_invite_view, this)
inviteAcceptView = findViewById(R.id.inviteAcceptView)
inviteAcceptView.callback = object : ButtonStateView.Callback { inviteAcceptView.callback = object : ButtonStateView.Callback {
override fun onButtonClicked() { override fun onButtonClicked() {
callback?.onAcceptInvite() callback?.onAcceptInvite()
@ -62,6 +72,7 @@ class VectorInviteView @JvmOverloads constructor(context: Context, attrs: Attrib
} }
} }
inviteRejectView = findViewById(R.id.inviteRejectView)
inviteRejectView.callback = object : ButtonStateView.Callback { inviteRejectView.callback = object : ButtonStateView.Callback {
override fun onButtonClicked() { override fun onButtonClicked() {
callback?.onRejectInvite() callback?.onRejectInvite()
@ -71,6 +82,11 @@ class VectorInviteView @JvmOverloads constructor(context: Context, attrs: Attrib
callback?.onRejectInvite() callback?.onRejectInvite()
} }
} }
inviteAvatarView = findViewById(R.id.inviteAvatarView)
inviteLabelView = findViewById(R.id.inviteLabelView)
inviteNameView = findViewById(R.id.inviteNameView)
inviteIdentifierView = findViewById(R.id.inviteIdentifierView)
} }
fun render(sender: RoomMemberSummary, mode: Mode = Mode.LARGE, changeMembershipState: ChangeMembershipState) { fun render(sender: RoomMemberSummary, mode: Mode = Mode.LARGE, changeMembershipState: ChangeMembershipState) {

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