Fix permission handling + share my code by text

This commit is contained in:
Valere 2020-11-20 10:04:41 +01:00
parent ae6de8fdf1
commit baef9f5aa7
13 changed files with 148 additions and 42 deletions

View File

@ -587,6 +587,16 @@ abstract class VectorBaseActivity : AppCompatActivity(), HasScreenInjector {
}
}
fun showSnackbar(message: String, @StringRes withActionTitle: Int?, action: (() -> Unit)?) {
coordinatorLayout?.let {
Snackbar.make(it, message, Snackbar.LENGTH_LONG).apply {
withActionTitle?.let {
setAction(withActionTitle, { action?.invoke() })
}
}.show()
}
}
/* ==========================================================================================
* User Consent
* ========================================================================================== */

View File

@ -19,8 +19,11 @@ package im.vector.app.core.utils
import android.Manifest
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.provider.Settings
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
@ -30,6 +33,8 @@ import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import im.vector.app.R
import im.vector.app.core.platform.VectorBaseActivity
import org.matrix.android.sdk.api.extensions.tryOrNull
import timber.log.Timber
// Android M permission request code management
@ -284,6 +289,19 @@ private fun checkPermissions(permissionsToBeGrantedBitMap: Int,
return isPermissionGranted
}
fun VectorBaseActivity.onPermissionDeniedSnackbar(@StringRes rationaleMessage: Int) {
showSnackbar(getString(rationaleMessage), R.string.settings) {
tryOrNull {
startActivity(
Intent().apply {
action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
data = Uri.fromParts("package", this@onPermissionDeniedSnackbar.packageName, null)
})
}
}
}
/**
* Helper method used in [.checkPermissions] to populate the list of the
* permissions to be granted (permissionsListToBeGrantedOut) and the list of the permissions already denied (permissionAlreadyDeniedListOut).

View File

@ -22,7 +22,6 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
@ -43,6 +42,7 @@ import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA
import im.vector.app.core.utils.PERMISSION_REQUEST_CODE_READ_CONTACTS
import im.vector.app.core.utils.allGranted
import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.onPermissionDeniedSnackbar
import im.vector.app.features.contactsbook.ContactsBookFragment
import im.vector.app.features.contactsbook.ContactsBookViewModel
import im.vector.app.features.userdirectory.UserListFragment
@ -132,9 +132,10 @@ class CreateDirectRoomActivity : SimpleFragmentActivity(), UserListViewModel.Fac
addFragment(R.id.container, CreateDirectRoomByQrCodeFragment::class.java)
}
} else {
Toast.makeText(baseContext, R.string.missing_permissions_error, Toast.LENGTH_SHORT).show()
if (requestCode == PERMISSION_REQUEST_CODE_LAUNCH_CAMERA) {
finish()
onPermissionDeniedSnackbar(R.string.permissions_denied_qr_code)
} else if (requestCode == PERMISSION_REQUEST_CODE_READ_CONTACTS) {
onPermissionDeniedSnackbar(R.string.permissions_denied_add_contact)
}
}
}

View File

@ -16,10 +16,13 @@
package im.vector.app.features.usercode
import android.Manifest
import android.app.Activity
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.core.content.ContextCompat
import com.airbnb.mvrx.activityViewModel
import com.google.zxing.Result
import com.google.zxing.ResultMetadataType
@ -60,6 +63,9 @@ class ScanUserCodeFragment @Inject constructor()
private val openCameraActivityResultLauncher = registerForPermissionsResult { allGranted ->
if (allGranted) {
startCamera()
} else {
// For now just go back
sharedViewModel.handle(UserCodeActions.SwitchMode(UserCodeState.Mode.SHOW))
}
}
@ -90,12 +96,18 @@ class ScanUserCodeFragment @Inject constructor()
}
}
override fun onStart() {
super.onStart()
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), openCameraActivityResultLauncher)) {
startCamera()
}
}
override fun onResume() {
super.onResume()
// Register ourselves as a handler for scan results.
userCodeScannerView.setResultHandler(this)
// Start camera on resume
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), openCameraActivityResultLauncher)) {
if (PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.CAMERA)) {
startCamera()
}
}

View File

@ -23,6 +23,10 @@ import com.airbnb.mvrx.withState
import im.vector.app.R
import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.core.utils.startSharePlainTextIntent
import im.vector.app.features.home.AvatarRenderer
import kotlinx.android.synthetic.main.fragment_user_code_show.*
import javax.inject.Inject
@ -35,13 +39,38 @@ class ShowUserCodeFragment @Inject constructor(
val sharedViewModel: UserCodeSharedViewModel by activityViewModel()
private val openCameraActivityResultLauncher = registerForPermissionsResult { allGranted ->
if (allGranted) {
doOpenQRCodeScanner()
} else {
sharedViewModel.handle(UserCodeActions.CameraPermissionNotGranted)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
showUserCodeClose.debouncedClicks {
sharedViewModel.handle(UserCodeActions.DismissAction)
}
showUserCodeScanButton.debouncedClicks {
doOpenQRCodeScanner()
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), openCameraActivityResultLauncher)) {
doOpenQRCodeScanner()
}
}
shareByText.debouncedClicks {
sharedViewModel.handle(UserCodeActions.ShareByText)
}
sharedViewModel.observeViewEvents {
if (it is UserCodeShareViewEvents.SharePlainText) {
startSharePlainTextIntent(
fragment = this,
activityResultLauncher = null,
chooserTitle = it.title,
text = it.text,
extraTitle = it.richPlainText
)
}
}
}

View File

@ -24,4 +24,6 @@ sealed class UserCodeActions : VectorViewModelAction {
data class SwitchMode(val mode: UserCodeState.Mode) : UserCodeActions()
data class DecodedQRCode(val code: String) : UserCodeActions()
data class StartChattingWithUser(val matrixItem: MatrixItem) : UserCodeActions()
object CameraPermissionNotGranted : UserCodeActions()
object ShareByText : UserCodeActions()
}

View File

@ -32,6 +32,7 @@ import im.vector.app.core.di.ScreenComponent
import im.vector.app.core.extensions.commitTransaction
import im.vector.app.core.extensions.exhaustive
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.utils.onPermissionDeniedSnackbar
import im.vector.app.features.matrixto.MatrixToBottomSheet
import kotlinx.android.parcel.Parcelize
import kotlinx.android.synthetic.main.activity_simple.*
@ -78,13 +79,15 @@ class UserCodeActivity
sharedViewModel.observeViewEvents {
when (it) {
is UserCodeShareViewEvents.InviteFriend -> TODO()
UserCodeShareViewEvents.Dismiss -> ActivityCompat.finishAfterTransition(this)
UserCodeShareViewEvents.ShowWaitingScreen -> simpleActivityWaitingView.isVisible = true
UserCodeShareViewEvents.HideWaitingScreen -> simpleActivityWaitingView.isVisible = false
is UserCodeShareViewEvents.ToastMessage -> Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
is UserCodeShareViewEvents.NavigateToRoom -> navigator.openRoom(this, it.roomId)
}.exhaustive
UserCodeShareViewEvents.Dismiss -> ActivityCompat.finishAfterTransition(this)
UserCodeShareViewEvents.ShowWaitingScreen -> simpleActivityWaitingView.isVisible = true
UserCodeShareViewEvents.HideWaitingScreen -> simpleActivityWaitingView.isVisible = false
is UserCodeShareViewEvents.ToastMessage -> Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
is UserCodeShareViewEvents.NavigateToRoom -> navigator.openRoom(this, it.roomId)
UserCodeShareViewEvents.CameraPermissionNotGranted -> onPermissionDeniedSnackbar(R.string.permissions_denied_qr_code)
else -> {
}
}
}
}

View File

@ -19,10 +19,11 @@ package im.vector.app.features.usercode
import im.vector.app.core.platform.VectorViewEvents
sealed class UserCodeShareViewEvents : VectorViewEvents {
data class InviteFriend(val permalink: String) : UserCodeShareViewEvents()
object Dismiss : UserCodeShareViewEvents()
object ShowWaitingScreen : UserCodeShareViewEvents()
object HideWaitingScreen : UserCodeShareViewEvents()
data class ToastMessage(val message: String) : UserCodeShareViewEvents()
data class NavigateToRoom(val roomId: String) : UserCodeShareViewEvents()
object CameraPermissionNotGranted : UserCodeShareViewEvents()
data class SharePlainText(val text: String, val title: String, val richPlainText: String) : UserCodeShareViewEvents()
}

View File

@ -71,12 +71,6 @@ class UserCodeSharedViewModel @AssistedInject constructor(
}
}
private fun handleInviteFriend() {
session.permalinkService().createPermalink(initialState.userId)?.let { permalink ->
_viewEvents.post(UserCodeShareViewEvents.InviteFriend(permalink))
}
}
@AssistedInject.Factory
interface Factory {
fun create(initialState: UserCodeState, args: UserCodeActivity.Args): UserCodeSharedViewModel
@ -84,10 +78,23 @@ class UserCodeSharedViewModel @AssistedInject constructor(
override fun handle(action: UserCodeActions) {
when (action) {
UserCodeActions.DismissAction -> _viewEvents.post(UserCodeShareViewEvents.Dismiss)
is UserCodeActions.SwitchMode -> setState { copy(mode = action.mode) }
is UserCodeActions.DecodedQRCode -> handleQrCodeDecoded(action)
is UserCodeActions.StartChattingWithUser -> handleStartChatting(action)
UserCodeActions.DismissAction -> _viewEvents.post(UserCodeShareViewEvents.Dismiss)
is UserCodeActions.SwitchMode -> setState { copy(mode = action.mode) }
is UserCodeActions.DecodedQRCode -> handleQrCodeDecoded(action)
is UserCodeActions.StartChattingWithUser -> handleStartChatting(action)
UserCodeActions.CameraPermissionNotGranted -> _viewEvents.post(UserCodeShareViewEvents.CameraPermissionNotGranted)
UserCodeActions.ShareByText -> handleShareByText()
}
}
private fun handleShareByText() {
session.permalinkService().createPermalink(session.myUserId)?.let { permalink ->
val text = stringProvider.getString(R.string.invite_friends_text, permalink)
_viewEvents.post(UserCodeShareViewEvents.SharePlainText(
text,
stringProvider.getString(R.string.invite_friends),
stringProvider.getString(R.string.invite_friends_rich_title)
))
}
}

View File

@ -1,26 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/vector_coordinator_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
style="@style/VectorToolbarStyle"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp"
app:layout_constraintTop_toTopOf="parent" />
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/container"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar" />
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
style="@style/VectorToolbarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp"
app:layout_constraintTop_toTopOf="parent" />
<include layout="@layout/merge_overlay_waiting_view"/>
<androidx.fragment.app.FragmentContainerView
android:id="@+id/container"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar" />
</androidx.constraintlayout.widget.ConstraintLayout>
<include layout="@layout/merge_overlay_waiting_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -49,6 +49,19 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/showUserCodeClose"
app:layout_constraintEnd_toStartOf="@id/shareByText"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/shareByText"
android:layout_width="@dimen/layout_touch_size"
android:layout_height="@dimen/layout_touch_size"
android:scaleType="center"
android:src="@drawable/ic_share"
app:tint="@color/riotx_accent"
android:layout_marginEnd="@dimen/layout_horizontal_margin"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -425,6 +425,8 @@
<string name="permissions_msg_contacts_warning_other_androids">Element can check your address book to find other Matrix users based on their email and phone numbers.\n\nDo you agree to share your address book for this purpose?</string>
<string name="permissions_action_not_performed_missing_permissions">Sorry. Action not performed, due to missing permissions</string>
<string name="permissions_denied_qr_code">To scan a QR code, allow Camera permission to take a picture</string>
<string name="permissions_denied_add_contact">To check your address book, allow Contact permission.</string>
<!-- medias slider string -->
<string name="media_slider_saved">Saved</string>

View File

@ -5,7 +5,9 @@
<item name="android:background">@color/notification_accent_color</item>
</style>
<style name="VectorSnackBarButton" parent="@style/Widget.MaterialComponents.Button" />
<style name="VectorSnackBarButton" parent="@style/Widget.MaterialComponents.Button">
<item name="android:textColor">@color/white</item>
</style>
<style name="VectorSnackBarText" parent="@style/Widget.MaterialComponents.Snackbar.TextView">
<item name="android:textColor">@color/white</item>