diff --git a/app/src/main/java/com/cleveroad/aropensource/ui/base/BaseLifecycleActivity.kt b/app/src/main/java/com/cleveroad/aropensource/ui/base/BaseLifecycleActivity.kt index 3b4d132..7503a6c 100644 --- a/app/src/main/java/com/cleveroad/aropensource/ui/base/BaseLifecycleActivity.kt +++ b/app/src/main/java/com/cleveroad/aropensource/ui/base/BaseLifecycleActivity.kt @@ -18,12 +18,10 @@ abstract class BaseLifecycleActivity : AppCompatActivity() { setContentView(layoutId) } - protected fun replaceFragment( - fragment: Fragment, - needToAddToBackStack: Boolean = true, - @AnimRes inAnimRes: Int = 0, - @AnimRes outAnimRes: Int = 0 - ) { + protected fun replaceFragment(fragment: Fragment, + needToAddToBackStack: Boolean = true, + @AnimRes inAnimRes: Int = 0, + @AnimRes outAnimRes: Int = 0) { val name = fragment.javaClass.simpleName supportFragmentManager.beginTransaction().apply { if (inAnimRes != 0 || outAnimRes != 0) setCustomAnimations(inAnimRes, outAnimRes) @@ -66,6 +64,6 @@ abstract class BaseLifecycleActivity : AppCompatActivity() { override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) supportFragmentManager.findFragmentById(containerId) - ?.onRequestPermissionsResult(requestCode, permissions, grantResults) + ?.onRequestPermissionsResult(requestCode, permissions, grantResults) } } \ No newline at end of file diff --git a/app/src/main/java/com/cleveroad/aropensource/ui/base/BaseLifecycleFragment.kt b/app/src/main/java/com/cleveroad/aropensource/ui/base/BaseLifecycleFragment.kt index ae2d7d0..394cec7 100644 --- a/app/src/main/java/com/cleveroad/aropensource/ui/base/BaseLifecycleFragment.kt +++ b/app/src/main/java/com/cleveroad/aropensource/ui/base/BaseLifecycleFragment.kt @@ -28,7 +28,7 @@ abstract class BaseLifecycleFragment : Fragment() { protected var toolbar: Toolbar? = null override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = - inflater.inflate(layoutId, container, false) + inflater.inflate(layoutId, container, false) override fun onResume() { super.onResume() diff --git a/app/src/main/java/com/cleveroad/aropensource/ui/base/Extensions.kt b/app/src/main/java/com/cleveroad/aropensource/ui/base/Extensions.kt index 72a1134..906d84c 100644 --- a/app/src/main/java/com/cleveroad/aropensource/ui/base/Extensions.kt +++ b/app/src/main/java/com/cleveroad/aropensource/ui/base/Extensions.kt @@ -8,5 +8,5 @@ package com.cleveroad.aropensource.ui.base * @return first object which implement interface [T] */ inline fun bindInterfaceOrThrow(vararg objects: Any?): T = - objects.find { it is T } as T - ?: throw NotImplementedInterfaceException(T::class.java) \ No newline at end of file + objects.find { it is T } as T + ?: throw NotImplementedInterfaceException(T::class.java) \ No newline at end of file diff --git a/app/src/main/java/com/cleveroad/aropensource/ui/screens/main/chooser/InstrumentsFragment.kt b/app/src/main/java/com/cleveroad/aropensource/ui/screens/main/chooser/InstrumentsFragment.kt index 768834d..e3518d5 100644 --- a/app/src/main/java/com/cleveroad/aropensource/ui/screens/main/chooser/InstrumentsFragment.kt +++ b/app/src/main/java/com/cleveroad/aropensource/ui/screens/main/chooser/InstrumentsFragment.kt @@ -12,9 +12,9 @@ class InstrumentsFragment : BaseLifecycleFragment(), View.OnClickListener { companion object { fun newInstance() = - InstrumentsFragment().apply { - arguments = Bundle() - } + InstrumentsFragment().apply { + arguments = Bundle() + } } override val layoutId = R.layout.fragment_instruments diff --git a/gradle.properties b/gradle.properties index 23339e0..c73d239 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,5 +17,3 @@ org.gradle.jvmargs=-Xmx1536m android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=true -# Kotlin code style for this project: "official" or "obsolete": -kotlin.code.style=official diff --git a/lib/src/main/java/com/cleveroad/arfacedetector/ui/base/BaseLifecycleFragment.kt b/lib/src/main/java/com/cleveroad/arfacedetector/ui/base/BaseLifecycleFragment.kt index 6814181..3652eff 100644 --- a/lib/src/main/java/com/cleveroad/arfacedetector/ui/base/BaseLifecycleFragment.kt +++ b/lib/src/main/java/com/cleveroad/arfacedetector/ui/base/BaseLifecycleFragment.kt @@ -46,7 +46,7 @@ abstract class BaseLifecycleFragment : Fragment() { } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = - inflater.inflate(layoutId, container, false) + inflater.inflate(layoutId, container, false) override fun onResume() { super.onResume() @@ -157,16 +157,16 @@ abstract class BaseLifecycleFragment : Fragment() { * @return Screen title */ private fun getStringScreenTitle() = - when (getScreenTitle()) { - NO_TITLE -> EMPTY_STRING - else -> getString(getScreenTitle()) - } + when (getScreenTitle()) { + NO_TITLE -> EMPTY_STRING + else -> getString(getScreenTitle()) + } @SuppressWarnings("SpreadOperator") protected fun requestPermission( - vararg permission: String, - isDeniedCallback: () -> Unit = { }, - isGrantedCallback: () -> Unit + vararg permission: String, + isDeniedCallback: () -> Unit = { }, + isGrantedCallback: () -> Unit ) { permissionDisposable?.add(rxPermission?.request(*permission)?.subscribe { granted -> if (granted) isGrantedCallback() else isDeniedCallback() diff --git a/lib/src/main/java/com/cleveroad/arfacedetector/ui/base/Extensions.kt b/lib/src/main/java/com/cleveroad/arfacedetector/ui/base/Extensions.kt index f13f621..6684249 100644 --- a/lib/src/main/java/com/cleveroad/arfacedetector/ui/base/Extensions.kt +++ b/lib/src/main/java/com/cleveroad/arfacedetector/ui/base/Extensions.kt @@ -1,4 +1,4 @@ package com.cleveroad.arfacedetector.ui.base fun safeLet(p1: T1?, p2: T2?, block: (T1, T2) -> R?): R? = - if (p1 == null || p2 == null) null else block(p1, p2) \ No newline at end of file + if (p1 == null || p2 == null) null else block(p1, p2) \ No newline at end of file diff --git a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/arcore/face_detector/AugmentedFacesFragment.kt b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/arcore/face_detector/AugmentedFacesFragment.kt index 00a625c..661ebfb 100644 --- a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/arcore/face_detector/AugmentedFacesFragment.kt +++ b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/arcore/face_detector/AugmentedFacesFragment.kt @@ -21,11 +21,11 @@ class AugmentedFacesFragment : BaseLifecycleFragment() { private const val RES_ID_EXTRA = "res_id" fun newInstance(@DrawableRes resId: Int) = - AugmentedFacesFragment().apply { - arguments = Bundle().apply { - putInt(RES_ID_EXTRA, resId) + AugmentedFacesFragment().apply { + arguments = Bundle().apply { + putInt(RES_ID_EXTRA, resId) + } } - } } override val layoutId = R.layout.fragment_augmented_faces @@ -71,10 +71,10 @@ class AugmentedFacesFragment : BaseLifecycleFragment() { if (!faceNodeMap.containsKey(face)) { safeLet(bitmap, context) { bitmap, context -> faceNodeMap[face] = - CustomAugmentedFaceNode(face, context, bitmap) - .apply { - setParent(sceneView.scene) - } + CustomAugmentedFaceNode(face, context, bitmap) + .apply { + setParent(sceneView.scene) + } } } } diff --git a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/arcore/face_detector/CustomAugmentedFaceNode.kt b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/arcore/face_detector/CustomAugmentedFaceNode.kt index 172c987..279001d 100644 --- a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/arcore/face_detector/CustomAugmentedFaceNode.kt +++ b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/arcore/face_detector/CustomAugmentedFaceNode.kt @@ -18,7 +18,7 @@ import com.google.ar.sceneform.ux.AugmentedFaceNode class CustomAugmentedFaceNode(augmentedFace: AugmentedFace?, private val context: Context, private val bitmap: Bitmap) : - AugmentedFaceNode(augmentedFace) { + AugmentedFaceNode(augmentedFace) { companion object { private const val HALF_DIVIDER = 2 @@ -33,23 +33,23 @@ class CustomAugmentedFaceNode(augmentedFace: AugmentedFace?, private val context logoNode?.setParent(this) logoNode?.isEnabled = isTracking() - val imageView = LayoutInflater.from(context).inflate(R.layout.logo_view, null) as ImageView + val imageView = LayoutInflater.from(context).inflate(R.layout.image_node_view, null) as ImageView imageView.setImageBitmap(bitmap) ViewRenderable.builder() - .setView(context, imageView) - .build() - .thenAccept { renderable -> - logoNode?.renderable = renderable - } - .exceptionally { throwable -> - Toast.makeText(context, "Could not load plane card view.", Toast.LENGTH_LONG).show() - throw AssertionError("Could not load plane card view.", throwable) - } + .setView(context, imageView) + .build() + .thenAccept { renderable -> + logoNode?.renderable = renderable + } + .exceptionally { throwable -> + Toast.makeText(context, "Could not load plane card view.", Toast.LENGTH_LONG).show() + throw AssertionError("Could not load plane card view.", throwable) + } } private fun isTracking() = - augmentedFace != null && augmentedFace?.trackingState == TrackingState.TRACKING + augmentedFace != null && augmentedFace?.trackingState == TrackingState.TRACKING override fun onUpdate(frameTime: FrameTime?) { super.onUpdate(frameTime) @@ -61,12 +61,11 @@ class CustomAugmentedFaceNode(augmentedFace: AugmentedFace?, private val context val leftHeard = getRegionPose(AugmentedFace.RegionType.FOREHEAD_LEFT) val zCoordinate = - (leftHeard.tz() + rightHeard.tz()) / HALF_DIVIDER + (getRegionPose(AugmentedFace.RegionType.NOSE_TIP).tz() - centerPose.tz()) * -1 - logoNode?.worldPosition = Vector3( - (leftHeard.tx() + rightHeard.tx()) / HALF_DIVIDER, - (leftHeard.ty() + rightHeard.ty()) / HALF_DIVIDER, - zCoordinate - ) + (leftHeard.tz() + rightHeard.tz()) / HALF_DIVIDER + (getRegionPose(AugmentedFace.RegionType.NOSE_TIP).tz() - centerPose.tz()) * -1 + logoNode?.worldPosition = + Vector3((leftHeard.tx() + rightHeard.tx()) / HALF_DIVIDER, + (leftHeard.ty() + rightHeard.ty()) / HALF_DIVIDER, + zCoordinate) } } } diff --git a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/arcore/face_detector/FaceDetectorFragment.kt b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/arcore/face_detector/FaceDetectorFragment.kt index 82f0fe5..91ad635 100644 --- a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/arcore/face_detector/FaceDetectorFragment.kt +++ b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/arcore/face_detector/FaceDetectorFragment.kt @@ -18,12 +18,12 @@ internal class FaceDetectorFragment : ArFragment() { } override fun getSessionConfiguration(session: Session?): Config = - Config(session).apply { - augmentedFaceMode = Config.AugmentedFaceMode.MESH3D - } + Config(session).apply { + augmentedFaceMode = Config.AugmentedFaceMode.MESH3D + } override fun getSessionFeatures(): MutableSet = - mutableSetOf(Session.Feature.FRONT_CAMERA) + mutableSetOf(Session.Feature.FRONT_CAMERA) /** * Override to turn off planeDiscoveryController. Plane trackables are not supported with the diff --git a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/FaceDetectorFragment.kt b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/FaceDetectorFragment.kt index 3ab13bb..ff95b9f 100644 --- a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/FaceDetectorFragment.kt +++ b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/FaceDetectorFragment.kt @@ -28,11 +28,11 @@ class FaceDetectorFragment : BaseLifecycleFragment(), CompoundButton.OnCheckedCh private const val RES_ID_EXTRA = "res_id" fun newInstance(@DrawableRes resId: Int) = - FaceDetectorFragment().apply { - arguments = Bundle().apply { - putInt(RES_ID_EXTRA, resId) + FaceDetectorFragment().apply { + arguments = Bundle().apply { + putInt(RES_ID_EXTRA, resId) + } } - } } override val layoutId = R.layout.ml_kit_face_detector_fragment @@ -61,7 +61,7 @@ class FaceDetectorFragment : BaseLifecycleFragment(), CompoundButton.OnCheckedCh override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) { cameraSource?.setFacing(if (isChecked) CAMERA_FACING_FRONT else CAMERA_FACING_BACK) - firePreview?.stop() + cameraSourcePreview?.stop() startCameraSource() } @@ -72,7 +72,7 @@ class FaceDetectorFragment : BaseLifecycleFragment(), CompoundButton.OnCheckedCh /** Stops the camera. */ override fun onPause() { - firePreview?.stop() + cameraSourcePreview?.stop() super.onPause() } @@ -89,9 +89,9 @@ class FaceDetectorFragment : BaseLifecycleFragment(), CompoundButton.OnCheckedCh private fun startCameraSource() { cameraSource?.let { try { - firePreview ?: Log.d(LOG_TAG, "resume: Preview is null") - fireFaceOverlay ?: Log.d(LOG_TAG, "resume: graphOverlay is null") - safeLet(firePreview, fireFaceOverlay) { firePreview, fireFaceOverlay -> + cameraSourcePreview ?: Log.d(LOG_TAG, "resume: Preview is null") + faceOverlay ?: Log.d(LOG_TAG, "resume: graphOverlay is null") + safeLet(cameraSourcePreview, faceOverlay) { firePreview, fireFaceOverlay -> firePreview.start(cameraSource, fireFaceOverlay) } } catch (e: IOException) { @@ -105,7 +105,7 @@ class FaceDetectorFragment : BaseLifecycleFragment(), CompoundButton.OnCheckedCh private fun createCameraSource() { // If there's no existing cameraSource, create one. if (cameraSource == null) { - cameraSource = CameraSource(activity, fireFaceOverlay) + cameraSource = CameraSource(activity, faceOverlay) } safeLet(context, arguments?.getInt(RES_ID_EXTRA)) { context, resId -> BitmapUtils.getBitmapFromVectorDrawable(context, resId)?.let { diff --git a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/CameraSource.java b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/CameraSource.java index d7eea36..be8a197 100644 --- a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/CameraSource.java +++ b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/CameraSource.java @@ -26,8 +26,10 @@ import android.view.Surface; import android.view.SurfaceHolder; import android.view.WindowManager; + import androidx.annotation.Nullable; import androidx.annotation.RequiresPermission; + import com.google.android.gms.common.images.Size; import java.io.IOException; diff --git a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/CameraSourcePreview.java b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/CameraSourcePreview.java index 8a7315e..ba550c7 100644 --- a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/CameraSourcePreview.java +++ b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/CameraSourcePreview.java @@ -21,159 +21,163 @@ import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.ViewGroup; + import com.google.android.gms.common.images.Size; import java.io.IOException; -/** Preview the camera image in the screen. */ +/** + * Preview the camera image in the screen. + */ public class CameraSourcePreview extends ViewGroup { - private static final String TAG = "MIDemoApp:Preview"; - - private Context context; - private SurfaceView surfaceView; - private boolean startRequested; - private boolean surfaceAvailable; - private CameraSource cameraSource; - - private GraphicOverlay overlay; - - public CameraSourcePreview(Context context, AttributeSet attrs) { - super(context, attrs); - this.context = context; - startRequested = false; - surfaceAvailable = false; - - surfaceView = new SurfaceView(context); - surfaceView.getHolder().addCallback(new SurfaceCallback()); - addView(surfaceView); - } - - public void start(CameraSource cameraSource) throws IOException { - if (cameraSource == null) { - stop(); - } + private static final String TAG = "MIDemoApp:Preview"; - this.cameraSource = cameraSource; + private Context context; + private SurfaceView surfaceView; + private boolean startRequested; + private boolean surfaceAvailable; + private CameraSource cameraSource; - if (this.cameraSource != null) { - startRequested = true; - startIfReady(); - } - } + private GraphicOverlay overlay; - public void start(CameraSource cameraSource, GraphicOverlay overlay) throws IOException { - this.overlay = overlay; - start(cameraSource); - } + public CameraSourcePreview(Context context, AttributeSet attrs) { + super(context, attrs); + this.context = context; + startRequested = false; + surfaceAvailable = false; - public void stop() { - if (cameraSource != null) { - cameraSource.stop(); + surfaceView = new SurfaceView(context); + surfaceView.getHolder().addCallback(new SurfaceCallback()); + addView(surfaceView); } - } - public void release() { - if (cameraSource != null) { - cameraSource.release(); - cameraSource = null; - } - } - - @SuppressLint("MissingPermission") - private void startIfReady() throws IOException { - if (startRequested && surfaceAvailable) { - cameraSource.start(); - if (overlay != null) { - Size size = cameraSource.getPreviewSize(); - int min = Math.min(size.getWidth(), size.getHeight()); - int max = Math.max(size.getWidth(), size.getHeight()); - if (isPortraitMode()) { - // Swap width and height sizes when in portrait, since it will be rotated by - // 90 degrees - overlay.setCameraInfo(min, max, cameraSource.getCameraFacing()); - } else { - overlay.setCameraInfo(max, min, cameraSource.getCameraFacing()); + public void start(CameraSource cameraSource) throws IOException { + if (cameraSource == null) { + stop(); + } + + this.cameraSource = cameraSource; + + if (this.cameraSource != null) { + startRequested = true; + startIfReady(); } - overlay.clear(); - } - startRequested = false; } - } - private class SurfaceCallback implements SurfaceHolder.Callback { - @Override - public void surfaceCreated(SurfaceHolder surface) { - surfaceAvailable = true; - try { - startIfReady(); - } catch (IOException e) { - Log.e(TAG, "Could not start camera source.", e); - } + public void start(CameraSource cameraSource, GraphicOverlay overlay) throws IOException { + this.overlay = overlay; + start(cameraSource); } - @Override - public void surfaceDestroyed(SurfaceHolder surface) { - surfaceAvailable = false; + public void stop() { + if (cameraSource != null) { + cameraSource.stop(); + } } - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {} - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - int width = 320; - int height = 240; - if (cameraSource != null) { - Size size = cameraSource.getPreviewSize(); - if (size != null) { - width = size.getWidth(); - height = size.getHeight(); - } + public void release() { + if (cameraSource != null) { + cameraSource.release(); + cameraSource = null; + } } - // Swap width and height sizes when in portrait, since it will be rotated 90 degrees - if (isPortraitMode()) { - int tmp = width; - width = height; - height = tmp; + @SuppressLint("MissingPermission") + private void startIfReady() throws IOException { + if (startRequested && surfaceAvailable) { + cameraSource.start(); + if (overlay != null) { + Size size = cameraSource.getPreviewSize(); + int min = Math.min(size.getWidth(), size.getHeight()); + int max = Math.max(size.getWidth(), size.getHeight()); + if (isPortraitMode()) { + // Swap width and height sizes when in portrait, since it will be rotated by + // 90 degrees + overlay.setCameraInfo(min, max, cameraSource.getCameraFacing()); + } else { + overlay.setCameraInfo(max, min, cameraSource.getCameraFacing()); + } + overlay.clear(); + } + startRequested = false; + } } - final int layoutWidth = right - left; - final int layoutHeight = bottom - top; + private class SurfaceCallback implements SurfaceHolder.Callback { + @Override + public void surfaceCreated(SurfaceHolder surface) { + surfaceAvailable = true; + try { + startIfReady(); + } catch (IOException e) { + Log.e(TAG, "Could not start camera source.", e); + } + } - // Computes height and width for potentially doing fit width. - int childWidth = layoutWidth; - int childHeight = (int) (((float) layoutWidth / (float) width) * height); + @Override + public void surfaceDestroyed(SurfaceHolder surface) { + surfaceAvailable = false; + } - // If height is too tall using fit width, does fit height instead. - if (childHeight > layoutHeight) { - childHeight = layoutHeight; - childWidth = (int) (((float) layoutHeight / (float) height) * width); + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + } } - for (int i = 0; i < getChildCount(); ++i) { - getChildAt(i).layout(0, 0, childWidth, childHeight); - Log.d(TAG, "Assigned view: " + i); - } + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + int width = 320; + int height = 240; + if (cameraSource != null) { + Size size = cameraSource.getPreviewSize(); + if (size != null) { + width = size.getWidth(); + height = size.getHeight(); + } + } - try { - startIfReady(); - } catch (IOException e) { - Log.e(TAG, "Could not start camera source.", e); - } - } + // Swap width and height sizes when in portrait, since it will be rotated 90 degrees + if (isPortraitMode()) { + int tmp = width; + width = height; + height = tmp; + } - private boolean isPortraitMode() { - int orientation = context.getResources().getConfiguration().orientation; - if (orientation == Configuration.ORIENTATION_LANDSCAPE) { - return false; - } - if (orientation == Configuration.ORIENTATION_PORTRAIT) { - return true; + final int layoutWidth = right - left; + final int layoutHeight = bottom - top; + + // Computes height and width for potentially doing fit width. + int childWidth = layoutWidth; + int childHeight = (int) (((float) layoutWidth / (float) width) * height); + + // If height is too tall using fit width, does fit height instead. + if (childHeight > layoutHeight) { + childHeight = layoutHeight; + childWidth = (int) (((float) layoutHeight / (float) height) * width); + } + + for (int i = 0; i < getChildCount(); ++i) { + getChildAt(i).layout(0, 0, childWidth, childHeight); + Log.d(TAG, "Assigned view: " + i); + } + + try { + startIfReady(); + } catch (IOException e) { + Log.e(TAG, "Could not start camera source.", e); + } } - Log.d(TAG, "isPortraitMode returning false by default"); - return false; - } + private boolean isPortraitMode() { + int orientation = context.getResources().getConfiguration().orientation; + if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + return false; + } + if (orientation == Configuration.ORIENTATION_PORTRAIT) { + return true; + } + + Log.d(TAG, "isPortraitMode returning false by default"); + return false; + } } diff --git a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/FrameMetadata.kt b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/FrameMetadata.kt index 9fc1407..cf2753d 100755 --- a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/FrameMetadata.kt +++ b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/FrameMetadata.kt @@ -1,3 +1,6 @@ package com.cleveroad.arfacedetector.ui.screens.main.mlkit.common -data class FrameMetadata(val width: Int, val height: Int, val rotation: Int, val cameraFacing: Int) \ No newline at end of file +data class FrameMetadata(val width: Int, + val height: Int, + val rotation: Int, + val cameraFacing: Int) \ No newline at end of file diff --git a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/GraphicOverlay.java b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/GraphicOverlay.java index ed9b261..ac8cc07 100755 --- a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/GraphicOverlay.java +++ b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/GraphicOverlay.java @@ -17,6 +17,7 @@ import android.graphics.Canvas; import android.util.AttributeSet; import android.view.View; + import com.google.android.gms.vision.CameraSource; import java.util.ArrayList; @@ -35,142 +36,154 @@ * coordinates for the graphics that are drawn: * *
    - *
  1. {@link Graphic#scaleX(float)} and {@link Graphic#scaleY(float)} adjust the size of the - * supplied value from the preview scale to the view scale. - *
  2. {@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the - * coordinate from the preview's coordinate system to the view coordinate system. + *
  3. {@link Graphic#scaleX(float)} and {@link Graphic#scaleY(float)} adjust the size of the + * supplied value from the preview scale to the view scale. + *
  4. {@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the + * coordinate from the preview's coordinate system to the view coordinate system. *
*/ public class GraphicOverlay extends View { - private final Object lock = new Object(); - private int previewWidth; - private float widthScaleFactor = 1.0f; - private int previewHeight; - private float heightScaleFactor = 1.0f; - private int facing = CameraSource.CAMERA_FACING_BACK; - private final List graphics = new ArrayList<>(); - - /** - * Base class for a custom graphics object to be rendered within the graphic overlay. Subclass - * this and implement the {@link Graphic#draw(Canvas)} method to define the graphics element. Add - * instances to the overlay using {@link GraphicOverlay#add(Graphic)}. - */ - public abstract static class Graphic { - private GraphicOverlay overlay; - - public Graphic(GraphicOverlay overlay) { - this.overlay = overlay; - } - - /** - * Draw the graphic on the supplied canvas. Drawing should use the following methods to convert - * to view coordinates for the graphics that are drawn: - * - *
    - *
  1. {@link Graphic#scaleX(float)} and {@link Graphic#scaleY(float)} adjust the size of the - * supplied value from the preview scale to the view scale. - *
  2. {@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the - * coordinate from the preview's coordinate system to the view coordinate system. - *
- * - * @param canvas drawing canvas - */ - public abstract void draw(Canvas canvas); + private final Object lock = new Object(); + private int previewWidth; + private float widthScaleFactor = 1.0f; + private int previewHeight; + private float heightScaleFactor = 1.0f; + private int facing = CameraSource.CAMERA_FACING_BACK; + private final List graphics = new ArrayList<>(); /** - * Adjusts a horizontal value of the supplied value from the preview scale to the view scale. + * Base class for a custom graphics object to be rendered within the graphic overlay. Subclass + * this and implement the {@link Graphic#draw(Canvas)} method to define the graphics element. Add + * instances to the overlay using {@link GraphicOverlay#add(Graphic)}. */ - public float scaleX(float horizontal) { - return horizontal * overlay.widthScaleFactor; + public abstract static class Graphic { + private GraphicOverlay overlay; + + public Graphic(GraphicOverlay overlay) { + this.overlay = overlay; + } + + /** + * Draw the graphic on the supplied canvas. Drawing should use the following methods to convert + * to view coordinates for the graphics that are drawn: + * + *
    + *
  1. {@link Graphic#scaleX(float)} and {@link Graphic#scaleY(float)} adjust the size of the + * supplied value from the preview scale to the view scale. + *
  2. {@link Graphic#translateX(float)} and {@link Graphic#translateY(float)} adjust the + * coordinate from the preview's coordinate system to the view coordinate system. + *
+ * + * @param canvas drawing canvas + */ + public abstract void draw(Canvas canvas); + + /** + * Adjusts a horizontal value of the supplied value from the preview scale to the view scale. + */ + public float scaleX(float horizontal) { + return horizontal * overlay.widthScaleFactor; + } + + /** + * Adjusts a vertical value of the supplied value from the preview scale to the view scale. + */ + public float scaleY(float vertical) { + return vertical * overlay.heightScaleFactor; + } + + /** + * Returns the application context of the app. + */ + public Context getApplicationContext() { + return overlay.getContext().getApplicationContext(); + } + + /** + * Adjusts the x coordinate from the preview's coordinate system to the view coordinate system. + */ + public float translateX(float x) { + if (overlay.facing == CameraSource.CAMERA_FACING_FRONT) { + return overlay.getWidth() - scaleX(x); + } else { + return scaleX(x); + } + } + + /** + * Adjusts the y coordinate from the preview's coordinate system to the view coordinate system. + */ + public float translateY(float y) { + return scaleY(y); + } + + public void postInvalidate() { + overlay.postInvalidate(); + } } - /** Adjusts a vertical value of the supplied value from the preview scale to the view scale. */ - public float scaleY(float vertical) { - return vertical * overlay.heightScaleFactor; - } - - /** Returns the application context of the app. */ - public Context getApplicationContext() { - return overlay.getContext().getApplicationContext(); + public GraphicOverlay(Context context, AttributeSet attrs) { + super(context, attrs); } /** - * Adjusts the x coordinate from the preview's coordinate system to the view coordinate system. + * Removes all graphics from the overlay. */ - public float translateX(float x) { - if (overlay.facing == CameraSource.CAMERA_FACING_FRONT) { - return overlay.getWidth() - scaleX(x); - } else { - return scaleX(x); - } + public void clear() { + synchronized (lock) { + graphics.clear(); + } + postInvalidate(); } /** - * Adjusts the y coordinate from the preview's coordinate system to the view coordinate system. + * Adds a graphic to the overlay. */ - public float translateY(float y) { - return scaleY(y); - } - - public void postInvalidate() { - overlay.postInvalidate(); + public void add(Graphic graphic) { + synchronized (lock) { + graphics.add(graphic); + } } - } - public GraphicOverlay(Context context, AttributeSet attrs) { - super(context, attrs); - } - - /** Removes all graphics from the overlay. */ - public void clear() { - synchronized (lock) { - graphics.clear(); + /** + * Removes a graphic from the overlay. + */ + public void remove(Graphic graphic) { + synchronized (lock) { + graphics.remove(graphic); + } + postInvalidate(); } - postInvalidate(); - } - /** Adds a graphic to the overlay. */ - public void add(Graphic graphic) { - synchronized (lock) { - graphics.add(graphic); + /** + * Sets the camera attributes for size and facing direction, which informs how to transform image + * coordinates later. + */ + public void setCameraInfo(int previewWidth, int previewHeight, int facing) { + synchronized (lock) { + this.previewWidth = previewWidth; + this.previewHeight = previewHeight; + this.facing = facing; + } + postInvalidate(); } - } - /** Removes a graphic from the overlay. */ - public void remove(Graphic graphic) { - synchronized (lock) { - graphics.remove(graphic); - } - postInvalidate(); - } - - /** - * Sets the camera attributes for size and facing direction, which informs how to transform image - * coordinates later. - */ - public void setCameraInfo(int previewWidth, int previewHeight, int facing) { - synchronized (lock) { - this.previewWidth = previewWidth; - this.previewHeight = previewHeight; - this.facing = facing; - } - postInvalidate(); - } - - /** Draws the overlay with its associated graphic objects. */ - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - - synchronized (lock) { - if ((previewWidth != 0) && (previewHeight != 0)) { - widthScaleFactor = (float) canvas.getWidth() / (float) previewWidth; - heightScaleFactor = (float) canvas.getHeight() / (float) previewHeight; - } - - for (Graphic graphic : graphics) { - graphic.draw(canvas); - } + /** + * Draws the overlay with its associated graphic objects. + */ + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + synchronized (lock) { + if ((previewWidth != 0) && (previewHeight != 0)) { + widthScaleFactor = (float) canvas.getWidth() / (float) previewWidth; + heightScaleFactor = (float) canvas.getHeight() / (float) previewHeight; + } + + for (Graphic graphic : graphics) { + graphic.draw(canvas); + } + } } - } } diff --git a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/VisionProcessorBase.kt b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/VisionProcessorBase.kt index 08fbf44..df6885c 100755 --- a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/VisionProcessorBase.kt +++ b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/common/VisionProcessorBase.kt @@ -32,11 +32,9 @@ abstract class VisionProcessorBase : VisionImageProcessor { private var processingMetaData: FrameMetadata? = null @Synchronized - override fun process( - data: ByteBuffer, - frameMetadata: FrameMetadata, - graphicOverlay: GraphicOverlay - ) { + override fun process(data: ByteBuffer, + frameMetadata: FrameMetadata, + graphicOverlay: GraphicOverlay) { latestImage = data latestImageMetaData = frameMetadata if (processingImage == null && processingMetaData == null) { @@ -55,43 +53,38 @@ abstract class VisionProcessorBase : VisionImageProcessor { } } - private fun processImage(data: ByteBuffer, frameMetadata: FrameMetadata, graphicOverlay: GraphicOverlay) { + private fun processImage(data: ByteBuffer, + frameMetadata: FrameMetadata, + graphicOverlay: GraphicOverlay) { + val metadata = FirebaseVisionImageMetadata.Builder() - .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) - .setWidth(frameMetadata.width) - .setHeight(frameMetadata.height) - .setRotation(frameMetadata.rotation) - .build() + .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) + .setWidth(frameMetadata.width) + .setHeight(frameMetadata.height) + .setRotation(frameMetadata.rotation) + .build() BitmapUtils.getBitmap(data, frameMetadata)?.let { - detectInVisionImage( - it, - FirebaseVisionImage.fromByteBuffer(data, metadata), - frameMetadata, - graphicOverlay - ) + detectInVisionImage(it, + FirebaseVisionImage.fromByteBuffer(data, metadata), + frameMetadata, + graphicOverlay) } } - private fun detectInVisionImage( - originalCameraImage: Bitmap, - image: FirebaseVisionImage, - metadata: FrameMetadata?, - graphicOverlay: GraphicOverlay - ) { + private fun detectInVisionImage(originalCameraImage: Bitmap, + image: FirebaseVisionImage, + metadata: FrameMetadata, + graphicOverlay: GraphicOverlay) { detectInImage(image) - .addOnSuccessListener { results -> - onSuccess( - originalCameraImage, results, - metadata!!, - graphicOverlay - ) - processLatestImage(graphicOverlay) - } - .addOnFailureListener { e -> onFailure(e) } + .addOnSuccessListener { results -> + onSuccess(originalCameraImage, results, metadata, graphicOverlay) + processLatestImage(graphicOverlay) + } + .addOnFailureListener { e -> onFailure(e) } } - override fun stop() {} + override fun stop() = Unit protected abstract fun detectInImage(image: FirebaseVisionImage): Task @@ -101,12 +94,10 @@ abstract class VisionProcessorBase : VisionImageProcessor { * @param originalCameraImage hold the original image from camera, used to draw the background * image. */ - protected abstract fun onSuccess( - originalCameraImage: Bitmap, - results: T, - frameMetadata: FrameMetadata, - graphicOverlay: GraphicOverlay - ) + protected abstract fun onSuccess(originalCameraImage: Bitmap, + results: T, + frameMetadata: FrameMetadata, + graphicOverlay: GraphicOverlay) protected abstract fun onFailure(e: Exception) } diff --git a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/face_detection_heplers/FaceDetectionProcessor.kt b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/face_detection_heplers/FaceDetectionProcessor.kt index ab0deea..e997d09 100644 --- a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/face_detection_heplers/FaceDetectionProcessor.kt +++ b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/face_detection_heplers/FaceDetectionProcessor.kt @@ -6,7 +6,6 @@ import com.cleveroad.arfacedetector.ui.screens.main.mlkit.common.CameraImageGrap import com.cleveroad.arfacedetector.ui.screens.main.mlkit.common.FrameMetadata import com.cleveroad.arfacedetector.ui.screens.main.mlkit.common.GraphicOverlay import com.cleveroad.arfacedetector.ui.screens.main.mlkit.common.VisionProcessorBase -import com.cleveroad.arfacedetector.utils.BitmapUtils import com.google.android.gms.tasks.Task import com.google.firebase.ml.vision.FirebaseVision import com.google.firebase.ml.vision.common.FirebaseVisionImage @@ -18,17 +17,23 @@ import java.io.IOException class FaceDetectionProcessor(private val overlayBitmap: Bitmap) : VisionProcessorBase>() { + companion object { + + private val TAG = "FaceDetectionProcessor" + + private const val MIN_FACE_SIZE = 0.4F + } private val detector: FirebaseVisionFaceDetector init { val options = FirebaseVisionFaceDetectorOptions.Builder() - .setPerformanceMode(FirebaseVisionFaceDetectorOptions.FAST) - .setClassificationMode(FirebaseVisionFaceDetectorOptions.NO_CLASSIFICATIONS) - .setLandmarkMode(FirebaseVisionFaceDetectorOptions.ALL_LANDMARKS) - .setMinFaceSize(0.4f) - .setContourMode(NO_CONTOURS) - .build() + .setPerformanceMode(FirebaseVisionFaceDetectorOptions.FAST) + .setClassificationMode(FirebaseVisionFaceDetectorOptions.NO_CLASSIFICATIONS) + .setLandmarkMode(FirebaseVisionFaceDetectorOptions.ALL_LANDMARKS) + .setMinFaceSize(MIN_FACE_SIZE) + .setContourMode(NO_CONTOURS) + .build() detector = FirebaseVision.getInstance().getVisionFaceDetector(options) } @@ -45,12 +50,10 @@ class FaceDetectionProcessor(private val overlayBitmap: Bitmap) : VisionProcesso return detector.detectInImage(image) } - override fun onSuccess( - originalCameraImage: Bitmap, - results: List, - frameMetadata: FrameMetadata, - graphicOverlay: GraphicOverlay - ) { + override fun onSuccess(originalCameraImage: Bitmap, + results: List, + frameMetadata: FrameMetadata, + graphicOverlay: GraphicOverlay) { graphicOverlay.clear() val imageGraphic = CameraImageGraphic(graphicOverlay, originalCameraImage) graphicOverlay.add(imageGraphic) @@ -65,9 +68,4 @@ class FaceDetectionProcessor(private val overlayBitmap: Bitmap) : VisionProcesso override fun onFailure(e: Exception) { Log.e(TAG, "Face detection failed $e") } - - companion object { - - private val TAG = "FaceDetectionProcessor" - } } \ No newline at end of file diff --git a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/face_detection_heplers/FaceGraphic.kt b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/face_detection_heplers/FaceGraphic.kt index 49c080c..712904b 100755 --- a/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/face_detection_heplers/FaceGraphic.kt +++ b/lib/src/main/java/com/cleveroad/arfacedetector/ui/screens/main/mlkit/face_detection_heplers/FaceGraphic.kt @@ -3,8 +3,8 @@ package com.cleveroad.arfacedetector.ui.screens.main.mlkit.face_detection_hepler import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Rect -import com.cleveroad.arfacedetector.utils.BitmapUtils.rotateBitmap import com.cleveroad.arfacedetector.ui.screens.main.mlkit.common.GraphicOverlay +import com.cleveroad.arfacedetector.utils.BitmapUtils.rotateBitmap import com.google.ar.sceneform.math.Vector3 import com.google.firebase.ml.vision.face.FirebaseVisionFace import com.google.firebase.ml.vision.face.FirebaseVisionFaceLandmark.* @@ -13,26 +13,23 @@ import com.google.firebase.ml.vision.face.FirebaseVisionFaceLandmark.* * Graphic instance for rendering face position, orientation, and landmarks within an associated * graphic overlay view. */ -class FaceGraphic( - overlay: GraphicOverlay, - private val firebaseVisionFace: FirebaseVisionFace, - private val overlayBitmap: Bitmap?, - private val cameraFacing: Int -) : GraphicOverlay.Graphic(overlay) { +class FaceGraphic(overlay: GraphicOverlay, + private val firebaseVisionFace: FirebaseVisionFace, + private val overlayBitmap: Bitmap?, + private val cameraFacing: Int) : GraphicOverlay.Graphic(overlay) { override fun draw(canvas: Canvas) { - val leftEye = firebaseVisionFace.getLandmark(LEFT_EYE)?.position ?: return val rightEye = firebaseVisionFace.getLandmark(RIGHT_EYE)?.position ?: return val mouthBottom = firebaseVisionFace.getLandmark(MOUTH_BOTTOM)?.position ?: return val faceHeight = firebaseVisionFace.boundingBox.height() * 1.3F - val direction = Vector3( - (rightEye.x + leftEye.x) / 2 - mouthBottom.x, - (rightEye.y + leftEye.y) / 2 - mouthBottom.y, - 0F - ).normalized() + val direction = + Vector3((rightEye.x + leftEye.x) / 2 - mouthBottom.x, + (rightEye.y + leftEye.y) / 2 - mouthBottom.y, + 0F + ).normalized() direction.x *= faceHeight direction.y *= faceHeight diff --git a/lib/src/main/java/com/cleveroad/arfacedetector/utils/ArUtils.kt b/lib/src/main/java/com/cleveroad/arfacedetector/utils/ArUtils.kt index af06bab..8f8c14a 100644 --- a/lib/src/main/java/com/cleveroad/arfacedetector/utils/ArUtils.kt +++ b/lib/src/main/java/com/cleveroad/arfacedetector/utils/ArUtils.kt @@ -27,9 +27,9 @@ object ArUtils { return false } val openGlVersionString = - (context.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager) - ?.deviceConfigurationInfo - ?.glEsVersion + (context.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager) + ?.deviceConfigurationInfo + ?.glEsVersion openGlVersionString?.toDoubleOrNull()?.let { if (it < MIN_OPENGL_VERSION) { Log.e(LOG_TAG, "Sceneform requires OpenGL ES 3.0 later") diff --git a/lib/src/main/java/com/cleveroad/arfacedetector/utils/BitmapUtils.kt b/lib/src/main/java/com/cleveroad/arfacedetector/utils/BitmapUtils.kt index fd61888..642561b 100755 --- a/lib/src/main/java/com/cleveroad/arfacedetector/utils/BitmapUtils.kt +++ b/lib/src/main/java/com/cleveroad/arfacedetector/utils/BitmapUtils.kt @@ -16,13 +16,13 @@ import java.nio.ByteBuffer object BitmapUtils { fun getBitmapFromVectorDrawable(context: Context, drawableId: Int): Bitmap? = - ContextCompat.getDrawable(context, drawableId)?.run { - val bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888) - val canvas = Canvas(bitmap) - setBounds(0, 0, canvas.width, canvas.height) - draw(canvas) - return bitmap - } + ContextCompat.getDrawable(context, drawableId)?.run { + val bitmap = Bitmap.createBitmap(intrinsicWidth, intrinsicHeight, Bitmap.Config.ARGB_8888) + val canvas = Canvas(bitmap) + setBounds(0, 0, canvas.width, canvas.height) + draw(canvas) + return bitmap + } // Convert NV21 format byte buffer to bitmap. fun getBitmap(data: ByteBuffer, metadata: FrameMetadata): Bitmap? { diff --git a/lib/src/main/res/layout/logo_view.xml b/lib/src/main/res/layout/image_node_view.xml similarity index 100% rename from lib/src/main/res/layout/logo_view.xml rename to lib/src/main/res/layout/image_node_view.xml diff --git a/lib/src/main/res/layout/ml_kit_face_detector_fragment.xml b/lib/src/main/res/layout/ml_kit_face_detector_fragment.xml index 81e1773..80a570c 100644 --- a/lib/src/main/res/layout/ml_kit_face_detector_fragment.xml +++ b/lib/src/main/res/layout/ml_kit_face_detector_fragment.xml @@ -1,7 +1,7 @@