├── .gitignore ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── encodings.xml ├── gradle.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── mohsenmb │ │ └── facedetectiontest │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ │ └── com │ │ │ └── mohsenmb │ │ │ └── facedetectiontest │ │ │ ├── detection │ │ │ ├── ActivityResolver.java │ │ │ ├── CameraActivity.java │ │ │ ├── CameraManager.java │ │ │ └── FaceDetectionImageAnalyzer.java │ │ │ └── preview │ │ │ ├── FacePreviewViewModel.java │ │ │ ├── PreviewActivity.java │ │ │ └── PreviewFaceDetectionAnalyzer.java │ └── res │ │ ├── drawable │ │ ├── background_camera_snap.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_launcher_foreground.xml │ │ └── img_oops_not_found.jpg │ │ ├── layout │ │ ├── activity_main.xml │ │ └── activity_preview.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── mohsenmb │ └── facedetectiontest │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | app/google-services.json -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 174 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Android Face Detection 2 | ===== 3 | 4 | ## About Project 5 | This is a simple straightforward sample, for face detection, using the following stack 6 | 7 | I used these libraries/frameworks/tools: 8 | - **Java** Programming Language 9 | - **AndroidX** 10 | - **CameraX** to access the device camera 11 | - **Firebase ML Kit** for face detection 12 | - **RxJava2**, **RxAndroid** 13 | 14 | 15 | ## Meta 16 | 17 | Mohsen Bahman Poor – [@mohsenmbcom](https://twitter.com/mohsenmbcom) – me@mohsenmb.com 18 | 19 | [https://github.com/mohsenmbcom/android-camera-face-detection-app](https://github.com/mohsenmbcom/android-camera-face-detection-app) -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | apply plugin: 'com.google.gms.google-services' 7 | 8 | android { 9 | compileSdkVersion 28 10 | defaultConfig { 11 | applicationId "com.mohsenmb.facedetectiontest" 12 | minSdkVersion 23 13 | targetSdkVersion 28 14 | versionCode 1 15 | versionName "1.0" 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | compileOptions { 25 | sourceCompatibility = '1.8' 26 | targetCompatibility = '1.8' 27 | } 28 | } 29 | 30 | dependencies { 31 | implementation fileTree(dir: 'libs', include: ['*.jar']) 32 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 33 | implementation 'androidx.appcompat:appcompat:1.1.0-rc01' 34 | implementation "com.google.android.material:material:1.1.0-alpha09" 35 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 36 | testImplementation 'junit:junit:4.12' 37 | androidTestImplementation 'androidx.test:runner:1.2.0' 38 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 39 | 40 | // Firebase ML Kit 41 | implementation 'com.google.firebase:firebase-ml-vision:22.0.0' 42 | implementation 'com.google.firebase:firebase-ml-vision-face-model:18.0.0' 43 | 44 | // RxJava 2 45 | implementation 'io.reactivex.rxjava2:rxjava:2.2.11' 46 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' 47 | 48 | def camerax_version = "1.0.0-alpha03" 49 | implementation "androidx.camera:camera-core:${camerax_version}" 50 | implementation "androidx.camera:camera-camera2:${camerax_version}" 51 | } 52 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/mohsenmb/facedetectiontest/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.mohsenmb.facedetectiontest 2 | 3 | import androidx.test.InstrumentationRegistry 4 | import androidx.test.runner.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getTargetContext() 22 | assertEquals("com.mohsenmb.facedetectiontest", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 18 | 19 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohsenmbcom/android-camera-face-detection-app/f68dfb4a6a10bad8176ece6e8a2c3d0f0a9c33bc/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/com/mohsenmb/facedetectiontest/detection/ActivityResolver.java: -------------------------------------------------------------------------------- 1 | package com.mohsenmb.facedetectiontest.detection; 2 | 3 | import android.app.Activity; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.appcompat.app.AppCompatActivity; 7 | import androidx.lifecycle.LifecycleOwner; 8 | 9 | public interface ActivityResolver { 10 | Activity resolveActivity(); 11 | LifecycleOwner resolveLifecycleOwner(); 12 | 13 | void setPermissionResultListener(PermissionResultListener listener); 14 | 15 | interface PermissionResultListener{ 16 | void onPermissionResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/mohsenmb/facedetectiontest/detection/CameraActivity.java: -------------------------------------------------------------------------------- 1 | package com.mohsenmb.facedetectiontest.detection; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Bundle; 7 | import android.view.View; 8 | 9 | import androidx.annotation.NonNull; 10 | import androidx.annotation.Nullable; 11 | import androidx.appcompat.app.AppCompatActivity; 12 | import androidx.lifecycle.LifecycleOwner; 13 | 14 | import com.mohsenmb.facedetectiontest.R; 15 | import com.mohsenmb.facedetectiontest.preview.PreviewActivity; 16 | 17 | public class CameraActivity extends AppCompatActivity implements ActivityResolver, LifecycleOwner, FaceDetectionImageAnalyzer.FaceDetectionListener { 18 | 19 | private PermissionResultListener permissionListener; 20 | private CameraManager cameraManager; 21 | private View viewSnapPanel; 22 | private View viewCameraOverlay; 23 | 24 | @Override 25 | protected void onStart() { 26 | super.onStart(); 27 | 28 | viewCameraOverlay = findViewById(R.id.frame_camera_overlay); 29 | 30 | viewSnapPanel = findViewById(R.id.view_snap_panel); 31 | cameraManager = new CameraManager(this, findViewById(R.id.texture_view)); 32 | cameraManager.setFaceDetectionListener(this); 33 | 34 | View viewSnap = findViewById(R.id.view_snap_image); 35 | viewSnap.setOnClickListener(view -> { 36 | viewCameraOverlay.setVisibility(View.VISIBLE); 37 | cameraManager.captureImage(image -> runOnUiThread(() -> { 38 | Intent myIntent = new Intent(CameraActivity.this, PreviewActivity.class); 39 | myIntent.setData(Uri.fromFile(image)); 40 | startActivity(myIntent); 41 | })); 42 | }); 43 | cameraManager.setup(); 44 | } 45 | 46 | @Override 47 | protected void onCreate(@Nullable Bundle savedInstanceState) { 48 | super.onCreate(savedInstanceState); 49 | setContentView(R.layout.activity_main); 50 | } 51 | 52 | @Override 53 | protected void onPause() { 54 | cameraManager.pause(); 55 | super.onPause(); 56 | } 57 | 58 | @Override 59 | protected void onResume() { 60 | super.onResume(); 61 | viewCameraOverlay.setVisibility(View.GONE); 62 | cameraManager.resume(); 63 | } 64 | 65 | @Override 66 | protected void onStop() { 67 | cameraManager.stop(); 68 | super.onStop(); 69 | } 70 | 71 | @Override 72 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 73 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 74 | if (permissionListener != null) { 75 | permissionListener.onPermissionResult(requestCode, permissions, grantResults); 76 | } 77 | } 78 | 79 | @Override 80 | public Activity resolveActivity() { 81 | return this; 82 | } 83 | 84 | @Override 85 | public LifecycleOwner resolveLifecycleOwner() { 86 | return this; 87 | } 88 | 89 | @Override 90 | public void setPermissionResultListener(PermissionResultListener listener) { 91 | this.permissionListener = listener; 92 | } 93 | 94 | @Override 95 | public void onFaceDetected(int faces) { 96 | findViewById(R.id.view_snap_image).setEnabled(true); 97 | viewSnapPanel.animate().alpha(1F).start(); 98 | } 99 | 100 | @Override 101 | public void onNoFaceDetected() { 102 | findViewById(R.id.view_snap_image).setEnabled(false); 103 | viewSnapPanel.animate().alpha(0F).start(); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/java/com/mohsenmb/facedetectiontest/detection/CameraManager.java: -------------------------------------------------------------------------------- 1 | package com.mohsenmb.facedetectiontest.detection; 2 | 3 | import android.Manifest; 4 | import android.content.pm.PackageManager; 5 | import android.graphics.Matrix; 6 | import android.os.Handler; 7 | import android.os.HandlerThread; 8 | import android.util.Rational; 9 | import android.util.Size; 10 | import android.view.Surface; 11 | import android.view.TextureView; 12 | import android.view.ViewGroup; 13 | import android.widget.Toast; 14 | 15 | import androidx.annotation.NonNull; 16 | import androidx.annotation.Nullable; 17 | import androidx.appcompat.app.AlertDialog; 18 | import androidx.camera.core.CameraX; 19 | import androidx.camera.core.ImageAnalysis; 20 | import androidx.camera.core.ImageAnalysisConfig; 21 | import androidx.camera.core.ImageCapture; 22 | import androidx.camera.core.ImageCaptureConfig; 23 | import androidx.camera.core.Preview; 24 | import androidx.camera.core.PreviewConfig; 25 | import androidx.core.app.ActivityCompat; 26 | 27 | import com.mohsenmb.facedetectiontest.R; 28 | 29 | import java.io.File; 30 | 31 | public class CameraManager implements ActivityResolver.PermissionResultListener { 32 | // region Constants 33 | private static final int REQUEST_CODE_PERMISSION = 1122; 34 | private static final String CAMERA_PERMISSION = Manifest.permission.CAMERA; 35 | 36 | public static final String SAVING_IMAGE_NAME = "temp_captured_image.jpg"; 37 | // endregion Constants 38 | 39 | private ActivityResolver activityResolver; 40 | private TextureView cameraView; 41 | 42 | private boolean isSetupCompleted = false; 43 | 44 | private FaceDetectionImageAnalyzer.FaceDetectionListener faceDetectionListener; 45 | // This variable saves the last amount of the faces 46 | // has seen by the analyzer and avoids calling the callback every time 47 | private int lastDetectedFaces = 0; 48 | 49 | private ImageCapture imageCapture; 50 | private Preview preview; 51 | private ImageAnalysis analyzerUseCase; 52 | 53 | 54 | CameraManager(@NonNull ActivityResolver activityResolver, @NonNull TextureView cameraView) { 55 | this.activityResolver = activityResolver; 56 | activityResolver.setPermissionResultListener(this); 57 | 58 | this.cameraView = cameraView; 59 | } 60 | 61 | private void updateTransform(Size textureSize) { 62 | Matrix matrix = new Matrix(); 63 | 64 | // Compute the center of the view finder 65 | float centerX = cameraView.getWidth() / 2f; 66 | float centerY = cameraView.getHeight() / 2f; 67 | 68 | // Correct preview output to account for display rotation 69 | int rotation; 70 | switch (cameraView.getDisplay().getRotation()) { 71 | case Surface.ROTATION_0: 72 | rotation = 0; 73 | break; 74 | case Surface.ROTATION_90: 75 | rotation = 90; 76 | break; 77 | case Surface.ROTATION_180: 78 | rotation = 180; 79 | break; 80 | case Surface.ROTATION_270: 81 | rotation = 270; 82 | break; 83 | default: 84 | return; 85 | } 86 | 87 | int previewHeight = Math.max(textureSize.getWidth(), textureSize.getHeight()); 88 | int previewWidth = Math.min(textureSize.getWidth(), textureSize.getHeight()); 89 | 90 | // If the preview ratio is not the same as the view aspect ratio 91 | // this scale modification will fix that 92 | int outputWidth, outputHeight; 93 | if (previewWidth * cameraView.getHeight() > previewHeight * cameraView.getWidth()) { 94 | outputWidth = cameraView.getWidth() * previewHeight / cameraView.getHeight(); 95 | outputHeight = previewHeight; 96 | } else { 97 | outputWidth = previewWidth; 98 | outputHeight = cameraView.getHeight() * previewWidth / cameraView.getWidth(); 99 | } 100 | 101 | matrix.setScale(previewWidth * 1F / outputWidth, previewHeight * 1F / outputHeight, centerX, centerY); 102 | 103 | 104 | matrix.postRotate(-rotation, centerX, centerY); 105 | 106 | // Finally, apply transformations to our TextureView 107 | cameraView.setTransform(matrix); 108 | } 109 | 110 | void setup() { 111 | if (ActivityCompat.checkSelfPermission(activityResolver.resolveActivity(), CAMERA_PERMISSION) == PackageManager.PERMISSION_GRANTED) { 112 | cameraView.post(this::startCamera); 113 | } else { 114 | ActivityCompat.requestPermissions( 115 | activityResolver.resolveActivity(), new String[]{CAMERA_PERMISSION}, REQUEST_CODE_PERMISSION); 116 | } 117 | } 118 | 119 | void pause() { 120 | CameraX.unbindAll(); 121 | } 122 | 123 | void resume() { 124 | if (isSetupCompleted) { 125 | lastDetectedFaces = 0; 126 | if (faceDetectionListener != null) { 127 | faceDetectionListener.onNoFaceDetected(); 128 | } 129 | startCamera(); 130 | } 131 | } 132 | 133 | void captureImage(@NonNull final ImageCaptureCallback imageCaptureCallback) { 134 | if (imageCapture != null) { 135 | 136 | if (lastDetectedFaces == -1) { 137 | return; 138 | } 139 | 140 | CameraX.unbind(analyzerUseCase); 141 | lastDetectedFaces = -1; 142 | 143 | File file = new File(activityResolver.resolveActivity().getCacheDir(), SAVING_IMAGE_NAME); 144 | 145 | imageCapture.takePicture(file, new ImageCapture.OnImageSavedListener() { 146 | @Override 147 | public void onImageSaved(@NonNull File file) { 148 | imageCaptureCallback.onImageCaptured(file); 149 | } 150 | 151 | @Override 152 | public void onError(@NonNull ImageCapture.UseCaseError useCaseError, @NonNull String message, @Nullable Throwable cause) { 153 | Toast.makeText( 154 | activityResolver.resolveActivity(), 155 | message, 156 | Toast.LENGTH_LONG).show(); 157 | } 158 | }); 159 | 160 | } else { 161 | setup(); 162 | Toast.makeText( 163 | activityResolver.resolveActivity(), 164 | "You may need to grant the camera permission!", 165 | Toast.LENGTH_LONG).show(); 166 | } 167 | } 168 | 169 | void stop() { 170 | CameraX.unbindAll(); 171 | } 172 | 173 | private void startCamera() { 174 | // To make sure there is no lifecycle bound before 175 | CameraX.unbindAll(); 176 | 177 | // Bind use cases to lifecycle 178 | if (!activityResolver.resolveActivity().isFinishing() && !activityResolver.resolveActivity().isDestroyed()) { 179 | CameraX.bindToLifecycle(activityResolver.resolveLifecycleOwner(), createPreviewUseCase(), createImageCaptureUseCase(), createAnalyzerUseCase()); 180 | isSetupCompleted = true; 181 | } 182 | } 183 | 184 | @Override 185 | public void onPermissionResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 186 | if (requestCode == REQUEST_CODE_PERMISSION) { 187 | if (ActivityCompat.checkSelfPermission(activityResolver.resolveActivity(), CAMERA_PERMISSION) == PackageManager.PERMISSION_GRANTED) { 188 | permissionGranted(); 189 | } else { 190 | new AlertDialog.Builder(activityResolver.resolveActivity()) 191 | .setTitle(R.string.title_permission_required) 192 | .setMessage(R.string.error_permission_not_granted) 193 | .setNegativeButton(android.R.string.cancel, (dialog, which) -> activityResolver.resolveActivity().finish()) 194 | .setPositiveButton(R.string.grant_permission, (dialog, which) -> setup()) 195 | .create() 196 | .show(); 197 | } 198 | } 199 | } 200 | 201 | private void permissionGranted() { 202 | setup(); 203 | } 204 | 205 | private Preview createPreviewUseCase() { 206 | PreviewConfig.Builder previewConfigBuilder = new PreviewConfig.Builder(); 207 | previewConfigBuilder 208 | .setLensFacing(CameraX.LensFacing.FRONT); 209 | 210 | PreviewConfig previewConfig = previewConfigBuilder.build(); 211 | 212 | // Build the viewfinder use case 213 | preview = new Preview(previewConfig); 214 | // Every time the viewfinder is updated, recompute layout 215 | preview.setOnPreviewOutputUpdateListener(output -> { 216 | // To update the SurfaceTexture, we have to remove it and re-add it 217 | ViewGroup parent = (ViewGroup) cameraView.getParent(); 218 | parent.removeView(cameraView); 219 | parent.addView(cameraView, 0); 220 | 221 | 222 | cameraView.setSurfaceTexture(output.getSurfaceTexture()); 223 | updateTransform(output.getTextureSize()); 224 | }); 225 | return preview; 226 | } 227 | 228 | private ImageCapture createImageCaptureUseCase() { 229 | ImageCaptureConfig.Builder imageCaptureConfig = new ImageCaptureConfig.Builder(); 230 | imageCaptureConfig.setCaptureMode(ImageCapture.CaptureMode.MIN_LATENCY); 231 | imageCaptureConfig.setLensFacing(CameraX.LensFacing.FRONT); 232 | 233 | // Build the image capture use case and attach button click listener 234 | imageCapture = new ImageCapture(imageCaptureConfig.build()); 235 | 236 | return imageCapture; 237 | } 238 | 239 | private ImageAnalysis createAnalyzerUseCase() { 240 | ImageAnalysisConfig.Builder analyzerConfig = new ImageAnalysisConfig.Builder(); 241 | // Use a worker thread for image analysis to prevent glitches 242 | HandlerThread analyzerThread = new HandlerThread("FaceDetectionAnalyzer"); 243 | analyzerThread.start(); 244 | analyzerConfig.setCallbackHandler(new Handler(analyzerThread.getLooper())); 245 | // In our analysis, we care more about the latest image than 246 | // analyzing *every* image 247 | analyzerConfig.setImageReaderMode( 248 | ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE); 249 | 250 | // To have a pretty quick analysis a resolution is enough. 251 | Size analyzeResolution = new Size(108, 192); 252 | analyzerConfig.setTargetResolution(analyzeResolution); 253 | analyzerConfig.setTargetAspectRatio(new Rational(analyzeResolution.getWidth(), analyzeResolution.getHeight())); 254 | analyzerConfig.setLensFacing(CameraX.LensFacing.FRONT); 255 | 256 | FaceDetectionImageAnalyzer analyzer = new FaceDetectionImageAnalyzer(); 257 | analyzer.setFaceDetectionListener(new FaceDetectionImageAnalyzer.FaceDetectionListener() { 258 | @Override 259 | public void onFaceDetected(int faces) { 260 | if (faceDetectionListener != null) { 261 | if (lastDetectedFaces != faces) { 262 | lastDetectedFaces = faces; 263 | faceDetectionListener.onFaceDetected(faces); 264 | } 265 | } 266 | } 267 | 268 | @Override 269 | public void onNoFaceDetected() { 270 | if (faceDetectionListener != null) { 271 | if (lastDetectedFaces > 0) { 272 | lastDetectedFaces = 0; 273 | faceDetectionListener.onNoFaceDetected(); 274 | } 275 | } 276 | } 277 | }); 278 | analyzerUseCase = new ImageAnalysis(analyzerConfig.build()); 279 | analyzerUseCase.setAnalyzer(analyzer); 280 | return analyzerUseCase; 281 | } 282 | 283 | public void removeFaceDetectionListener() { 284 | this.faceDetectionListener = null; 285 | } 286 | 287 | public void setFaceDetectionListener(FaceDetectionImageAnalyzer.FaceDetectionListener faceDetectionListener) { 288 | this.faceDetectionListener = faceDetectionListener; 289 | } 290 | 291 | interface ImageCaptureCallback { 292 | void onImageCaptured(File image); 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /app/src/main/java/com/mohsenmb/facedetectiontest/detection/FaceDetectionImageAnalyzer.java: -------------------------------------------------------------------------------- 1 | package com.mohsenmb.facedetectiontest.detection; 2 | 3 | import android.media.Image; 4 | 5 | import androidx.camera.core.ImageAnalysis; 6 | import androidx.camera.core.ImageProxy; 7 | 8 | import com.google.firebase.ml.vision.FirebaseVision; 9 | import com.google.firebase.ml.vision.common.FirebaseVisionImage; 10 | import com.google.firebase.ml.vision.common.FirebaseVisionImageMetadata; 11 | import com.google.firebase.ml.vision.face.FirebaseVisionFaceDetector; 12 | import com.google.firebase.ml.vision.face.FirebaseVisionFaceDetectorOptions; 13 | 14 | public class FaceDetectionImageAnalyzer implements ImageAnalysis.Analyzer { 15 | 16 | private FirebaseVisionFaceDetector detector; 17 | 18 | private FaceDetectionListener faceDetectionListener; 19 | 20 | FaceDetectionImageAnalyzer() { 21 | FirebaseVisionFaceDetectorOptions visionRealtimeOptions = new FirebaseVisionFaceDetectorOptions.Builder() 22 | .setPerformanceMode(FirebaseVisionFaceDetectorOptions.ACCURATE) 23 | .setLandmarkMode(FirebaseVisionFaceDetectorOptions.NO_LANDMARKS) 24 | .setClassificationMode(FirebaseVisionFaceDetectorOptions.NO_CLASSIFICATIONS) 25 | .build(); 26 | detector = FirebaseVision.getInstance() 27 | .getVisionFaceDetector(visionRealtimeOptions); 28 | } 29 | 30 | private int degreesToFirebaseRotation(int degrees) { 31 | switch (degrees) { 32 | case 0: 33 | return FirebaseVisionImageMetadata.ROTATION_0; 34 | case 90: 35 | return FirebaseVisionImageMetadata.ROTATION_90; 36 | case 180: 37 | return FirebaseVisionImageMetadata.ROTATION_180; 38 | case 270: 39 | return FirebaseVisionImageMetadata.ROTATION_270; 40 | default: 41 | throw new IllegalArgumentException( 42 | "Rotation must be 0, 90, 180, or 270."); 43 | } 44 | } 45 | 46 | @Override 47 | public void analyze(ImageProxy imageProxy, int degrees) { 48 | if (imageProxy == null || imageProxy.getImage() == null) { 49 | return; 50 | } 51 | Image mediaImage = imageProxy.getImage(); 52 | int rotation = degreesToFirebaseRotation(degrees); 53 | FirebaseVisionImage image = 54 | FirebaseVisionImage.fromMediaImage(mediaImage, rotation); 55 | 56 | 57 | detector.detectInImage(image) 58 | .addOnSuccessListener(firebaseVisionFaces -> { 59 | if (faceDetectionListener != null) { 60 | if (!firebaseVisionFaces.isEmpty()) { 61 | faceDetectionListener.onFaceDetected(firebaseVisionFaces.size()); 62 | } else { 63 | faceDetectionListener.onNoFaceDetected(); 64 | } 65 | } 66 | }) 67 | .addOnFailureListener(Throwable::printStackTrace); 68 | } 69 | 70 | void setFaceDetectionListener(FaceDetectionListener faceDetectionListener) { 71 | this.faceDetectionListener = faceDetectionListener; 72 | } 73 | 74 | interface FaceDetectionListener { 75 | void onFaceDetected(int faces); 76 | 77 | void onNoFaceDetected(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /app/src/main/java/com/mohsenmb/facedetectiontest/preview/FacePreviewViewModel.java: -------------------------------------------------------------------------------- 1 | package com.mohsenmb.facedetectiontest.preview; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.graphics.Matrix; 7 | import android.graphics.Rect; 8 | import android.net.Uri; 9 | import android.provider.MediaStore; 10 | import android.widget.ImageView; 11 | 12 | import androidx.annotation.NonNull; 13 | import androidx.annotation.Nullable; 14 | 15 | import com.mohsenmb.facedetectiontest.R; 16 | 17 | import io.reactivex.Single; 18 | import io.reactivex.android.schedulers.AndroidSchedulers; 19 | import io.reactivex.disposables.Disposable; 20 | import io.reactivex.schedulers.Schedulers; 21 | 22 | public class FacePreviewViewModel { 23 | private final PreviewFaceDetectionAnalyzer faceDetectionHandler; 24 | 25 | private Activity activity; 26 | private ImageView previewImageView; 27 | private OperationStateCallback operationStateCallback; 28 | 29 | private int screenWidth; 30 | 31 | private Disposable operationDisposable; 32 | 33 | public FacePreviewViewModel(@NonNull Activity activity, @NonNull ImageView previewImageView, @Nullable OperationStateCallback operationStateCallback) { 34 | this.activity = activity; 35 | this.previewImageView = previewImageView; 36 | this.operationStateCallback = operationStateCallback; 37 | 38 | faceDetectionHandler = new PreviewFaceDetectionAnalyzer(); 39 | 40 | screenWidth = activity.getResources().getDisplayMetrics().widthPixels; 41 | } 42 | 43 | public void cropFace(Uri imageUri) { 44 | if (operationStateCallback != null) { 45 | operationStateCallback.onOperationStateChanged(OperationState.InProgress); 46 | } 47 | 48 | if (operationDisposable != null && !operationDisposable.isDisposed()) { 49 | operationDisposable.dispose(); 50 | } 51 | operationDisposable = Single.just(imageUri) 52 | .map(uri -> { 53 | Bitmap bitmap = MediaStore.Images.Media.getBitmap(activity.getContentResolver(), uri); 54 | Matrix matrix = new Matrix(); 55 | matrix.postRotate(90, bitmap.getWidth() / 2F, bitmap.getHeight() / 2F); 56 | matrix.postScale(1, -1, bitmap.getWidth() / 2F, bitmap.getHeight() / 2F); 57 | return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); 58 | }) 59 | .subscribeOn(Schedulers.io()) 60 | .flatMap(faceDetectionHandler::analyzePhoto) 61 | .subscribeOn(Schedulers.io()) 62 | .map(result -> { 63 | if (result.faces.isEmpty()) { 64 | return BitmapFactory.decodeResource(activity.getResources(), R.drawable.img_oops_not_found); 65 | } else { 66 | Bitmap bitmap = result.bitmap; 67 | Rect rect = result.faces.get(0).getBoundingBox(); 68 | 69 | int x = (rect.left > 0) ? rect.left : 0; 70 | int y = (rect.top > 0) ? rect.top : 0; 71 | int width = rect.width(); 72 | int height = rect.height(); 73 | return Bitmap.createBitmap(bitmap, 74 | x, 75 | y, 76 | (x + width > bitmap.getWidth()) ? bitmap.getWidth() - x : width, 77 | (y + height > bitmap.getHeight()) ? bitmap.getHeight() - y : height); 78 | } 79 | }) 80 | .subscribeOn(Schedulers.io()) 81 | .observeOn(AndroidSchedulers.mainThread()) 82 | .subscribe(bitmap -> { 83 | if (operationStateCallback != null) { 84 | operationStateCallback.onOperationStateChanged(OperationState.Success); 85 | } 86 | previewImageView.setImageBitmap(bitmap); 87 | }, throwable -> { 88 | throwable.printStackTrace(); 89 | if (operationStateCallback != null) { 90 | operationStateCallback.onOperationStateChanged(OperationState.Failure); 91 | } 92 | }); 93 | } 94 | 95 | 96 | void destroy() { 97 | if (operationDisposable != null && !operationDisposable.isDisposed()) { 98 | operationDisposable.dispose(); 99 | } 100 | } 101 | 102 | enum OperationState { 103 | InProgress, Success, Failure 104 | } 105 | 106 | interface OperationStateCallback { 107 | void onOperationStateChanged(OperationState state); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /app/src/main/java/com/mohsenmb/facedetectiontest/preview/PreviewActivity.java: -------------------------------------------------------------------------------- 1 | package com.mohsenmb.facedetectiontest.preview; 2 | 3 | import android.net.Uri; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.ImageView; 7 | import android.widget.Toast; 8 | 9 | import androidx.appcompat.app.AppCompatActivity; 10 | 11 | import com.mohsenmb.facedetectiontest.R; 12 | 13 | public class PreviewActivity extends AppCompatActivity implements FacePreviewViewModel.OperationStateCallback { 14 | 15 | private FacePreviewViewModel viewModel; 16 | private View viewProgress; 17 | 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | setContentView(R.layout.activity_preview); 22 | 23 | Uri imageUri = getIntent().getData(); 24 | if (imageUri == null) { 25 | Toast.makeText( 26 | this, 27 | R.string.cant_find_anything_to_display, 28 | Toast.LENGTH_LONG).show(); 29 | finish(); 30 | return; 31 | } 32 | 33 | viewProgress = findViewById(R.id.view_progress); 34 | 35 | viewProgress.post(() -> { 36 | viewModel = new FacePreviewViewModel(this, findViewById(R.id.image_cropped_face), this); 37 | viewModel.cropFace(imageUri); 38 | }); 39 | } 40 | 41 | @Override 42 | public void onOperationStateChanged(FacePreviewViewModel.OperationState state) { 43 | switch (state) { 44 | case InProgress: 45 | viewProgress.setVisibility(View.VISIBLE); 46 | break; 47 | case Success: 48 | viewProgress.setVisibility(View.GONE); 49 | break; 50 | case Failure: 51 | Toast.makeText(this, R.string.error_faild_cropping, Toast.LENGTH_LONG).show(); 52 | break; 53 | } 54 | } 55 | 56 | @Override 57 | protected void onDestroy() { 58 | viewModel.destroy(); 59 | super.onDestroy(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/mohsenmb/facedetectiontest/preview/PreviewFaceDetectionAnalyzer.java: -------------------------------------------------------------------------------- 1 | package com.mohsenmb.facedetectiontest.preview; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Rect; 5 | 6 | import com.google.firebase.ml.vision.FirebaseVision; 7 | import com.google.firebase.ml.vision.common.FirebaseVisionImage; 8 | import com.google.firebase.ml.vision.face.FirebaseVisionFace; 9 | import com.google.firebase.ml.vision.face.FirebaseVisionFaceDetector; 10 | import com.google.firebase.ml.vision.face.FirebaseVisionFaceDetectorOptions; 11 | 12 | import java.util.List; 13 | 14 | import io.reactivex.Single; 15 | 16 | public class PreviewFaceDetectionAnalyzer { 17 | // This factor is used to make the detecting image smaller, to make the process faster 18 | private static final int SCALING_FACTOR = 10; 19 | 20 | private FirebaseVisionFaceDetector detector; 21 | 22 | public PreviewFaceDetectionAnalyzer() { 23 | FirebaseVisionFaceDetectorOptions visionRealtimeOptions = new FirebaseVisionFaceDetectorOptions.Builder() 24 | .setPerformanceMode(FirebaseVisionFaceDetectorOptions.ACCURATE) 25 | .setLandmarkMode(FirebaseVisionFaceDetectorOptions.ALL_LANDMARKS) 26 | .setClassificationMode(FirebaseVisionFaceDetectorOptions.ALL_CLASSIFICATIONS) 27 | .build(); 28 | 29 | detector = FirebaseVision.getInstance() 30 | .getVisionFaceDetector(visionRealtimeOptions); 31 | } 32 | 33 | public Single analyzePhoto(final Bitmap bitmap) { 34 | return Single.create(emitter -> { 35 | Bitmap smallerBitmap = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth() / SCALING_FACTOR, bitmap.getHeight() / SCALING_FACTOR, false); 36 | FirebaseVisionImage image = FirebaseVisionImage.fromBitmap(smallerBitmap); 37 | 38 | detector.detectInImage(image) 39 | .addOnFailureListener(emitter::onError) 40 | .addOnSuccessListener(faces -> { 41 | for (FirebaseVisionFace face : faces) { 42 | Rect rect = face.getBoundingBox(); 43 | rect.set(rect.left * SCALING_FACTOR, rect.top * SCALING_FACTOR, rect.right * SCALING_FACTOR, rect.bottom * SCALING_FACTOR); 44 | } 45 | emitter.onSuccess(new BitmapDetectionResult(bitmap, faces)); 46 | }); 47 | }); 48 | } 49 | 50 | static class BitmapDetectionResult { 51 | final Bitmap bitmap; 52 | final List faces; 53 | 54 | public BitmapDetectionResult(Bitmap bitmap, List faces) { 55 | this.bitmap = bitmap; 56 | this.faces = faces; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_camera_snap.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 13 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/img_oops_not_found.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohsenmbcom/android-camera-face-detection-app/f68dfb4a6a10bad8176ece6e8a2c3d0f0a9c33bc/app/src/main/res/drawable/img_oops_not_found.jpg -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 18 | 19 | 27 | 28 | 42 | 43 | 44 | 45 | 46 | 55 | 56 | 64 | 65 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_preview.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 20 | 21 | 28 | 29 | 36 | 37 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohsenmbcom/android-camera-face-detection-app/f68dfb4a6a10bad8176ece6e8a2c3d0f0a9c33bc/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohsenmbcom/android-camera-face-detection-app/f68dfb4a6a10bad8176ece6e8a2c3d0f0a9c33bc/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohsenmbcom/android-camera-face-detection-app/f68dfb4a6a10bad8176ece6e8a2c3d0f0a9c33bc/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohsenmbcom/android-camera-face-detection-app/f68dfb4a6a10bad8176ece6e8a2c3d0f0a9c33bc/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohsenmbcom/android-camera-face-detection-app/f68dfb4a6a10bad8176ece6e8a2c3d0f0a9c33bc/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohsenmbcom/android-camera-face-detection-app/f68dfb4a6a10bad8176ece6e8a2c3d0f0a9c33bc/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohsenmbcom/android-camera-face-detection-app/f68dfb4a6a10bad8176ece6e8a2c3d0f0a9c33bc/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohsenmbcom/android-camera-face-detection-app/f68dfb4a6a10bad8176ece6e8a2c3d0f0a9c33bc/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohsenmbcom/android-camera-face-detection-app/f68dfb4a6a10bad8176ece6e8a2c3d0f0a9c33bc/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohsenmbcom/android-camera-face-detection-app/f68dfb4a6a10bad8176ece6e8a2c3d0f0a9c33bc/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #686999 4 | #4B4C75 5 | #787999 6 | #55000000 7 | #88ffffff 8 | #FFF 9 | #4B4C75 10 | #aaffffff 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #4B4C75 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Face Detection Test 3 | To detect faces, the app needs permission to access your phone camera. 4 | Can\'t find anything to display 5 | Grant Permission 6 | Permission Required 7 | Face Preview 8 | Failed cropping the face 😕 9 | Cropping the image... 10 | Saving the image... 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/test/java/com/mohsenmb/facedetectiontest/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.mohsenmb.facedetectiontest 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.3.41' 5 | repositories { 6 | google() 7 | jcenter() 8 | 9 | } 10 | dependencies { 11 | classpath 'com.android.tools.build:gradle:3.4.2' 12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 13 | classpath 'com.google.gms:google-services:4.3.0' 14 | // NOTE: Do not place your application dependencies here; they belong 15 | // in the individual module build.gradle files 16 | } 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | google() 22 | jcenter() 23 | 24 | } 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # Kotlin code style for this project: "official" or "obsolete": 15 | kotlin.code.style=official 16 | android.useAndroidX=true 17 | android.enableJetifier=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mohsenmbcom/android-camera-face-detection-app/f68dfb4a6a10bad8176ece6e8a2c3d0f0a9c33bc/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Aug 02 22:03:24 EET 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------