├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Makefile ├── README.md ├── UNLICENSE ├── app ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── de │ │ └── markusfisch │ │ └── android │ │ └── cameraviewdemo │ │ └── activity │ │ └── MainActivity.java │ └── res │ └── values │ └── strings.xml ├── build.gradle ├── cameraview ├── build.gradle └── src │ └── main │ └── java │ └── de │ └── markusfisch │ └── android │ └── cameraview │ └── widget │ └── CameraView.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | .gradle 3 | .idea 4 | build 5 | local.properties 6 | gradle.properties 7 | infer-out 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 1.10.0 4 | * Add setScaleType to control camera preview layout 5 | 6 | ## 1.9.2 7 | * Remove appcompat dependency 8 | 9 | ## 1.9.1 10 | * Don't reset touch listener in focusTo() 11 | 12 | ## 1.9.0 13 | * Add focusTo() to manually focus to some spot 14 | 15 | ## 1.8.4 16 | * Catch possible RuntimeException on autoFocus() 17 | 18 | ## 1.8.3 19 | * Terminate HandlerThread after closing camera 20 | 21 | ## 1.8.2 22 | * Make CameraView.PreviewCallback.onPreviewFrame() run in a background thread 23 | * Forward camera errors to OnCameraListener.onCameraError() 24 | 25 | ## 1.8.1 26 | * Reactivate JitPack support 27 | 28 | ## 1.8.0 29 | * Adds setTapToFocus() to enable tap to focus 30 | 31 | ## 1.7.2 32 | * Expose findBestPreviewSize() to ease setting a custom preview size 33 | 34 | ## 1.7.1 35 | * Allow setting a custom preview size in onConfigureParameters() 36 | 37 | ## 1.7.0 38 | * Improve reliability of opening the camera 39 | * Does only restart the camera for 180deg changes in orientation listener 40 | 41 | ## 1.6.0 42 | * Add setUseOrientationListener() to fix landscape to landscape rotations 43 | 44 | ## 1.5.0 45 | * Fix relative camera orientation for front facing cameras 46 | * Rename OnCameraListener.onCameraStarted() to onCameraReady() 47 | 48 | ## 1.4.0 49 | * Adds OnCameraListener.onPreviewStarted() callback 50 | 51 | ## 1.3.3 52 | * Fixed Gradle configuration for JitPack again 53 | 54 | ## 1.3.2 55 | * Fixed Gradle configuration for JitPack 56 | 57 | ## 1.3.1 58 | * Fixed closing the camera immediately after openAsync() 59 | 60 | ## 1.3.0 61 | * Helper functions to easily set a focus area (required for tap to focus) 62 | 63 | ## 1.2.1 64 | * Fixes two formal issues static analyzers will complain about 65 | 66 | ## 1.2.0 67 | * OnCameraListener.onCameraError() gets called when Camera.open() fails too 68 | 69 | ## 1.1.0 70 | * CameraView.setAutoFocus() must be called manually now 71 | * Handle camera errors in OnCameraListener.onCameraError() 72 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | Please try to keep things in good shape and comply to what's there. 4 | 5 | This project follows Android [best practices][android_best_practices] 6 | so please have a look if you've never heard of them. 7 | 8 | The code is formatted according to Android Studio's standard, with the 9 | exception of indent being tabs instead of spaces. 10 | 11 | Use the feature branch workflow to add new features and make sure 12 | to squash when merging into master: 13 | 14 | $ git merge cool_feature --squash 15 | 16 | Then write a [good commit message][commit_messages] to keep the history 17 | meaningful and useful. One feature, one commit. 18 | 19 | [android_best_practices]: https://developer.android.com/distribute/best-practices/develop/ 20 | [commit_messages]: https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PACKAGE = de.markusfisch.android.cameraviewdemo 2 | 3 | all: debug install start 4 | 5 | debug: 6 | ./gradlew assembleDebug 7 | 8 | lint: 9 | ./gradlew lintDebug 10 | 11 | infer: clean 12 | infer -- ./gradlew assembleDebug 13 | 14 | release: lint 15 | ./gradlew assembleRelease 16 | 17 | aar: clean 18 | ./gradlew :cameraview:assembleRelease 19 | 20 | install: 21 | adb $(TARGET) install -r app/build/outputs/apk/debug/app-debug.apk 22 | 23 | start: 24 | adb $(TARGET) shell 'am start -n $(PACKAGE)/.activity.MainActivity' 25 | 26 | uninstall: 27 | adb $(TARGET) uninstall $(PACKAGE) 28 | 29 | clean: 30 | ./gradlew clean 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CameraView 2 | 3 | Camera view for Android. Supports orientation changes, fits preview image 4 | into available view space and works with Gingerbread (minSDK 9) or better 5 | (since it still uses the deprecated Camera API). All in just ~500 lines of 6 | code. 7 | 8 | ## Why the deprecated Camera API? 9 | 10 | This library is deliberately still on API level 9 (Android 2.3). 11 | 12 | If you're not interested in supporting old versions of Android and/or 13 | don't want to use the deprecated Camera API on newer devices, have a look at 14 | the [CameraX support library](https://developer.android.com/training/camerax) 15 | (available from API level 21). 16 | 17 | There, we finally find 18 | [PreviewView](https://developer.android.com/reference/androidx/camera/view/PreviewView) 19 | as part of the SDK. You can read 20 | [here](https://developer.android.com/training/camerax/preview) 21 | how to implement it. 22 | 23 | ## How to include 24 | 25 | ### Gradle 26 | 27 | Add the JitPack repository in your root build.gradle at the end of 28 | repositories: 29 | 30 | ```groovy 31 | allprojects { 32 | repositories { 33 | … 34 | maven { url 'https://jitpack.io' } 35 | } 36 | } 37 | ``` 38 | 39 | Then add the dependency in your app/build.gradle: 40 | 41 | ```groovy 42 | dependencies { 43 | implementation 'com.github.markusfisch:CameraView:1.10.0' 44 | } 45 | ``` 46 | 47 | ### Manually 48 | 49 | Alternatively you may just download the latest `aar` from 50 | [Releases](https://github.com/markusfisch/CameraView/releases) and put it 51 | into `app/libs` in your app. 52 | 53 | Then make sure your `app/build.gradle` contains the following line in the 54 | `dependencies` block: 55 | 56 | ```groovy 57 | dependencies { 58 | implementation fileTree(dir: 'libs', include: '*') 59 | … 60 | } 61 | ``` 62 | 63 | ## How to use 64 | 65 | Add it to a layout: 66 | 67 | ```xml 68 | 73 | ``` 74 | 75 | Or create it in Java: 76 | 77 | ```java 78 | import de.markusfisch.android.cameraview.widget.CameraView; 79 | 80 | CameraView cameraView = new CameraView(context); 81 | ``` 82 | 83 | If your app supports *orientation changes*, please also enable the built-in 84 | orientation listener: 85 | 86 | ```java 87 | cameraView.setUseOrientationListener(true); 88 | ``` 89 | 90 | This will take care of landscape to reverse landscape rotations (and vice 91 | versa). Without this, Android will re-use the Activity without calling a 92 | life cycle method what will result in an upside down camera preview. 93 | 94 | Run `CameraView.openAsync()`/`.close()` in `onResume()`/`onPause()` of 95 | your activity or fragment: 96 | 97 | ```java 98 | @Override 99 | public void onResume() { 100 | super.onResume(); 101 | cameraView.openAsync(CameraView.findCameraId( 102 | Camera.CameraInfo.CAMERA_FACING_BACK)); 103 | } 104 | 105 | @Override 106 | public void onPause() { 107 | super.onPause(); 108 | cameraView.close(); 109 | } 110 | ``` 111 | 112 | To set custom camera parameters or a preview listener to get the camera 113 | frame, set an OnCameraListener: 114 | 115 | ```java 116 | cameraView.setOnCameraListener(new OnCameraListener { 117 | @Override 118 | public void onConfigureParameters(Camera.Parameters parameters) { 119 | // set additional camera parameters here 120 | } 121 | 122 | @Override 123 | public void onCameraError() { 124 | // handle camera errors 125 | } 126 | 127 | @Override 128 | public void onCameraReady(Camera camera) { 129 | // set a preview listener 130 | } 131 | 132 | @Override 133 | public void onPreviewStarted(Camera camera) { 134 | // start processing camera data 135 | } 136 | 137 | @Override 138 | public void onCameraStopping(Camera camera) { 139 | // clean up 140 | } 141 | }); 142 | ``` 143 | 144 | ## Preview resolution 145 | 146 | By default CameraView picks the camera preview resolution that is closest 147 | to the size of the view on screen. If you want a lower or higher resolution, 148 | you may use `CameraView.findBestPreviewSize()` (or a customized copy of it) 149 | in `OnCameraListener.onConfigureParameters()` to pick another size. 150 | 151 | For example, if you want to pick the highest possible resolution, you can 152 | do this: 153 | 154 | ```java 155 | cameraView.setOnCameraListener(new OnCameraListener { 156 | @Override 157 | public void onConfigureParameters(Camera.Parameters parameters) { 158 | Camera.Size size = findBestPreviewSize( 159 | parameters.getSupportedPreviewSizes(), 160 | cameraView.getFrameWidth() * 1000, 161 | cameraView.getFrameHeight() * 1000); 162 | parameters.setPreviewSize(size.width, size.height); 163 | … 164 | } 165 | … 166 | ``` 167 | 168 | `CameraView.findBestPreviewSize()` returns the preview resolution that has 169 | the smallest absolute distance to the given dimensions *and* is as close to 170 | the aspect ratio of those dimensions as possible. 171 | 172 | ## Preview layout 173 | 174 | On some devices, the camera frame has a different aspect ratio than the 175 | screen. The camera preview can either be laid so that it completely covers 176 | the available `View` area (which is `SCALE_TYPE_CENTER_CROP`, the default), 177 | or so that it lies completely within it (`SCALE_TYPE_CENTER_INSIDE`). 178 | 179 | Use `CameraView.setScaleType()` with the appropriate constant: 180 | 181 | ```java 182 | cameraView.setScaleType(CameraView.SCALE_TYPE_CENTER_INSIDE); 183 | ``` 184 | 185 | ## Auto Focus 186 | 187 | To enable Auto Focus, you should run `CameraView.setAutoFocus()` in 188 | `OnCameraListener.onConfigureParameters()`: 189 | 190 | ```java 191 | cameraView.setOnCameraListener(new OnCameraListener { 192 | @Override 193 | public void onConfigureParameters(Camera.Parameters parameters) { 194 | CameraView.setAutoFocus(parameters); 195 | … 196 | } 197 | … 198 | ``` 199 | 200 | `CameraView.setAutoFocus()` picks the best available Auto Focus mode for 201 | making pictures. If you want something else, just have a look at this 202 | method and re-implement it in the client to fit your needs. 203 | 204 | Note that Auto Focus is not available on all devices. If your app depends 205 | on Auto Focus, you should put a `` tag in your 206 | `AndroidManifest.xml` to make Google Play restrict your app to devices 207 | that sport this feature: 208 | 209 | ```xml 210 | 211 | ``` 212 | 213 | ## Tap to focus 214 | 215 | To make the CameraView focus where a user taps, simply call `setTapToFocus()` 216 | on your `cameraView` instance: 217 | 218 | ```java 219 | cameraView.setTapToFocus(); 220 | ``` 221 | 222 | This adds a `View.OnTouchListener` to the `CameraView` to process the tap. 223 | 224 | If you want to use a custom `View.OnTouchListener`, you can call `focusTo()` 225 | manually in your touch listener instead of using `setTapToFocus()`: 226 | 227 | ```java 228 | if (event.getActionMasked() == MotionEvent.ACTION_UP) { 229 | boolean success = focusTo(cameraView, event.getX(), event.getY()); 230 | … 231 | } 232 | ``` 233 | 234 | If `focusTo()` returns `false`, you should stop calling it because that 235 | means there was a `RuntimeException` that will be thrown (and catched) in 236 | the future too. 237 | 238 | ## Scene modes 239 | 240 | You may use a predefined scene mode to use optimized camera parameters for 241 | a specific purpose. Consequently, setting a scene mode may override previously 242 | set camera parameters, of course. 243 | 244 | For example, to use `SCENE_MODE_BARCODE` (if it's available) do: 245 | 246 | ```java 247 | cameraView.setOnCameraListener(new OnCameraListener { 248 | @Override 249 | public void onConfigureParameters(Camera.Parameters parameters) { 250 | List modes = parameters.getSupportedSceneModes(); 251 | if (modes != null) { 252 | for (String mode : modes) { 253 | if (Camera.Parameters.SCENE_MODE_BARCODE.equals(mode)) { 254 | parameters.setSceneMode(mode); 255 | break; 256 | } 257 | } 258 | } 259 | … 260 | } 261 | … 262 | ``` 263 | 264 | Please note, not all devices support scene modes. 265 | 266 | ## Demo 267 | 268 | You can run the enclosed demo app to see if this widget is what you want. 269 | Either import it into Android Studio or, if you're not on that thing from 270 | Redmond, just type `make` to build, install and run. 271 | 272 | Tap on the screen to switch between the front and back camera. 273 | 274 | ## License 275 | 276 | This widget is so basic, it should be Public Domain. And it is. 277 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | namespace 'de.markusfisch.android.cameraviewdemo' 5 | compileSdkVersion sdk_version 6 | 7 | defaultConfig { 8 | minSdkVersion 9 9 | targetSdkVersion sdk_version 10 | 11 | versionCode 1 12 | versionName '0.0.0' 13 | } 14 | } 15 | 16 | dependencies { 17 | implementation project(':cameraview') 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 8 | 9 | 10 | 11 | 15 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/de/markusfisch/android/cameraviewdemo/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package de.markusfisch.android.cameraviewdemo.activity; 2 | 3 | import android.app.Activity; 4 | import android.content.pm.PackageManager; 5 | import android.hardware.Camera; 6 | import android.os.Build; 7 | import android.os.Bundle; 8 | import android.view.Window; 9 | import android.view.WindowManager; 10 | import android.widget.Toast; 11 | 12 | import de.markusfisch.android.cameraview.widget.CameraView; 13 | import de.markusfisch.android.cameraviewdemo.R; 14 | 15 | public class MainActivity extends Activity { 16 | private static final int REQUEST_CAMERA = 1; 17 | 18 | private static boolean frontFacing = false; 19 | 20 | private CameraView cameraView; 21 | 22 | @Override 23 | public void onRequestPermissionsResult( 24 | int requestCode, 25 | String[] permissions, 26 | int[] grantResults) { 27 | if (requestCode == REQUEST_CAMERA && 28 | grantResults.length > 0 && 29 | grantResults[0] != PackageManager.PERMISSION_GRANTED) { 30 | Toast.makeText( 31 | this, 32 | R.string.error_camera, 33 | Toast.LENGTH_SHORT).show(); 34 | finish(); 35 | } 36 | } 37 | 38 | @Override 39 | protected void onCreate(Bundle state) { 40 | super.onCreate(state); 41 | 42 | requestWindowFeature(Window.FEATURE_NO_TITLE); 43 | getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); 44 | 45 | checkPermissions(); 46 | 47 | cameraView = new CameraView(this); 48 | cameraView.setUseOrientationListener(true); 49 | cameraView.setOnClickListener(v -> invertCamera()); 50 | 51 | setContentView(cameraView); 52 | } 53 | 54 | @Override 55 | public void onResume() { 56 | super.onResume(); 57 | openCameraView(); 58 | } 59 | 60 | @Override 61 | public void onPause() { 62 | super.onPause(); 63 | closeCameraView(); 64 | } 65 | 66 | private void checkPermissions() { 67 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 68 | String permission = android.Manifest.permission.CAMERA; 69 | if (checkSelfPermission(permission) != 70 | PackageManager.PERMISSION_GRANTED) { 71 | requestPermissions(new String[]{permission}, REQUEST_CAMERA); 72 | } 73 | } 74 | } 75 | 76 | private void openCameraView() { 77 | cameraView.openAsync(CameraView.findCameraId(getFacing())); 78 | } 79 | 80 | private void closeCameraView() { 81 | cameraView.close(); 82 | } 83 | 84 | private void invertCamera() { 85 | frontFacing ^= true; 86 | closeCameraView(); 87 | openCameraView(); 88 | } 89 | 90 | private int getFacing() { 91 | return frontFacing ? 92 | Camera.CameraInfo.CAMERA_FACING_FRONT : 93 | Camera.CameraInfo.CAMERA_FACING_BACK; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | CameraView 3 | Cannot open camera 4 | 5 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | tools_version = '7.2.0' 4 | sdk_version = 34 5 | } 6 | 7 | repositories { 8 | google() 9 | mavenCentral() 10 | } 11 | 12 | dependencies { 13 | classpath "com.android.tools.build:gradle:$tools_version" 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | mavenCentral() 21 | } 22 | 23 | gradle.projectsEvaluated { 24 | tasks.withType(JavaCompile) { 25 | options.compilerArgs << "-Xlint:unchecked" 26 | } 27 | } 28 | } 29 | 30 | task clean(type: Delete) { 31 | delete rootProject.buildDir 32 | } 33 | -------------------------------------------------------------------------------- /cameraview/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | 7 | dependencies { 8 | classpath "com.android.tools.build:gradle:$tools_version" 9 | } 10 | } 11 | 12 | apply plugin: 'com.android.library' 13 | apply plugin: 'maven-publish' 14 | 15 | android { 16 | namespace 'de.markusfisch.android.cameraview' 17 | compileSdkVersion sdk_version 18 | 19 | defaultConfig { 20 | minSdkVersion 9 21 | targetSdkVersion sdk_version 22 | 23 | versionCode 22 24 | versionName '1.10.0' 25 | } 26 | } 27 | 28 | afterEvaluate { 29 | publishing { 30 | publications { 31 | release(MavenPublication) { 32 | from components.release 33 | groupId = 'com.github.markusfisch' 34 | artifactId = 'final' 35 | version = '1.10.0' 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /cameraview/src/main/java/de/markusfisch/android/cameraview/widget/CameraView.java: -------------------------------------------------------------------------------- 1 | package de.markusfisch.android.cameraview.widget; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.annotation.TargetApi; 5 | import android.content.Context; 6 | import android.graphics.Rect; 7 | import android.hardware.Camera; 8 | import android.hardware.SensorManager; 9 | import android.os.Build; 10 | import android.os.Handler; 11 | import android.os.HandlerThread; 12 | import android.util.AttributeSet; 13 | import android.view.Display; 14 | import android.view.MotionEvent; 15 | import android.view.OrientationEventListener; 16 | import android.view.Surface; 17 | import android.view.SurfaceHolder; 18 | import android.view.SurfaceView; 19 | import android.view.View; 20 | import android.view.WindowManager; 21 | import android.widget.FrameLayout; 22 | 23 | import java.io.IOException; 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | public class CameraView extends FrameLayout { 28 | public interface OnCameraListener { 29 | void onConfigureParameters(Camera.Parameters parameters); 30 | 31 | void onCameraError(); 32 | 33 | void onCameraReady(Camera camera); 34 | 35 | void onPreviewStarted(Camera camera); 36 | 37 | void onCameraStopping(Camera camera); 38 | } 39 | 40 | public static final int SCALE_TYPE_CENTER_CROP = 0; 41 | public static final int SCALE_TYPE_CENTER_INSIDE = 1; 42 | 43 | public final Rect previewRect = new Rect(); 44 | 45 | private final Runnable focusRunnable = new Runnable() { 46 | @Override 47 | public void run() { 48 | setFocusArea(null); 49 | } 50 | }; 51 | 52 | private boolean isOpen = false; 53 | private boolean useOrientationListener = false; 54 | private OnCameraListener cameraListener; 55 | private HandlerThread cameraCallbackThread; 56 | private Camera cam; 57 | private OrientationEventListener orientationListener; 58 | private int scaleType = SCALE_TYPE_CENTER_CROP; 59 | private int tries = 0; 60 | private int viewWidth; 61 | private int viewHeight; 62 | private int frameWidth; 63 | private int frameHeight; 64 | private int frameOrientation; 65 | 66 | public static int findCameraId(int facing) { 67 | for (int i = 0, l = Camera.getNumberOfCameras(); i < l; ++i) { 68 | Camera.CameraInfo info = new Camera.CameraInfo(); 69 | Camera.getCameraInfo(i, info); 70 | if (info.facing == facing) { 71 | return i; 72 | } 73 | } 74 | return -1; 75 | } 76 | 77 | public static int getRelativeCameraOrientation( 78 | Context context, 79 | int cameraId) { 80 | Camera.CameraInfo info = new Camera.CameraInfo(); 81 | Camera.getCameraInfo(cameraId, info); 82 | int orientation = info.orientation; 83 | if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { 84 | orientation -= 180; 85 | } 86 | return (orientation - getDeviceRotation(context) + 360) % 360; 87 | } 88 | 89 | public static int getDeviceRotation(Context context) { 90 | switch (((WindowManager) context 91 | .getSystemService(Context.WINDOW_SERVICE)) 92 | .getDefaultDisplay() 93 | .getRotation()) { 94 | case Surface.ROTATION_90: 95 | return 90; 96 | case Surface.ROTATION_180: 97 | return 180; 98 | case Surface.ROTATION_270: 99 | return 270; 100 | case Surface.ROTATION_0: 101 | default: 102 | return 0; 103 | } 104 | } 105 | 106 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) 107 | public static boolean setAutoFocus(Camera.Parameters parameters) { 108 | // best for taking pictures, API >= ICE_CREAM_SANDWICH 109 | String continuousPicture = 110 | Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE; 111 | // less aggressive than CONTINUOUS_PICTURE, API >= GINGERBREAD 112 | String continuousVideo = 113 | Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO; 114 | // last resort 115 | String autoFocus = Camera.Parameters.FOCUS_MODE_AUTO; 116 | 117 | // prefer feature detection instead of checking BUILD.VERSION 118 | List focusModes = parameters.getSupportedFocusModes(); 119 | 120 | if (focusModes.contains(continuousPicture)) { 121 | parameters.setFocusMode(continuousPicture); 122 | } else if (focusModes.contains(continuousVideo)) { 123 | parameters.setFocusMode(continuousVideo); 124 | } else if (focusModes.contains(autoFocus)) { 125 | parameters.setFocusMode(autoFocus); 126 | } else { 127 | return false; 128 | } 129 | 130 | return true; 131 | } 132 | 133 | // overriding `View.performClick()` wouldn't make any sense here 134 | @SuppressLint("ClickableViewAccessibility") 135 | public void setTapToFocus() { 136 | setOnTouchListener(new View.OnTouchListener() { 137 | @Override 138 | public boolean onTouch(View v, MotionEvent event) { 139 | if (event.getActionMasked() == MotionEvent.ACTION_UP && 140 | !focusTo(v, event.getX(), event.getY())) { 141 | v.setOnTouchListener(null); 142 | return false; 143 | } 144 | v.performClick(); 145 | return true; 146 | } 147 | }); 148 | } 149 | 150 | public boolean focusTo(final View v, float x, float y) { 151 | if (cam == null) { 152 | return false; 153 | } 154 | // catch possible RuntimeException's for autoFocus() 155 | // as there a devices with broken camera drivers 156 | try { 157 | cam.cancelAutoFocus(); 158 | if (!setFocusArea(calculateFocusRect(x, y, 100))) { 159 | return false; 160 | } 161 | cam.autoFocus(new Camera.AutoFocusCallback() { 162 | @Override 163 | public void onAutoFocus(boolean success, Camera camera) { 164 | v.removeCallbacks(focusRunnable); 165 | v.postDelayed(focusRunnable, 3000); 166 | } 167 | }); 168 | } catch (RuntimeException e) { 169 | return false; 170 | } 171 | return true; 172 | } 173 | 174 | public static Camera.Size findBestPreviewSize( 175 | List sizes, 176 | int width, 177 | int height) { 178 | final double ASPECT_TOLERANCE = 0.1; 179 | double targetRatio = (double) width / height; 180 | double minDiff = Double.MAX_VALUE; 181 | double minDiffAspect = Double.MAX_VALUE; 182 | Camera.Size bestSize = null; 183 | Camera.Size bestSizeAspect = null; 184 | 185 | for (Camera.Size size : sizes) { 186 | double diff = (double) Math.abs(size.height - height) + 187 | Math.abs(size.width - width); 188 | 189 | if (diff < minDiff) { 190 | bestSize = size; 191 | minDiff = diff; 192 | } 193 | 194 | double ratio = (double) size.width / size.height; 195 | 196 | if (Math.abs(ratio - targetRatio) < ASPECT_TOLERANCE && 197 | diff < minDiffAspect) { 198 | bestSizeAspect = size; 199 | minDiffAspect = diff; 200 | } 201 | } 202 | 203 | return bestSizeAspect != null ? bestSizeAspect : bestSize; 204 | } 205 | 206 | public CameraView(Context context) { 207 | super(context); 208 | } 209 | 210 | public CameraView(Context context, AttributeSet attrs) { 211 | super(context, attrs); 212 | } 213 | 214 | public CameraView( 215 | Context context, 216 | AttributeSet attrs, 217 | int defStyleAttr) { 218 | super(context, attrs, defStyleAttr); 219 | } 220 | 221 | public void setUseOrientationListener(boolean use) { 222 | useOrientationListener = use; 223 | } 224 | 225 | public void setScaleType(int scaleType) { 226 | this.scaleType = scaleType; 227 | } 228 | 229 | public void openAsync(final int cameraId) { 230 | if (isOpen || cameraCallbackThread != null) { 231 | return; 232 | } 233 | isOpen = true; 234 | // use a HandlerThread so future preview callbacks are invoked 235 | // from this background thread; an AsyncTask is terminated after 236 | // runInBackground() ends 237 | cameraCallbackThread = new HandlerThread( 238 | "CameraCallbackHandlerThread"); 239 | cameraCallbackThread.start(); 240 | Handler callbackThreadHandler = new Handler( 241 | cameraCallbackThread.getLooper()); 242 | callbackThreadHandler.post(new Runnable() { 243 | @Override 244 | public void run() { 245 | // abort if there's already an open camera 246 | if (cam != null) { 247 | return; 248 | } 249 | // Camera.open() may take a while so it shouldn't be 250 | // invoked on the main thread according to the docs 251 | final Camera camera = openCameraAndCatch(cameraId); 252 | CameraView.this.post(new Runnable() { 253 | @Override 254 | public void run() { 255 | initCamera(camera, cameraId); 256 | } 257 | }); 258 | } 259 | }); 260 | } 261 | 262 | public void close() { 263 | isOpen = false; 264 | if (orientationListener != null) { 265 | orientationListener.disable(); 266 | orientationListener = null; 267 | } 268 | if (cam != null) { 269 | if (cameraListener != null) { 270 | cameraListener.onCameraStopping(cam); 271 | } 272 | cam.stopPreview(); 273 | cam.setPreviewCallback(null); 274 | cam.release(); 275 | cam = null; 276 | } 277 | if (cameraCallbackThread != null) { 278 | // terminate cameraCallbackThread, discard all pending messages 279 | cameraCallbackThread.quit(); 280 | try { 281 | cameraCallbackThread.join(); 282 | } catch (InterruptedException ignore) { 283 | } 284 | cameraCallbackThread = null; 285 | } 286 | removeAllViews(); 287 | } 288 | 289 | public void setOnCameraListener(OnCameraListener listener) { 290 | cameraListener = listener; 291 | } 292 | 293 | public Camera getCamera() { 294 | return cam; 295 | } 296 | 297 | public int getFrameWidth() { 298 | return frameWidth; 299 | } 300 | 301 | public int getFrameHeight() { 302 | return frameHeight; 303 | } 304 | 305 | public int getFrameOrientation() { 306 | return frameOrientation; 307 | } 308 | 309 | public Rect calculateFocusRect(float x, float y, int radius) { 310 | int cx = Math.round(2000f / viewWidth * x - 1000f); 311 | int cy = Math.round(2000f / viewHeight * y - 1000f); 312 | return new Rect( 313 | Math.max(-1000, cx - radius), 314 | Math.max(-1000, cy - radius), 315 | Math.min(1000, cx + radius), 316 | Math.min(1000, cy + radius)); 317 | } 318 | 319 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) 320 | public boolean setFocusArea(Rect area) { 321 | if (cam == null || Build.VERSION.SDK_INT < 322 | Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 323 | return false; 324 | } 325 | try { 326 | Camera.Parameters parameters = cam.getParameters(); 327 | if (parameters.getMaxNumFocusAreas() > 0) { 328 | if (area != null) { 329 | List focusAreas = 330 | new ArrayList(); 331 | focusAreas.add(new Camera.Area(area, 1000)); 332 | parameters.setFocusAreas(focusAreas); 333 | parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); 334 | } else { 335 | parameters.setFocusAreas(null); 336 | CameraView.setAutoFocus(parameters); 337 | } 338 | } 339 | cam.setParameters(parameters); 340 | return true; 341 | } catch (RuntimeException e) { 342 | return false; 343 | } 344 | } 345 | 346 | @Override 347 | protected void onLayout( 348 | boolean changed, 349 | int left, 350 | int top, 351 | int right, 352 | int bottom) { 353 | if (!changed) { 354 | return; 355 | } 356 | viewWidth = right - left; 357 | viewHeight = bottom - top; 358 | if (cam != null && getChildCount() == 0) { 359 | Context context = getContext(); 360 | if (context == null) { 361 | return; 362 | } 363 | addPreview(context); 364 | } 365 | } 366 | 367 | private static Camera openCameraAndCatch(int cameraId) { 368 | try { 369 | return Camera.open(cameraId); 370 | } catch (RuntimeException e) { 371 | return null; 372 | } 373 | } 374 | 375 | private void initCamera(Camera camera, int cameraId) { 376 | if (!isOpen) { 377 | // close() was called while Camera.open() was 378 | // running on another thread 379 | if (camera != null) { 380 | camera.release(); 381 | } 382 | return; 383 | } 384 | if (camera == null) { 385 | if (cameraListener != null && 386 | // only invoke onCameraError() if there 387 | // isn't an open camera yet 388 | cam == null) { 389 | if (tries < 3) { 390 | isOpen = false; 391 | openAsync(cameraId); 392 | ++tries; 393 | } else { 394 | cameraListener.onCameraError(); 395 | } 396 | } 397 | return; 398 | } 399 | tries = 0; 400 | cam = camera; 401 | camera.setErrorCallback(new Camera.ErrorCallback() { 402 | public void onError(int error, Camera camera) { 403 | if (cameraListener != null) { 404 | cameraListener.onCameraError(); 405 | } 406 | } 407 | }); 408 | Context context = getContext(); 409 | if (context == null) { 410 | close(); 411 | return; 412 | } 413 | if (useOrientationListener) { 414 | enableOrientationListener(context, cameraId); 415 | } 416 | frameOrientation = getRelativeCameraOrientation(context, cameraId); 417 | if (viewWidth > 0) { 418 | addPreview(context); 419 | } 420 | } 421 | 422 | private void enableOrientationListener(Context context, 423 | final int cameraId) { 424 | final Display defaultDisplay = ((WindowManager) context 425 | .getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); 426 | final int defaultOrientation = defaultDisplay.getRotation(); 427 | orientationListener = new OrientationEventListener(context, 428 | SensorManager.SENSOR_DELAY_NORMAL) { 429 | @Override 430 | public void onOrientationChanged(int orientation) { 431 | if (Math.abs(defaultOrientation - 432 | defaultDisplay.getRotation()) == 2) { 433 | close(); 434 | openAsync(cameraId); 435 | } 436 | } 437 | }; 438 | orientationListener.enable(); 439 | } 440 | 441 | private void addPreview(Context context) { 442 | boolean transpose; 443 | try { 444 | transpose = setCameraParameters(); 445 | } catch (RuntimeException e) { 446 | if (cameraListener != null) { 447 | cameraListener.onCameraError(); 448 | } 449 | return; 450 | } 451 | int childWidth; 452 | int childHeight; 453 | if (transpose) { 454 | childWidth = frameHeight; 455 | childHeight = frameWidth; 456 | } else { 457 | childWidth = frameWidth; 458 | childHeight = frameHeight; 459 | } 460 | addSurfaceView(context, childWidth, childHeight); 461 | if (cameraListener != null) { 462 | cameraListener.onCameraReady(cam); 463 | } 464 | } 465 | 466 | private boolean setCameraParameters() throws RuntimeException { 467 | boolean transpose = frameOrientation == 90 || frameOrientation == 270; 468 | Camera.Parameters parameters = cam.getParameters(); 469 | parameters.setRotation(frameOrientation); 470 | setPreviewSize(parameters, transpose); 471 | if (cameraListener != null) { 472 | cameraListener.onConfigureParameters(parameters); 473 | } 474 | Camera.Size size = parameters.getPreviewSize(); 475 | if (size != null) { 476 | frameWidth = size.width; 477 | frameHeight = size.height; 478 | } 479 | cam.setParameters(parameters); 480 | cam.setDisplayOrientation(frameOrientation); 481 | return transpose; 482 | } 483 | 484 | private void setPreviewSize( 485 | Camera.Parameters parameters, 486 | boolean transpose) { 487 | if (transpose) { 488 | frameWidth = viewHeight; 489 | frameHeight = viewWidth; 490 | } else { 491 | frameWidth = viewWidth; 492 | frameHeight = viewHeight; 493 | } 494 | Camera.Size size = findBestPreviewSize( 495 | // will always return at least one item 496 | parameters.getSupportedPreviewSizes(), 497 | frameWidth, 498 | frameHeight); 499 | parameters.setPreviewSize(size.width, size.height); 500 | } 501 | 502 | private void addSurfaceView( 503 | Context context, 504 | int surfaceWidth, 505 | int surfaceHeight) { 506 | SurfaceView surfaceView = new SurfaceView(context); 507 | SurfaceHolder holder = surfaceView.getHolder(); 508 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { 509 | holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 510 | } 511 | holder.setKeepScreenOn(true); 512 | holder.addCallback(new SurfaceHolder.Callback() { 513 | @Override 514 | public void surfaceCreated(SurfaceHolder holder) { 515 | // wait until the surface has dimensions 516 | } 517 | 518 | @Override 519 | public void surfaceChanged( 520 | SurfaceHolder holder, 521 | int format, 522 | int width, 523 | int height) { 524 | if (cam == null) { 525 | return; 526 | } 527 | try { 528 | cam.setPreviewDisplay(holder); 529 | } catch (IOException e) { 530 | return; 531 | } 532 | cam.startPreview(); 533 | if (cameraListener != null) { 534 | cameraListener.onPreviewStarted(cam); 535 | } 536 | } 537 | 538 | @Override 539 | public void surfaceDestroyed(SurfaceHolder holder) { 540 | close(); 541 | } 542 | }); 543 | addView(surfaceView); 544 | setChildLayout( 545 | viewWidth, 546 | viewHeight, 547 | surfaceView, 548 | surfaceWidth, 549 | surfaceHeight, 550 | previewRect, 551 | scaleType == SCALE_TYPE_CENTER_INSIDE); 552 | } 553 | 554 | private static void setChildLayout( 555 | int width, 556 | int height, 557 | View child, 558 | int childWidth, 559 | int childHeight, 560 | Rect childRect, 561 | boolean centerInside) { 562 | int widthByHeight = width * childHeight; 563 | int heightByWidth = height * childWidth; 564 | boolean dontScaleBeyondScreen = centerInside || 565 | Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH; 566 | 567 | if (dontScaleBeyondScreen ? 568 | // center within parent view 569 | widthByHeight > heightByWidth : 570 | // scale to cover parent view 571 | widthByHeight < heightByWidth) { 572 | childWidth = childWidth * height / childHeight; 573 | childHeight = height; 574 | } else { 575 | childHeight = childHeight * width / childWidth; 576 | childWidth = width; 577 | } 578 | 579 | int l = (width - childWidth) >> 1; 580 | int t = dontScaleBeyondScreen ? 581 | (height - childHeight) >> 1 : 582 | 0; 583 | 584 | childRect.set( 585 | l, 586 | t, 587 | l + childWidth, 588 | t + childHeight); 589 | 590 | child.layout( 591 | childRect.left, 592 | childRect.top, 593 | childRect.right, 594 | childRect.bottom); 595 | } 596 | } 597 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markusfisch/CameraView/745597d05bc6abfdb3637a09a8ecaf30fdce7b6e/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Jan 26 11:58:22 CET 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':cameraview' 2 | --------------------------------------------------------------------------------