#319 show hint when blocked cause of obscured while entering the license

Need to manually handle this in case of dialogs as otherwise the onFilterTouchEventForSecurity method of the ViewGroup isn't called when filterTouchesWhenObscured is set to true in the BaseDialog and in contrast to if set in an Activity
This commit is contained in:
Julian Raufelder 2021-06-02 15:20:18 +02:00
parent 9a65f62ae5
commit 68bb87c04b
No known key found for this signature in database
GPG Key ID: 17EE71F6634E381D
7 changed files with 126 additions and 53 deletions

View File

@ -6,13 +6,14 @@ import org.cryptomator.domain.usecases.LicenseCheck
import org.cryptomator.domain.usecases.NoOpResultHandler import org.cryptomator.domain.usecases.NoOpResultHandler
import org.cryptomator.presentation.exception.ExceptionHandlers import org.cryptomator.presentation.exception.ExceptionHandlers
import org.cryptomator.presentation.ui.activity.view.UpdateLicenseView import org.cryptomator.presentation.ui.activity.view.UpdateLicenseView
import org.cryptomator.presentation.ui.dialog.AppIsObscuredInfoDialog
import org.cryptomator.util.SharedPreferencesHandler import org.cryptomator.util.SharedPreferencesHandler
import javax.inject.Inject import javax.inject.Inject
import timber.log.Timber import timber.log.Timber
class LicenseCheckPresenter @Inject internal constructor( class LicenseCheckPresenter @Inject internal constructor(
exceptionHandlers: ExceptionHandlers, // exceptionHandlers: ExceptionHandlers, //
private val doLicenseCheckUsecase: DoLicenseCheckUseCase, // private val doLicenseCheckUseCase: DoLicenseCheckUseCase, //
private val sharedPreferencesHandler: SharedPreferencesHandler private val sharedPreferencesHandler: SharedPreferencesHandler
) : Presenter<UpdateLicenseView>(exceptionHandlers) { ) : Presenter<UpdateLicenseView>(exceptionHandlers) {
@ -20,18 +21,22 @@ class LicenseCheckPresenter @Inject internal constructor(
data?.let { data?.let {
val license = it.fragment ?: it.lastPathSegment ?: "" val license = it.fragment ?: it.lastPathSegment ?: ""
view?.showOrUpdateLicenseDialog(license) view?.showOrUpdateLicenseDialog(license)
doLicenseCheckUsecase doLicenseCheckUseCase
.withLicense(license) .withLicense(license)
.run(CheckLicenseStatusSubscriber()) .run(CheckLicenseStatusSubscriber())
} }
} }
fun validateDialogAware(license: String?) { fun validateDialogAware(license: String?) {
doLicenseCheckUsecase doLicenseCheckUseCase
.withLicense(license) .withLicense(license)
.run(CheckLicenseStatusSubscriber()) .run(CheckLicenseStatusSubscriber())
} }
fun onFilteredTouchEventForSecurity() {
view?.showDialog(AppIsObscuredInfoDialog.newInstance())
}
private inner class CheckLicenseStatusSubscriber : NoOpResultHandler<LicenseCheck>() { private inner class CheckLicenseStatusSubscriber : NoOpResultHandler<LicenseCheck>() {
override fun onSuccess(licenseCheck: LicenseCheck) { override fun onSuccess(licenseCheck: LicenseCheck) {
@ -49,6 +54,6 @@ class LicenseCheckPresenter @Inject internal constructor(
} }
init { init {
unsubscribeOnDestroy(doLicenseCheckUsecase) unsubscribeOnDestroy(doLicenseCheckUseCase)
} }
} }

View File

@ -10,11 +10,13 @@ import org.cryptomator.presentation.presenter.LicenseCheckPresenter
import org.cryptomator.presentation.ui.activity.view.UpdateLicenseView import org.cryptomator.presentation.ui.activity.view.UpdateLicenseView
import org.cryptomator.presentation.ui.dialog.LicenseConfirmationDialog import org.cryptomator.presentation.ui.dialog.LicenseConfirmationDialog
import org.cryptomator.presentation.ui.dialog.UpdateLicenseDialog import org.cryptomator.presentation.ui.dialog.UpdateLicenseDialog
import org.cryptomator.presentation.ui.layout.ObscuredAwareCoordinatorLayout
import javax.inject.Inject import javax.inject.Inject
import kotlin.system.exitProcess import kotlin.system.exitProcess
import kotlinx.android.synthetic.main.activity_layout_obscure_aware.activityRootView
import kotlinx.android.synthetic.main.toolbar_layout.toolbar import kotlinx.android.synthetic.main.toolbar_layout.toolbar
@Activity @Activity(layout = R.layout.activity_layout_obscure_aware)
class LicenseCheckActivity : BaseActivity(), UpdateLicenseDialog.Callback, LicenseConfirmationDialog.Callback, UpdateLicenseView { class LicenseCheckActivity : BaseActivity(), UpdateLicenseDialog.Callback, LicenseConfirmationDialog.Callback, UpdateLicenseView {
@Inject @Inject
@ -23,11 +25,19 @@ class LicenseCheckActivity : BaseActivity(), UpdateLicenseDialog.Callback, Licen
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setupToolbar() activityRootView.setOnFilteredTouchEventForSecurityListener(object : ObscuredAwareCoordinatorLayout.Listener {
override fun onFilteredTouchEventForSecurity() {
licenseCheckPresenter.onFilteredTouchEventForSecurity()
}
})
validate(intent) validate(intent)
} }
override fun setupView() {
setupToolbar()
}
override fun checkLicenseClicked(license: String?) { override fun checkLicenseClicked(license: String?) {
licenseCheckPresenter.validateDialogAware(license) licenseCheckPresenter.validateDialogAware(license)
} }
@ -51,6 +61,11 @@ class LicenseCheckActivity : BaseActivity(), UpdateLicenseDialog.Callback, Licen
exitProcess(0) exitProcess(0)
} }
override fun appObscuredClosingEnterLicenseDialog() {
closeDialog()
licenseCheckPresenter.onFilteredTouchEventForSecurity()
}
private fun setupToolbar() { private fun setupToolbar() {
toolbar.title = getString(R.string.app_name).uppercase() toolbar.title = getString(R.string.app_name).uppercase()
setSupportActionBar(toolbar) setSupportActionBar(toolbar)

View File

@ -82,9 +82,7 @@ abstract class BaseDialog<Callback> : DialogFragment() {
get() = requireContext().resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE get() = requireContext().resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
fun onErrorResponse(view: View?) { fun onErrorResponse(view: View?) {
if (view != null) { view?.let { it.isFocusableInTouchMode = true }
view.isFocusableInTouchMode = true
}
allowClosingDialog(true) allowClosingDialog(true)
enableButtons(true) enableButtons(true)
} }

View File

@ -16,10 +16,10 @@ class LicenseConfirmationDialog : BaseDialog<LicenseConfirmationDialog.Callback>
} }
public override fun setupDialog(builder: AlertDialog.Builder): android.app.Dialog { public override fun setupDialog(builder: AlertDialog.Builder): android.app.Dialog {
builder // return builder //
.setTitle(getString(R.string.dialog_license_confirmation_title)) // .setTitle(getString(R.string.dialog_license_confirmation_title)) //
.setNeutralButton(getText(R.string.dialog_license_confirmation_ok_btn)) { _: DialogInterface, _: Int -> callback?.licenseConfirmationClicked() } .setNeutralButton(getText(R.string.dialog_license_confirmation_ok_btn)) { _: DialogInterface, _: Int -> callback?.licenseConfirmationClicked() } //
return builder.create() .create()
} }
public override fun setupView() { public override fun setupView() {

View File

@ -7,6 +7,9 @@ import android.widget.Button
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import org.cryptomator.generator.Dialog import org.cryptomator.generator.Dialog
import org.cryptomator.presentation.R import org.cryptomator.presentation.R
import org.cryptomator.presentation.ui.layout.ObscuredAwareDialogCoordinatorLayout
import org.cryptomator.util.SharedPreferencesHandler
import kotlinx.android.synthetic.main.dialog_enter_license.dssialogRootView
import kotlinx.android.synthetic.main.dialog_enter_license.et_license import kotlinx.android.synthetic.main.dialog_enter_license.et_license
@Dialog(R.layout.dialog_enter_license) @Dialog(R.layout.dialog_enter_license)
@ -19,6 +22,7 @@ class UpdateLicenseDialog : BaseProgressErrorDialog<UpdateLicenseDialog.Callback
fun checkLicenseClicked(license: String?) fun checkLicenseClicked(license: String?)
fun onCheckLicenseCanceled() fun onCheckLicenseCanceled()
fun appObscuredClosingEnterLicenseDialog()
} }
override fun onStart() { override fun onStart() {
@ -35,6 +39,15 @@ class UpdateLicenseDialog : BaseProgressErrorDialog<UpdateLicenseDialog.Callback
et_license.nextFocusForwardId = button.id et_license.nextFocusForwardId = button.id
} }
} }
/* need to manually handle this in case of dialogs as otherwise the onFilterTouchEventForSecurity method of the ViewGroup
isn't called when filterTouchesWhenObscured is set to true in the BaseDialog and in contrast to if set in an Activity */
dialog?.window?.decorView?.filterTouchesWhenObscured = false
dssialogRootView.setOnFilteredTouchEventForSecurityListener(object : ObscuredAwareDialogCoordinatorLayout.Listener {
override fun onFilteredTouchEventForSecurity() {
callback?.appObscuredClosingEnterLicenseDialog()
}
}, SharedPreferencesHandler(requireContext()).disableAppWhenObscured())
} }
public override fun setupDialog(builder: AlertDialog.Builder): android.app.Dialog { public override fun setupDialog(builder: AlertDialog.Builder): android.app.Dialog {
@ -47,9 +60,7 @@ class UpdateLicenseDialog : BaseProgressErrorDialog<UpdateLicenseDialog.Callback
public override fun setupView() { public override fun setupView() {
val license = requireArguments().getSerializable(LICENSE_ARG) as String? val license = requireArguments().getSerializable(LICENSE_ARG) as String?
if (license != null) { license?.let { et_license.setText(it) }
et_license.setText(license)
}
et_license.requestFocus() et_license.requestFocus()
registerOnEditorDoneActionAndPerformButtonClick(et_license) { checkLicenseButton } registerOnEditorDoneActionAndPerformButtonClick(et_license) { checkLicenseButton }
} }

View File

@ -0,0 +1,38 @@
package org.cryptomator.presentation.ui.layout
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.coordinatorlayout.widget.CoordinatorLayout
class ObscuredAwareDialogCoordinatorLayout : CoordinatorLayout {
private var listener: Listener? = null
private var active: Boolean = true
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
fun setOnFilteredTouchEventForSecurityListener(listener: Listener, active: Boolean) {
this.listener = listener
this.active = active
}
override fun onFilterTouchEventForSecurity(event: MotionEvent): Boolean {
return if (active and ((event.flags and MotionEvent.FLAG_WINDOW_IS_OBSCURED) == MotionEvent.FLAG_WINDOW_IS_OBSCURED)) {
listener?.onFilteredTouchEventForSecurity()
false
} else {
true
}
}
interface Listener {
fun onFilteredTouchEventForSecurity()
}
}

View File

@ -1,51 +1,57 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" <org.cryptomator.presentation.ui.layout.ObscuredAwareDialogCoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dssialogRootView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<RelativeLayout <androidx.core.widget.NestedScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:padding="@dimen/activity_vertical_margin">
<TextView <RelativeLayout
android:id="@+id/tv_message"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_marginBottom="5dp" android:padding="@dimen/activity_vertical_margin">
android:text="@string/dialog_enter_license_content" />
<LinearLayout <TextView
android:id="@+id/ll_folder" android:id="@+id/tv_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_message"
android:orientation="horizontal">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/ic_license_key" />
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/et_license"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:imeOptions="actionDone|flagNoPersonalizedLearning" android:layout_marginBottom="5dp"
android:inputType="text" /> android:text="@string/dialog_enter_license_content" />
</LinearLayout>
<include <LinearLayout
layout="@layout/view_dialog_progress" android:id="@+id/ll_license"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_below="@+id/til_password" /> android:layout_below="@+id/tv_message"
android:orientation="horizontal">
<include <ImageView
layout="@layout/view_dialog_error" android:layout_width="40dp"
android:layout_width="match_parent" android:layout_height="40dp"
android:layout_height="wrap_content" android:src="@drawable/ic_license_key" />
android:layout_below="@+id/ll_folder" />
</RelativeLayout> <com.google.android.material.textfield.TextInputEditText
</androidx.core.widget.NestedScrollView> android:id="@+id/et_license"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone|flagNoPersonalizedLearning"
android:inputType="text" />
</LinearLayout>
<include
layout="@layout/view_dialog_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/til_password" />
<include
layout="@layout/view_dialog_error"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/ll_license" />
</RelativeLayout>
</androidx.core.widget.NestedScrollView>
</org.cryptomator.presentation.ui.layout.ObscuredAwareDialogCoordinatorLayout>