Show permission denied when camera permissions not given (#231)

* Show permission denied when camera permissions not given

* Refactor camera code to add states

* Fix issue caused due to camera view permissions

* handle scenario where user did not give permissions in the settings page

* refactor setOnclickListener

* styling and add photo taking instructions

* fix flip image issue

* fix styling

* fix back button functionality

* show mask when settings(permissions) is displayed at the bottom

* fix styles

* fix mask image size

* Fix alignment of all views

* fix card styling

* add mask background

* fix navigation
This commit is contained in:
Sandhya Koti
2020-02-13 11:26:32 +05:30
committed by GitHub Enterprise
parent 4f66dc0e84
commit ed3fc76733
7 changed files with 239 additions and 164 deletions

View File

@@ -6,11 +6,14 @@
package com.navi.getloan.kyc.activities
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.os.Bundle
import android.view.View
import androidx.core.app.ActivityCompat
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.MutableLiveData
@@ -21,6 +24,7 @@ import com.navi.customcamera.CameraEventListener
import com.navi.customcamera.getCameraCaptureImageBitmap
import com.navi.databinding.SelfieCaptureActivityBinding
import com.navi.getloan.activities.GetLoanActivity
import com.navi.manager.PermissionsManager
import com.navi.utils.Constants.DATA
import com.navi.utils.log
import com.navi.utils.observeNullable
@@ -32,6 +36,10 @@ import com.otaliastudios.cameraview.controls.Preview
class SelfieCaptureActivity : BaseActivity(), View.OnClickListener {
private lateinit var binding: SelfieCaptureActivityBinding
private val captureImageBitmap = MutableLiveData<Bitmap>()
private val permissionsManager = PermissionsManager(this)
private val permissions = arrayOf(
Manifest.permission.CAMERA
)
private val cameraActionListener = object : CameraActionListener {
override fun onPictureTaken(result: PictureResult?) {
result?.let { pictureResult ->
@@ -53,76 +61,121 @@ class SelfieCaptureActivity : BaseActivity(), View.OnClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.selfie_capture_activity)
initUI()
if (permissionsManager.hasPermissions(permissions)) {
renderState(CAMERA_PERMISSION_GRANTED)
} else {
permissionsManager.requestPermissions(permissions)
}
}
private fun initUI() {
binding.cameraCaptureIv.setOnClickListener(this)
binding.cameraActionButton.setProperties(
getString(R.string.confirm),
iconId = R.drawable.ic_check_svg
)
binding.cameraActionButton.setOnClickListener(this)
binding.closeIv.setOnClickListener(this)
binding.backIv.setOnClickListener(this)
init()
}
private fun init() {
initBeforeScreenCapture()
observeCameraImage()
initializeCamera()
}
private fun initializeCamera() {
binding.camera.setLifecycleOwner(this)
binding.camera.addCameraListener(CameraEventListener(cameraActionListener))
openCamera()
}
private fun observeCameraImage() {
captureImageBitmap.observeNullable(this) { bitmap ->
bitmap?.let {
initAfterScreenCapture(bitmap)
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PermissionsManager.REQUEST_CODE) {
if (permissionsManager.hasPermissions(permissions as Array<String>)) {
renderState(CAMERA_PERMISSION_GRANTED)
} else if (isDontAskAgainClicked(permissions.first(), grantResults.first())) {
renderState(CAMERA_PERMISSION_DENIED_WITH_NEVER_ASK_AGAIN)
} else {
binding.closeIv.visibility = View.GONE
binding.cameraCaptureIv.setOnClickListener(this)
}
}
}
private fun initAfterScreenCapture(bitmap: Bitmap) {
binding.closeIv.visibility = View.VISIBLE
binding.cameraCaptureIv.visibility = View.GONE
binding.cameraActionButton.visibility = View.VISIBLE
binding.selfieImageIv.setImageBitmap(bitmap)
binding.selfieImageIv.visibility = View.VISIBLE
binding.focusMaskIv.visibility = View.GONE
}
private fun handleAfterCaptureImage(imageBitamp: Bitmap) {
try {
val intent = Intent()
intent.putExtra(DATA, putBitmapIntoFileAndGetFileName(this, imageBitamp))
setResult(Activity.RESULT_OK, intent)
finish()
} catch (e: Exception) {
e.printStackTrace()
private fun renderState(state: Int) {
when (state) {
CAMERA_PERMISSION_GRANTED -> {
setState(INITIAL_SETUP)
setState(BEFORE_IMAGE_CAPTURE)
captureImageBitmap.observeNullable(this) { bitmap ->
bitmap?.let {
setState(AFTER_IMAGE_CAPTURE, bitmap)
}
}
openCamera()
}
CAMERA_PERMISSION_DENIED_WITH_NEVER_ASK_AGAIN -> {
binding.backIv.setOnClickListener(this)
binding.selfieImageIv.visibility = View.GONE
binding.camera.visibility = View.GONE
binding.closeIv.visibility = View.GONE
binding.focusMaskIv.visibility = View.VISIBLE
binding.cameraCaptureIv.visibility = View.INVISIBLE
binding.permissionDeniedLayout.visibility = View.VISIBLE
}
}
}
private fun initBeforeScreenCapture() {
binding.closeIv.visibility = View.GONE
binding.focusMaskIv.visibility = View.VISIBLE
binding.cameraCaptureIv.visibility = View.VISIBLE
binding.cameraActionButton.visibility = View.GONE
binding.selfieImageIv.visibility = View.GONE
override fun onRestart() {
super.onRestart()
if (permissionsManager.hasPermissions(permissions as Array<String>)) {
binding = DataBindingUtil.setContentView(this, R.layout.selfie_capture_activity)
renderState(CAMERA_PERMISSION_GRANTED)
} else if (!ActivityCompat.shouldShowRequestPermissionRationale(
this,
permissions.first()
)
) {
renderState(CAMERA_PERMISSION_DENIED_WITH_NEVER_ASK_AGAIN)
} else {
binding = DataBindingUtil.setContentView(this, R.layout.selfie_capture_activity)
binding.permissionDeniedLayout.visibility = View.GONE
binding.cameraCaptureIv.setOnClickListener(this)
}
}
private fun navigateToKycScreen() {
val intent = Intent(this, GetLoanActivity::class.java)
startActivity(intent)
private fun isDontAskAgainClicked(permission: String, grantResult: Int): Boolean {
return !ActivityCompat.shouldShowRequestPermissionRationale(this, permission) &&
grantResult == PackageManager.PERMISSION_DENIED
}
private fun setState(state: Int, bitmap: Bitmap? = null) {
when (state) {
INITIAL_SETUP -> {
binding.cameraCaptureIv.setOnClickListener(this)
binding.cameraActionButton.setProperties(
getString(R.string.confirm),
iconId = R.drawable.ic_check_svg
)
binding.cameraActionButton.setOnClickListener(this)
binding.closeIv.setOnClickListener(this)
binding.backIv.setOnClickListener(this)
binding.camera.setLifecycleOwner(this)
binding.camera.addCameraListener(CameraEventListener(cameraActionListener))
}
BEFORE_IMAGE_CAPTURE -> {
binding.permissionDeniedLayout.visibility = View.GONE
binding.closeIv.visibility = View.GONE
binding.focusMaskIv.visibility = View.VISIBLE
binding.cameraCaptureIv.visibility = View.VISIBLE
binding.cameraActionButton.visibility = View.GONE
binding.selfieImageIv.visibility = View.GONE
}
AFTER_IMAGE_CAPTURE -> {
binding.closeIv.visibility = View.VISIBLE
binding.cameraCaptureIv.visibility = View.INVISIBLE
binding.cameraActionButton.visibility = View.VISIBLE
binding.selfieImageIv.setImageBitmap(bitmap)
binding.selfieImageIv.visibility = View.VISIBLE
binding.focusMaskIv.visibility = View.GONE
}
}
}
private fun capturePicture() {
if (binding.camera.isTakingPicture) return
if (binding.camera.preview != Preview.GL_SURFACE) {
return
}
binding.camera.takePicture()
}
private fun openCamera() {
initBeforeScreenCapture()
if (!binding.camera.isVisible) binding.camera.visibility = View.VISIBLE
if (!binding.camera.isOpened) {
binding.camera.open()
@@ -136,10 +189,26 @@ class SelfieCaptureActivity : BaseActivity(), View.OnClickListener {
}
}
private fun navigateToKycScreen() {
val intent = Intent()
setResult(Activity.RESULT_OK, intent)
finish()
}
override val screenName: String
get() = ""
override fun onClick(view: View?) {
when (view?.id) {
R.id.close_iv -> init()
R.id.camera_capture_iv -> capturePicture()
R.id.camera_capture_iv -> {
if (permissionsManager.hasPermissions(permissions)) capturePicture()
else permissionsManager.requestPermissions(permissions)
}
R.id.back_iv -> navigateToKycScreen()
R.id.close_iv -> {
setState(BEFORE_IMAGE_CAPTURE)
openCamera()
}
R.id.camera_action_button -> {
captureImageBitmap.observeNullable(this) { bitmap ->
bitmap?.let {
@@ -147,20 +216,25 @@ class SelfieCaptureActivity : BaseActivity(), View.OnClickListener {
}
}
}
R.id.back_iv -> {
if (binding.focusMaskIv.isVisible) navigateToKycScreen() else init()
}
}
}
private fun capturePicture() {
if (binding.camera.isTakingPicture) return
if (binding.camera.preview != Preview.GL_SURFACE) {
return
private fun handleAfterCaptureImage(imageBitamp: Bitmap) {
try {
val intent = Intent()
intent.putExtra(DATA, putBitmapIntoFileAndGetFileName(this, imageBitamp))
setResult(Activity.RESULT_OK, intent)
finish()
} catch (e: Exception) {
e.printStackTrace()
}
binding.camera.takePicture()
}
override val screenName: String
get() = ""
companion object {
private const val CAMERA_PERMISSION_GRANTED = 0
private const val CAMERA_PERMISSION_DENIED_WITH_NEVER_ASK_AGAIN = 1
private const val INITIAL_SETUP = 20
private const val BEFORE_IMAGE_CAPTURE = 21
private const val AFTER_IMAGE_CAPTURE = 22
}
}

View File

@@ -3,20 +3,12 @@
android:height="82dp"
android:viewportWidth="84"
android:viewportHeight="82">
<path
android:pathData="M72.765,41a30,30.588 90.0001,1 1,-61.176 0a30,30.588 90.0001,1 1,61.176 0z"
android:fillColor="#000"
android:fillType="evenOdd"/>
<path
android:pathData="M72.765,41a30,30.588 90.0001,1 1,-61.176 0a30,30.588 90.0001,1 1,61.176 0z"
android:strokeWidth="2"
android:fillColor="#FFF"
android:strokeColor="#F33"
android:fillType="evenOdd"/>
<path
android:pathData="M42.177,41m26.471,0a26.471,26.471 0,1 0,-52.942 0a26.471,26.471 0,1 0,52.942 0"
android:fillColor="#000"
android:fillType="evenOdd"/>
<path
android:pathData="M42.177,41m26.471,0a26.471,26.471 0,1 0,-52.942 0a26.471,26.471 0,1 0,52.942 0"
android:fillColor="#F33"

View File

@@ -13,91 +13,104 @@
android:background="@color/white">
<ImageView
style="@style/ContainerStyle1"
android:id="@+id/back_iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_dp_30"
android:layout_marginTop="@dimen/layout_dp_16"
android:layout_marginEnd="@dimen/layout_dp_22"
android:layout_marginStart="@dimen/layout_dp_30"
android:src="@drawable/ic_back_arrow_svg"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/selfie_image_iv"
style="@style/ContainerStyle1"
android:layout_width="0dp"
<TextView
android:id="@+id/photo_instructions_tv"
style="@style/BannerSubHeadingFontStyle"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:background="@drawable/image_placeholder"
android:scaleType="centerCrop"
android:layout_marginTop="@dimen/layout_dp_40"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintTop_toBottomOf="@id/back_iv" />
<ImageView
android:id="@+id/close_iv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_dp_22"
android:layout_marginEnd="@dimen/layout_dp_22"
android:src="@drawable/ic_close_black_background_svg"
app:layout_constraintEnd_toEndOf="@id/selfie_image_iv"
app:layout_constraintTop_toTopOf="@id/selfie_image_iv" />
<!-- Camera -->
<com.otaliastudios.cameraview.CameraView
android:id="@+id/camera"
style="@style/ContainerStyle1"
android:layout_width="0dp"
android:layout_height="0dp"
android:keepScreenOn="true"
app:cameraAudio="off"
app:cameraAutoFocusMarker="@string/cameraview_default_autofocus_marker"
app:cameraEngine="camera2"
app:cameraExperimental="true"
app:cameraFacing="front"
app:cameraFlash="auto"
app:cameraGestureLongTap="none"
app:cameraGestureScrollHorizontal="filterControl1"
app:cameraGestureScrollVertical="exposureCorrection"
app:cameraGestureTap="autoFocus"
app:cameraGrid="off"
app:cameraMode="picture"
app:cameraPlaySounds="false"
app:cameraPreview="glSurface"
app:layout_constraintDimensionRatio="H,1:1"
android:layout_marginTop="@dimen/layout_dp_40"
android:layout_marginTop="@dimen/layout_dp_16"
android:text="@string/take_a_clear_photo"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/back_iv" />
<ImageView
android:id="@+id/focus_mask_iv"
<androidx.cardview.widget.CardView
android:id="@+id/camera_card_view"
style="@style/ContainerStyle1"
android:layout_width="0dp"
android:layout_height="0dp"
android:src="@drawable/ic_camera_mask_svg"
app:layout_constraintDimensionRatio="H,1:1"
android:layout_marginTop="@dimen/layout_dp_40"
android:layout_marginTop="@dimen/layout_dp_24"
android:layout_marginBottom="@dimen/layout_dp_24"
app:cardCornerRadius="@dimen/layout_dp_10"
android:background="@drawable/box_input_background"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/back_iv" />
app:layout_constraintBottom_toTopOf="@+id/camera_capture_iv"
app:layout_constraintTop_toBottomOf="@id/photo_instructions_tv">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline1"
<ImageView
android:id="@+id/selfie_image_iv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/image_placeholder"
android:scaleType="fitXY"
android:scaleX="-1" />
<!-- Camera -->
<com.otaliastudios.cameraview.CameraView
android:id="@+id/camera"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"
app:cameraAudio="off"
app:cameraAutoFocusMarker="@string/cameraview_default_autofocus_marker"
app:cameraEngine="camera2"
app:cameraExperimental="true"
app:cameraFacing="front"
app:cameraFlash="auto"
app:cameraGestureLongTap="none"
app:cameraGestureScrollHorizontal="filterControl1"
app:cameraGestureScrollVertical="exposureCorrection"
app:cameraGestureTap="autoFocus"
app:cameraGrid="off"
app:cameraMode="picture"
app:cameraPlaySounds="false"
app:cameraPreview="glSurface" />
<ImageView
android:id="@+id/focus_mask_iv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="@drawable/ic_camera_mask_svg" />
<ImageView
android:id="@+id/close_iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="@dimen/layout_dp_22"
android:layout_marginEnd="@dimen/layout_dp_22"
android:src="@drawable/ic_close_black_background_svg" />
</androidx.cardview.widget.CardView>
<com.navi.common.customview.PermissionDeniedView
android:id="@+id/permission_denied_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.6" />
android:visibility="gone"
android:layout_marginBottom="@dimen/layout_dp_24"
app:layout_constraintBottom_toTopOf="@id/selfie_instructions_layout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ImageView
android:id="@+id/camera_capture_iv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/layout_dp_24"
android:src="@drawable/ic_camera_capture_svg"
app:layout_constraintTop_toBottomOf="@id/guideline1"
app:layout_constraintBottom_toTopOf="@id/selfie_instructions_layout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
@@ -108,16 +121,21 @@
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_dp_30"
android:layout_marginEnd="@dimen/layout_dp_30"
app:layout_constraintTop_toBottomOf="@id/guideline1"
android:layout_marginBottom="@dimen/layout_dp_32"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/selfie_instructions_layout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<include
android:id="@+id/selfie_instructions_layout"
style="@style/ContainerStyle1"
layout="@layout/selfie_instructions_layout"
app:layout_constraintTop_toBottomOf="@id/camera_action_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/layout_dp_32"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -10,13 +10,6 @@
android:layout_marginBottom="@dimen/layout_dp_56"
tools:showIn="@layout/selfie_capture_activity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.1" />
<TextView
android:id="@+id/selfie_instructions_tv"
style="@style/SubheadingFontStyle"
@@ -24,7 +17,7 @@
android:layout_height="wrap_content"
android:text="@string/selfie_instructions"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@id/guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<ImageView
@@ -33,20 +26,19 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_dp_22"
android:src="@drawable/ic_light_svg"
app:layout_constraintStart_toStartOf="@id/guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/selfie_instructions_tv" />
<TextView
android:id="@+id/light_tv"
style="@style/ParagraphOneFontStyle"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_dp_16"
android:layout_marginTop="@dimen/layout_dp_22"
android:text="@string/bright_light"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/light_iv"
app:layout_constraintTop_toBottomOf="@id/selfie_instructions_tv" />
app:layout_constraintTop_toTopOf="@id/light_iv"
app:layout_constraintBottom_toBottomOf="@id/light_iv"/>
<ImageView
android:id="@+id/hat_iv"
@@ -61,14 +53,13 @@
<TextView
android:id="@+id/hat_tv"
style="@style/ParagraphOneFontStyle"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_dp_16"
android:layout_marginTop="@dimen/layout_dp_22"
android:text="@string/no_hat"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/hat_iv"
app:layout_constraintTop_toBottomOf="@id/selfie_instructions_tv"/>
app:layout_constraintTop_toTopOf="@id/hat_iv"
app:layout_constraintBottom_toBottomOf="@id/hat_iv"/>
<ImageView
android:id="@+id/glasses_iv"
@@ -76,18 +67,18 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_dp_22"
android:src="@drawable/ic_glasses_svg"
app:layout_constraintStart_toStartOf="@id/guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/hat_tv" />
<TextView
android:id="@+id/glasses_tv"
style="@style/ParagraphOneFontStyle"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_dp_16"
android:layout_marginTop="@dimen/layout_dp_22"
android:text="@string/no_glasses"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/glasses_iv"
app:layout_constraintBottom_toBottomOf="@id/glasses_iv"
app:layout_constraintStart_toEndOf="@id/glasses_iv"
app:layout_constraintTop_toBottomOf="@id/light_tv" />
@@ -107,9 +98,9 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/layout_dp_16"
android:layout_marginTop="@dimen/layout_dp_22"
android:text="@string/no_blur"
app:layout_constraintTop_toTopOf="@id/blur_iv"
app:layout_constraintBottom_toBottomOf="@id/blur_iv"
app:layout_constraintStart_toEndOf="@id/blur_iv"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/hat_tv" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -55,7 +55,7 @@
<dimen name="action_view_elevation">2dp</dimen>
<!-- New UI -->
<dimen name="container_padding">20dp</dimen>
<dimen name="container_padding">30dp</dimen>
<dimen name="container_padding_extra">30dp</dimen>
<dimen name="container_half_padding">10dp</dimen>
<dimen name="container_double_padding">40dp</dimen>

View File

@@ -377,5 +377,5 @@
<string name="all_mandatory_permissions">All permissions are mandatory</string>
<string name="permissions_location"><![CDATA[Phone Settings > Apps > Navi > Permissions]]></string>
<string name="approve_permissions">Approve Permissions</string>
>>>>>>> new-design
<string name="take_a_clear_photo">Take a clear front view photo</string>
</resources>

View File

@@ -129,8 +129,8 @@
</style>
<style name="ContainerStyle1">
<item name="android:layout_marginStart">@dimen/container_margin</item>
<item name="android:layout_marginEnd">@dimen/container_margin</item>
<item name="android:layout_marginStart">30dp</item>
<item name="android:layout_marginEnd">30dp</item>
</style>
<style name="ActionTxtStyle" parent="@android:style/Widget.TextView">