Blink Recognition
This commit is contained in:
@@ -18,11 +18,14 @@
|
||||
|
||||
package xyz.quaver.pupil.ui
|
||||
|
||||
import android.Manifest
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Animatable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import android.view.*
|
||||
@@ -31,9 +34,9 @@ import android.view.animation.AnticipateInterpolator
|
||||
import android.view.animation.OvershootInterpolator
|
||||
import android.view.animation.TranslateAnimation
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.PagerSnapHelper
|
||||
@@ -43,6 +46,7 @@ import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
||||
import com.bumptech.glide.Glide
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.google.mlkit.vision.face.Face
|
||||
import com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller
|
||||
import kotlinx.android.synthetic.main.activity_reader.*
|
||||
import kotlinx.android.synthetic.main.activity_reader.view.*
|
||||
@@ -50,6 +54,7 @@ import kotlinx.android.synthetic.main.dialog_numberpicker.view.*
|
||||
import kotlinx.android.synthetic.main.reader_eye_card.view.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import xyz.quaver.Code
|
||||
import xyz.quaver.pupil.R
|
||||
@@ -62,7 +67,7 @@ import xyz.quaver.pupil.util.camera
|
||||
import xyz.quaver.pupil.util.closeCamera
|
||||
import xyz.quaver.pupil.util.downloader.Cache
|
||||
import xyz.quaver.pupil.util.downloader.DownloadManager
|
||||
import xyz.quaver.pupil.util.testCamera
|
||||
import xyz.quaver.pupil.util.startCamera
|
||||
import java.util.*
|
||||
import kotlin.concurrent.schedule
|
||||
|
||||
@@ -97,11 +102,29 @@ class ReaderActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
private val timer = Timer()
|
||||
|
||||
private val snapHelper = PagerSnapHelper()
|
||||
|
||||
private var menu: Menu? = null
|
||||
|
||||
private val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
|
||||
if (isGranted)
|
||||
toggleCamera()
|
||||
else
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.error)
|
||||
.setMessage(R.string.camera_denied)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->}
|
||||
.show()
|
||||
}
|
||||
|
||||
enum class Eye {
|
||||
LEFT,
|
||||
RIGHT
|
||||
}
|
||||
|
||||
private var cameraEnabled = false
|
||||
private var eyeType: Eye? = null
|
||||
private var eyeCount: Int = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_reader)
|
||||
@@ -226,6 +249,18 @@ class ReaderActivity : BaseActivity() {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
if (cameraEnabled)
|
||||
startCamera(this, cameraCallback)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
closeCamera()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
|
||||
@@ -390,66 +425,21 @@ class ReaderActivity : BaseActivity() {
|
||||
}
|
||||
|
||||
with(reader_fab_auto) {
|
||||
setImageResource(R.drawable.clock_start)
|
||||
setImageResource(R.drawable.eye_white)
|
||||
setOnClickListener {
|
||||
val eyes = this@ReaderActivity.eye_card
|
||||
when (camera) {
|
||||
null -> {
|
||||
eyes.apply {
|
||||
visibility = View.VISIBLE
|
||||
TranslateAnimation(0F, 0F, -100F, 0F).apply {
|
||||
duration = 500
|
||||
fillAfter = false
|
||||
interpolator = OvershootInterpolator()
|
||||
}.let { startAnimation(it) }
|
||||
}
|
||||
testCamera(context) { faces ->
|
||||
eyes.dot.let {
|
||||
it.visibility = View.VISIBLE
|
||||
Timer().schedule(50) {
|
||||
runOnUiThread {
|
||||
it.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (faces.size != 1)
|
||||
ResourcesCompat.getDrawable(resources, R.drawable.eye_off, context.theme).let {
|
||||
eyes.left_eye.setImageDrawable(it)
|
||||
eyes.right_eye.setImageDrawable(it)
|
||||
|
||||
return@testCamera
|
||||
}
|
||||
|
||||
val left = ResourcesCompat.getDrawable(resources,
|
||||
if (faces[0].rightEyeOpenProbability?.let { it > 0.4 } == true) R.drawable.eye else R.drawable.eye_closed,
|
||||
context.theme)
|
||||
val right = ResourcesCompat.getDrawable(resources,
|
||||
if (faces[0].leftEyeOpenProbability?.let { it > 0.4 } == true) R.drawable.eye else R.drawable.eye_closed,
|
||||
context.theme)
|
||||
|
||||
eyes.left_eye.setImageDrawable(left)
|
||||
eyes.right_eye.setImageDrawable(right)
|
||||
}
|
||||
when {
|
||||
ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED -> {
|
||||
toggleCamera()
|
||||
}
|
||||
else -> {
|
||||
eyes.apply {
|
||||
TranslateAnimation(0F, 0F, 0F, -100F).apply {
|
||||
duration = 500
|
||||
fillAfter = false
|
||||
interpolator = AnticipateInterpolator()
|
||||
setAnimationListener(object: Animation.AnimationListener {
|
||||
override fun onAnimationStart(p0: Animation?) {}
|
||||
override fun onAnimationRepeat(p0: Animation?) {}
|
||||
|
||||
override fun onAnimationEnd(p0: Animation?) {
|
||||
eyes.visibility = View.GONE
|
||||
}
|
||||
})
|
||||
}.let { startAnimation(it) }
|
||||
}
|
||||
closeCamera()
|
||||
Build.VERSION.SDK_INT >= 23 && shouldShowRequestPermissionRationale(Manifest.permission.CAMERA) -> {
|
||||
AlertDialog.Builder(this@ReaderActivity)
|
||||
.setTitle(R.string.warning)
|
||||
.setMessage(R.string.camera_denied)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->}
|
||||
.show()
|
||||
}
|
||||
else ->
|
||||
requestPermissionLauncher.launch(Manifest.permission.CAMERA)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -502,6 +492,17 @@ class ReaderActivity : BaseActivity() {
|
||||
} else {
|
||||
snapHelper.attachToRecyclerView(reader_recyclerview)
|
||||
reader_recyclerview.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, Preferences["rtl", false])
|
||||
|
||||
if (Preferences["rtl", false])
|
||||
with(reader_progressbar) {
|
||||
scaleX = -1F
|
||||
translationX = 1F
|
||||
}
|
||||
else
|
||||
with(reader_progressbar) {
|
||||
scaleX = 0F
|
||||
translationX = 0F
|
||||
}
|
||||
}
|
||||
|
||||
(reader_recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage-1, 0)
|
||||
@@ -536,6 +537,125 @@ class ReaderActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
val cameraCallback: (List<Face>) -> Unit = callback@{ faces ->
|
||||
eye_card.dot.let {
|
||||
it.visibility = View.VISIBLE
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
delay(50)
|
||||
it.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
if (faces.size != 1)
|
||||
ContextCompat.getDrawable(this, R.drawable.eye_off).let {
|
||||
with(eye_card) {
|
||||
left_eye.setImageDrawable(it)
|
||||
right_eye.setImageDrawable(it)
|
||||
}
|
||||
|
||||
return@callback
|
||||
}
|
||||
|
||||
val (left, right) = Pair(
|
||||
faces[0].rightEyeOpenProbability?.let { it > 0.4 } == true,
|
||||
faces[0].leftEyeOpenProbability?.let { it > 0.4 } == true
|
||||
)
|
||||
|
||||
with(eye_card) {
|
||||
left_eye.setImageDrawable(
|
||||
ContextCompat.getDrawable(
|
||||
context,
|
||||
if (left) R.drawable.eye else R.drawable.eye_closed
|
||||
)
|
||||
)
|
||||
right_eye.setImageDrawable(
|
||||
ContextCompat.getDrawable(
|
||||
context,
|
||||
if (right) R.drawable.eye else R.drawable.eye_closed
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
when {
|
||||
// Both closed / opened
|
||||
!left.xor(right) -> {
|
||||
eyeType = null
|
||||
eyeCount = 0
|
||||
}
|
||||
!left -> {
|
||||
if (eyeType != Eye.LEFT) {
|
||||
eyeType = Eye.LEFT
|
||||
eyeCount = 0
|
||||
}
|
||||
eyeCount++
|
||||
}
|
||||
!right -> {
|
||||
if (eyeType != Eye.RIGHT) {
|
||||
eyeType = Eye.RIGHT
|
||||
eyeCount = 0
|
||||
}
|
||||
eyeCount++
|
||||
}
|
||||
}
|
||||
|
||||
if (eyeCount > 3) {
|
||||
(this@ReaderActivity.reader_recyclerview.layoutManager as LinearLayoutManager).let {
|
||||
it.scrollToPositionWithOffset(when(eyeType!!) {
|
||||
Eye.RIGHT -> {
|
||||
if (it.reverseLayout) currentPage - 2 else currentPage
|
||||
}
|
||||
Eye.LEFT -> {
|
||||
if (it.reverseLayout) currentPage else currentPage - 2
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
|
||||
eyeType = null
|
||||
eyeCount = 0
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleCamera() {
|
||||
val eyes = this@ReaderActivity.eye_card
|
||||
when (camera) {
|
||||
null -> {
|
||||
reader_fab_auto.labelText = getString(R.string.reader_fab_auto_cancel)
|
||||
reader_fab_auto.setImageResource(R.drawable.eye_off_white)
|
||||
eyes.apply {
|
||||
visibility = View.VISIBLE
|
||||
TranslateAnimation(0F, 0F, -100F, 0F).apply {
|
||||
duration = 500
|
||||
fillAfter = false
|
||||
interpolator = OvershootInterpolator()
|
||||
}.let { startAnimation(it) }
|
||||
}
|
||||
startCamera(this, cameraCallback)
|
||||
cameraEnabled = true
|
||||
}
|
||||
else -> {
|
||||
reader_fab_auto.labelText = getString(R.string.reader_fab_auto)
|
||||
reader_fab_auto.setImageResource(R.drawable.eye_white)
|
||||
eyes.apply {
|
||||
TranslateAnimation(0F, 0F, 0F, -100F).apply {
|
||||
duration = 500
|
||||
fillAfter = false
|
||||
interpolator = AnticipateInterpolator()
|
||||
setAnimationListener(object: Animation.AnimationListener {
|
||||
override fun onAnimationStart(p0: Animation?) {}
|
||||
override fun onAnimationRepeat(p0: Animation?) {}
|
||||
|
||||
override fun onAnimationEnd(p0: Animation?) {
|
||||
eyes.visibility = View.GONE
|
||||
}
|
||||
})
|
||||
}.let { startAnimation(it) }
|
||||
}
|
||||
closeCamera()
|
||||
cameraEnabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLowMemory() {
|
||||
super.onLowMemory()
|
||||
Glide.get(this).onLowMemory()
|
||||
|
||||
Reference in New Issue
Block a user