This commit is contained in:
Benoit Marty
2019-04-03 16:36:45 +02:00
parent 3091a337c9
commit 08dacacdda
20 changed files with 883 additions and 19 deletions

View File

@ -27,6 +27,7 @@ import androidx.core.view.GravityCompat
import androidx.drawerlayout.widget.DrawerLayout
import androidx.fragment.app.FragmentManager
import com.airbnb.mvrx.viewModel
import im.vector.matrix.android.api.Matrix
import im.vector.riotredesign.R
import im.vector.riotredesign.core.extensions.hideKeyboard
import im.vector.riotredesign.core.extensions.observeEvent
@ -38,6 +39,7 @@ import im.vector.riotredesign.features.home.room.detail.LoadingRoomDetailFragmen
import im.vector.riotredesign.features.rageshake.BugReporter
import im.vector.riotredesign.features.rageshake.VectorUncaughtExceptionHandler
import im.vector.riotredesign.features.settings.VectorSettingsActivity
import im.vector.riotredesign.features.workers.signout.SignOutUiWorker
import kotlinx.android.synthetic.main.activity_home.*
import org.koin.android.ext.android.inject
import org.koin.android.scope.ext.android.bindScope
@ -114,6 +116,10 @@ class HomeActivity : RiotActivity(), ToolbarConfigurable {
startActivity(VectorSettingsActivity.getIntent(this, "TODO"))
return true
}
R.id.sliding_menu_sign_out -> {
SignOutUiWorker(this).perform(Matrix.getInstance().currentSession!!)
return true
}
}
return true

View File

@ -97,7 +97,7 @@ class RoomDetailViewModel(initialState: RoomDetailViewState,
.subscribeBy(onNext = { actions ->
val mostRecentEvent = actions.maxBy { it.event.displayIndex }
mostRecentEvent?.event?.root?.eventId?.let { eventId ->
room.setReadReceipt(eventId, callback = object : MatrixCallback<Void> {})
room.setReadReceipt(eventId, callback = object : MatrixCallback<Unit> {})
}
})
.disposeOnClear()

View File

@ -0,0 +1,257 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotredesign.features.workers.signout
import android.app.Dialog
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import butterknife.BindView
import butterknife.ButterKnife
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import im.vector.matrix.android.api.session.Session
import im.vector.riotredesign.R
import org.koin.android.ext.android.inject
class SignOutBottomSheetDialogFragment : BottomSheetDialogFragment() {
val session by inject<Session>()
@BindView(R.id.bottom_sheet_signout_warning_text)
lateinit var sheetTitle: TextView
@BindView(R.id.bottom_sheet_signout_backingup_status_group)
lateinit var backingUpStatusGroup: ViewGroup
@BindView(R.id.keys_backup_setup)
lateinit var setupClickableView: View
@BindView(R.id.keys_backup_activate)
lateinit var activateClickableView: View
@BindView(R.id.keys_backup_dont_want)
lateinit var dontWantClickableView: View
@BindView(R.id.bottom_sheet_signout_icon_progress_bar)
lateinit var backupProgress: ProgressBar
@BindView(R.id.bottom_sheet_signout_icon)
lateinit var backupCompleteImage: ImageView
@BindView(R.id.bottom_sheet_backup_status_text)
lateinit var backupStatusTex: TextView
@BindView(R.id.bottom_sheet_signout_button)
lateinit var signoutClickableView: View
@BindView(R.id.root_layout)
lateinit var rootLayout: ViewGroup
var onSignOut: Runnable? = null
companion object {
fun newInstance(userId: String) = SignOutBottomSheetDialogFragment()
private const val EXPORT_REQ = 0
}
init {
isCancelable = true
}
private lateinit var viewModel: SignOutViewModel
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
viewModel = ViewModelProviders.of(this).get(SignOutViewModel::class.java)
viewModel.init(session)
setupClickableView.setOnClickListener {
context?.let { context ->
// TODO startActivityForResult(KeysBackupSetupActivity.intent(context, getExtraMatrixID(), true), EXPORT_REQ)
}
}
activateClickableView.setOnClickListener {
context?.let { context ->
// TODO startActivity(KeysBackupManageActivity.intent(context, getExtraMatrixID()))
}
}
signoutClickableView.setOnClickListener {
this.onSignOut?.run()
}
dontWantClickableView.setOnClickListener { _ ->
context?.let {
AlertDialog.Builder(it)
.setTitle(R.string.are_you_sure)
.setMessage(R.string.sign_out_bottom_sheet_will_lose_secure_messages)
.setPositiveButton(R.string.backup) { _, _ ->
/* TODO
when (viewModel.keysBackupState.value) {
KeysBackupStateManager.KeysBackupState.NotTrusted -> {
context?.let { context ->
startActivity(KeysBackupManageActivity.intent(context, getExtraMatrixID()))
}
}
KeysBackupStateManager.KeysBackupState.Disabled -> {
context?.let { context ->
startActivityForResult(KeysBackupSetupActivity.intent(context, getExtraMatrixID(), true), EXPORT_REQ)
}
}
KeysBackupStateManager.KeysBackupState.BackingUp,
KeysBackupStateManager.KeysBackupState.WillBackUp -> {
//keys are already backing up please wait
context?.toast(R.string.keys_backup_is_not_finished_please_wait)
}
else -> {
//nop
}
}
*/
}
.setNegativeButton(R.string.action_sign_out) { _, _ ->
onSignOut?.run()
}
.show()
}
}
viewModel.keysExportedToFile.observe(this, Observer {
val hasExportedToFile = it ?: false
if (hasExportedToFile) {
//We can allow to sign out
sheetTitle.text = getString(R.string.action_sign_out_confirmation_simple)
signoutClickableView.isVisible = true
dontWantClickableView.isVisible = false
setupClickableView.isVisible = false
activateClickableView.isVisible = false
backingUpStatusGroup.isVisible = false
}
})
/* TODO
viewModel.keysBackupState.observe(this, Observer {
if (viewModel.keysExportedToFile.value == true) {
//ignore this
return@Observer
}
TransitionManager.beginDelayedTransition(rootLayout)
when (it) {
KeysBackupStateManager.KeysBackupState.ReadyToBackUp -> {
signoutClickableView.isVisible = true
dontWantClickableView.isVisible = false
setupClickableView.isVisible = false
activateClickableView.isVisible = false
backingUpStatusGroup.isVisible = true
backupProgress.isVisible = false
backupCompleteImage.isVisible = true
backupStatusTex.text = getString(R.string.keys_backup_info_keys_all_backup_up)
sheetTitle.text = getString(R.string.action_sign_out_confirmation_simple)
}
KeysBackupStateManager.KeysBackupState.BackingUp,
KeysBackupStateManager.KeysBackupState.WillBackUp -> {
backingUpStatusGroup.isVisible = true
sheetTitle.text = getString(R.string.sign_out_bottom_sheet_warning_backing_up)
dontWantClickableView.isVisible = true
setupClickableView.isVisible = false
activateClickableView.isVisible = false
backupProgress.isVisible = true
backupCompleteImage.isVisible = false
backupStatusTex.text = getString(R.string.sign_out_bottom_sheet_backing_up_keys)
}
KeysBackupStateManager.KeysBackupState.NotTrusted -> {
backingUpStatusGroup.isVisible = false
dontWantClickableView.isVisible = true
setupClickableView.isVisible = false
activateClickableView.isVisible = true
sheetTitle.text = getString(R.string.sign_out_bottom_sheet_warning_backup_not_active)
}
else -> {
backingUpStatusGroup.isVisible = false
dontWantClickableView.isVisible = true
setupClickableView.isVisible = true
activateClickableView.isVisible = false
sheetTitle.text = getString(R.string.sign_out_bottom_sheet_warning_no_backup)
}
}
// updateSignOutSection()
})
*/
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.bottom_sheet_logout_and_backup, container, false)
ButterKnife.bind(this, view)
return view
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val dialog = super.onCreateDialog(savedInstanceState)
//We want to force the bottom sheet initial state to expanded
(dialog as? BottomSheetDialog)?.let { bottomSheetDialog ->
bottomSheetDialog.setOnShowListener { dialog ->
val d = dialog as BottomSheetDialog
(d.findViewById<View>(com.google.android.material.R.id.design_bottom_sheet) as? FrameLayout)?.let {
BottomSheetBehavior.from(it).state = BottomSheetBehavior.STATE_EXPANDED
}
}
}
return dialog
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
/* TODO
if (resultCode == Activity.RESULT_OK) {
if (requestCode == EXPORT_REQ) {
val manualExportDone = data?.getBooleanExtra(KeysBackupSetupActivity.MANUAL_EXPORT, false)
viewModel.keysExportedToFile.value = manualExportDone
}
}
*/
}
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotredesign.features.workers.signout
import android.content.Intent
import androidx.appcompat.app.AlertDialog
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.Session
import im.vector.riotredesign.R
import im.vector.riotredesign.core.platform.RiotActivity
import im.vector.riotredesign.features.MainActivity
class SignOutUiWorker(val activity: RiotActivity) {
fun perform(session: Session) {
if (SignOutViewModel.doYouNeedToBeDisplayed(session)) {
val signOutDialog = SignOutBottomSheetDialogFragment.newInstance(session.sessionParams.credentials.userId)
signOutDialog.onSignOut = Runnable {
doSignOut(session)
}
signOutDialog.show(activity.supportFragmentManager, "SO")
} else {
// Display a simple confirmation dialog
AlertDialog.Builder(activity)
.setTitle(R.string.action_sign_out)
.setMessage(R.string.action_sign_out_confirmation_simple)
.setPositiveButton(R.string.action_sign_out) { _, _ ->
doSignOut(session)
}
.setNegativeButton(R.string.cancel, null)
.show()
}
}
private fun doSignOut(session: Session) {
// TODO showWaitingView()
session.signOut(object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
// Start MainActivity in a new task
val intent = Intent(activity, MainActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
activity.startActivity(intent)
}
override fun onFailure(failure: Throwable) {
// TODO Notify user, or ignore?
}
})
}
}

View File

@ -0,0 +1,110 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.riotredesign.features.workers.signout
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import im.vector.matrix.android.api.session.Session
class SignOutViewModel : ViewModel() { // TODO, KeysBackupStateManager.KeysBackupStateListener {
// Keys exported manually
var keysExportedToFile = MutableLiveData<Boolean>()
// var keysBackupState = MutableLiveData<KeysBackupStateManager.KeysBackupState>()
private var mxSession: Session? = null
fun init(session: Session) {
if (mxSession == null) {
mxSession = session
// TODO
//mxSession?.crypto
// ?.keysBackup
// ?.addListener(this)
}
//keysBackupState.value = mxSession?.crypto
// ?.keysBackup
// ?.state
}
// /**
// * Safe way to get the current KeysBackup version
// */
// fun getCurrentBackupVersion(): String {
// return mxSession
// ?.crypto
// ?.keysBackup
// ?.currentBackupVersion
// ?: ""
// }
//
// /**
// * Safe way to get the number of keys to backup
// */
// fun getNumberOfKeysToBackup(): Int {
// return mxSession
// ?.crypto
// ?.cryptoStore
// ?.inboundGroupSessionsCount(false)
// ?: 0
// }
//
// /**
// * Safe way to tell if there are more keys on the server
// */
// fun canRestoreKeys(): Boolean {
// return mxSession
// ?.crypto
// ?.keysBackup
// ?.canRestoreKeys() == true
// }
//
// override fun onCleared() {
// super.onCleared()
//
// mxSession?.crypto
// ?.keysBackup
// ?.removeListener(this)
// }
//
// override fun onStateChange(newState: KeysBackupStateManager.KeysBackupState) {
// keysBackupState.value = newState
// }
companion object {
/**
* The backup check on logout flow has to be displayed if there are keys in the store, and the keys backup state is not Ready
*/
fun doYouNeedToBeDisplayed(session: Session?): Boolean {
return false
/* TODO
return session
?.crypto
?.cryptoStore
?.inboundGroupSessionsCount(false)
?: 0 > 0
&& session
?.crypto
?.keysBackup
?.state != KeysBackupStateManager.KeysBackupState.ReadyToBackUp
*/
}
}
}

View File

@ -0,0 +1,202 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="8dp"
android:paddingBottom="8dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
android:layout_marginBottom="8dp"
android:text="@string/action_sign_out"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/bottom_sheet_signout_warning_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
android:layout_marginBottom="8dp"
android:textColor="?android:attr/textColorSecondary"
tools:text="@string/sign_out_bottom_sheet_warning_no_backup" />
<LinearLayout
android:id="@+id/bottom_sheet_signout_backingup_status_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_horizontal_margin"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
android:layout_marginBottom="8dp"
android:gravity="center"
android:orientation="horizontal"
android:visibility="gone"
tools:visibility="visible">
<ImageView
android:id="@+id/bottom_sheet_signout_icon"
android:layout_width="20dp"
android:layout_height="20dp"
android:visibility="gone"
app:srcCompat="@drawable/unit_test_ok"
tools:visibility="visible" />
<ProgressBar
android:id="@+id/bottom_sheet_signout_icon_progress_bar"
style="?android:attr/progressBarStyle"
android:layout_width="20dp"
android:layout_height="20dp"
android:visibility="visible"
tools:visibility="gone" />
<TextView
android:id="@+id/bottom_sheet_backup_status_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
tools:text="@string/keys_backup_info_keys_all_backup_up" />
</LinearLayout>
<LinearLayout
android:id="@+id/keys_backup_setup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:foreground="?attr/selectableItemBackground"
android:minHeight="50dp"
android:orientation="horizontal"
android:paddingLeft="@dimen/layout_horizontal_margin"
android:paddingTop="8dp"
android:paddingRight="@dimen/layout_horizontal_margin"
android:paddingBottom="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:scaleType="fitCenter"
android:src="@drawable/backup_keys"
android:tint="?android:attr/textColorTertiary" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/keys_backup_setup"
android:textSize="17sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/keys_backup_activate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:foreground="?attr/selectableItemBackground"
android:minHeight="50dp"
android:orientation="horizontal"
android:paddingLeft="@dimen/layout_horizontal_margin"
android:paddingTop="8dp"
android:paddingRight="@dimen/layout_horizontal_margin"
android:paddingBottom="8dp"
android:visibility="gone"
tools:visibility="visible">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:scaleType="fitCenter"
android:src="@drawable/backup_keys"
android:tint="?android:attr/textColorTertiary" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/keys_backup_activate"
android:textSize="17sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/keys_backup_dont_want"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:foreground="?attr/selectableItemBackground"
android:minHeight="50dp"
android:orientation="horizontal"
android:paddingLeft="@dimen/layout_horizontal_margin"
android:paddingTop="8dp"
android:paddingRight="@dimen/layout_horizontal_margin"
android:paddingBottom="8dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_material_leave"
android:tint="@color/vector_error_color" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/sign_out_bottom_sheet_dont_want_secure_messages"
android:textColor="@color/vector_error_color"
android:textSize="17sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/bottom_sheet_signout_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:foreground="?attr/selectableItemBackground"
android:minHeight="50dp"
android:orientation="horizontal"
android:paddingLeft="@dimen/layout_horizontal_margin"
android:paddingTop="8dp"
android:paddingRight="@dimen/layout_horizontal_margin"
android:paddingBottom="8dp"
android:visibility="gone"
tools:visibility="visible">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:src="@drawable/ic_material_exit_to_app"
android:tint="@color/vector_error_color" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/action_sign_out"
android:textColor="@color/vector_error_color"
android:textSize="17sp" />
</LinearLayout>
</LinearLayout>

View File

@ -1,11 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/sliding_menu_settings"
android:icon="@drawable/ic_settings"
android:title="@string/room_sliding_menu_settings"
app:showAsAction="ifRoom" />
android:title="@string/room_sliding_menu_settings" />
<item
android:id="@+id/sliding_menu_sign_out"
android:icon="@drawable/ic_material_exit_to_app"
android:title="@string/action_sign_out" />
</menu>