Documentation in GitHub pages

pull/360/head
Mattia Iavarone 6 years ago
parent c26eb87a87
commit 26f313c74e
  1. 731
      README.md
  2. BIN
      art/old_screen1.png
  3. BIN
      art/old_screen2.png
  4. 10
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/CameraViewTest.java
  5. 12
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/IntegrationTest.java
  6. 16
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraException.java
  7. 10
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraListener.java
  8. 20
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  9. 10
      cameraview/src/main/res/values/attrs.xml
  10. 9
      demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java
  11. 5
      docs/.gitignore
  12. 2
      docs/Gemfile
  13. 248
      docs/Gemfile.lock
  14. 1
      docs/README.md
  15. 70
      docs/_config.yml
  16. 12
      docs/_includes/disqus.html
  17. 1
      docs/_includes/footer.html
  18. 8
      docs/_includes/google_analytics.html
  19. 3
      docs/_includes/header.html
  20. 16
      docs/_includes/navigation.html
  21. 115
      docs/_layouts/default.html
  22. 11
      docs/_layouts/page.html
  23. 0
      docs/_posts/.gitkeep
  24. 78
      docs/_posts/2018-12-20-camera-events.md
  25. 105
      docs/_posts/2018-12-20-capture-size.md
  26. 82
      docs/_posts/2018-12-20-capturing-media.md
  27. 13
      docs/_posts/2018-12-20-changelog.md
  28. 14
      docs/_posts/2018-12-20-contact.md
  29. 52
      docs/_posts/2018-12-20-contributing.md
  30. 139
      docs/_posts/2018-12-20-controls.md
  31. 25
      docs/_posts/2018-12-20-debugging.md
  32. 49
      docs/_posts/2018-12-20-error-handling.md
  33. 51
      docs/_posts/2018-12-20-frame-processing.md
  34. 51
      docs/_posts/2018-12-20-gestures.md
  35. 146
      docs/_posts/2018-12-20-getting-started.md
  36. 29
      docs/_posts/2018-12-20-install.md
  37. 77
      docs/_posts/2018-12-20-more-features.md
  38. 53
      docs/_posts/2018-12-20-preview-size.md
  39. 43
      docs/_posts/2018-12-20-previews.md
  40. 46
      docs/_posts/2018-12-20-runtime-permissions.md
  41. 56
      docs/_posts/2018-12-20-v1-migration-guide.md
  42. 117
      docs/css/main.css
  43. 61
      docs/css/syntax.css
  44. 36
      docs/index.md
  45. 4
      docs/script/launch
  46. 107
      docs/script/new-page
  47. 0
      docs/static/icon.png
  48. 0
      docs/static/screen1.jpg
  49. 0
      docs/static/screen2.jpg
  50. 0
      docs/static/screen3.jpg

@ -2,724 +2,37 @@
[![Code Coverage](https://codecov.io/gh/natario1/CameraView/branch/master/graph/badge.svg)](https://codecov.io/gh/natario1/CameraView)
<p align="center">
<img src="art/icon.png" vspace="10" width="250" height="250">
<img src="docs/static/icon.png" vspace="10" width="250" height="250">
</p>
# CameraView
CameraView is a well documented, high-level library that makes capturing pictures and videos easy,
addressing most of the common issues and needs, and still leaving you with flexibility where needed.
See [CHANGELOG](https://github.com/natario1/CameraView/blob/master/CHANGELOG.md).
```groovy
compile 'com.otaliastudios:cameraview:1.6.1'
```
Make sure your project repositories include the `jcenter()`:
```groovy
allprojects {
repositories {
jcenter()
}
}
```
<p>
<img src="art/screen1.jpg" width="250" vspace="20" hspace="5">
<img src="art/screen2.jpg" width="250" vspace="20" hspace="5">
<img src="art/screen3.jpg" width="250" vspace="20" hspace="5">
</p>
*This was a fork of [CameraKit-Android](https://github.com/gogopop/CameraKit-Android), originally a
fork of [Google's CameraView](https://github.com/google/cameraview), but has been
[completely rewritten](https://github.com/natario1/CameraView/graphs/contributors?type=d).
See below for [licensing info](#contributing-and-licenses).*
### Features
- Seamless image and video capturing
- **Gestures** support (tap to focus, pinch to zoom and much more)
- System permission handling
- **Smart sizing** behavior
- Preview: Create a `CameraView` of **any size**
- Preview: Center inside or center crop behaviors
- Output: Handy SizeSelectors to set the output size
- Built-in **grid drawing**
- Multiple capture methods
- Capture high-quality content with `takePicture` and `takeVideo`
- Take **quick snapshots** as a freeze frame of the preview with `takePictureSnapshot`
- Control HDR, flash, zoom, white balance, exposure correction and more
- **Frame processing** support
- **Metadata** support for pictures and videos
- Automatically detected orientation tags
- Plug in **location tags** with `setLocation()` API
- `CameraUtils` to help with Bitmaps and orientations
- Error handling
- Thread safe, **well tested**
- **Lightweight**, no dependencies, just support `ExifInterface`
compile 'com.otaliastudios:cameraview:2.0.0-beta01'
```
- Fast & reliable
- Gestures support
- Frame processing support
- OpenGL powered preview
- Take high-quality content with `takePicture` and `takeVideo`
- Take super-fast snapshots with `takePictureSnapshot` and `takeVideoSnapshot`
- Smart sizing: create a `CameraView` of any size
- Control HDR, flash, zoom, white balance, exposure, location, grid drawing & more
- Lightweight: the only dep. is support `ExifInterface`
- Works down to API level 15
- Well tested
# Docs
- [Usage](#usage)
- [Capturing Images](#capturing-images)
- [Capturing Video](#capturing-video)
- [Other camera events](#other-camera-events)
- [Gestures](#gestures)
- [Gesture APIs](#gesture-apis)
- [Sizing Behavior](#sizing-behavior)
- [Preview Size](#preview-size)
- [Capture Size](#capture-size)
- [Size APIs](#size-apis)
- [Camera Controls](#camera-controls)
- [Frame Processing](#frame-processing)
- [Frame Processing APIs](#frame-processing-apis)
- [Other APIs](#other-apis)
- [Permissions Behavior](#permissions-behavior)
- [Logging](#logging)
## Usage
To use the CameraView engine, simply add a `CameraView` to your layout:
```xml
<com.otaliastudios.cameraview.CameraView
android:id="@+id/camera"
android:keepScreenOn="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
```
`CameraView` is a component bound to your activity or fragment lifecycle. This means that you must pass the
lifecycle owner using `setLifecycleOwner`:
```java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CameraView camera = findViewById(R.id.camera);
camera.setLifecycleOwner(this);
// From fragments, use fragment.viewLifecycleOwner instead of this!
}
```
For those who are not using the support libraries and the lifecycle implementation, make sure you override `onResume`,
`onPause` and `onDestroy` in your component, and call `CameraView.start()`, `stop()`
and `destroy()`.
```java
@Override
protected void onResume() {
super.onResume();
cameraView.start();
}
@Override
protected void onPause() {
super.onPause();
cameraView.stop();
}
@Override
protected void onDestroy() {
super.onDestroy();
cameraView.destroy();
}
```
### Capturing Images
To capture an image just call `CameraView.takePicture()`. Make sure you setup a `CameraListener`
to handle the image callback.
```java
camera.setMode(Mode.PICTURE);
camera.addCameraListener(new CameraListener() {
@Override
public void onPictureTaken(PictureResult result) {
// If planning to save a file, just get the jpeg array.
byte[] jpeg = result.getJpeg();
// If planning to show a Bitmap, we will take care of
// EXIF rotation and background threading for you...
result.asBitmap(maxWidth, maxHeight, callback);
}
});
camera.takePicture();
```
You can also use `camera.takePictureSnapshot()` to capture a preview frame. This can be faster,
but the output is lower quality of course.
### Capturing Video
To capture video just call `CameraView.takeVideo(file)` to start, and
`CameraView.stopVideo()` to finish. Make sure you setup a `CameraListener` to handle
the video callback.
```java
camera.setMode(Mode.VIDEO);
camera.addCameraListener(new CameraListener() {
@Override
public void onVideoTaken(VideoResult result) {
// Use result.getFile() to access a file holding
// the recorded video.
}
});
// Select output file. Make sure you have write permissions.
File file = ...;
camera.takeVideo(file);
// Later... stop recording. This will trigger onVideoTaken().
camera.stopVideo();
// You can also use one of the video constraints:
// videoMaxSize and videoMaxDuration will automatically stop recording when satisfied.
camera.setVideoMaxSize(100000);
camera.setVideoMaxDuration(5000);
camera.takeVideo(file);
```
### Other camera events
Make sure you can react to different camera events by setting up one or more `CameraListener`
instances. All these are executed on the UI thread.
```java
camera.addCameraListener(new CameraListener() {
/**
* Notifies that the camera was opened.
* The options object collects all supported options by the current camera.
*/
@Override
public void onCameraOpened(CameraOptions options) {}
/**
* Notifies that the camera session was closed.
*/
@Override
public void onCameraClosed() {}
/**
* Notifies about an error during the camera setup or configuration.
* At the moment, errors that are passed here are unrecoverable. When this is called,
* the camera has been released and is presumably showing a black preview.
*
* This is the right moment to show an error dialog to the user.
*/
@Override
public void onCameraError(CameraException error) {}
/**
* Notifies that a picture previously captured with takePicture()
* or takePictureSnapshot() is ready to be shown or saved.
*
* If planning to get a bitmap, you can use CameraUtils.decodeBitmap()
* to decode the byte array taking care about orientation.
*/
@Override
public void onPictureTaken(PictureResult result) {}
/**
* Notifies that a video capture has just ended.
*/
@Override
public void onVideoTaken(VideoResult result) {}
/**
* Notifies that the device was tilted or the window offset changed.
* The orientation passed can be used to align views (e.g. buttons) to the current
* camera viewport so they will appear correctly oriented to the user.
*/
@Override
public void onOrientationChanged(int orientation) {}
/**
* Notifies that user interacted with the screen and started focus with a gesture,
* and the autofocus is trying to focus around that area.
* This can be used to draw things on screen.
*/
@Override
public void onFocusStart(PointF point) {}
/**
* Notifies that a gesture focus event just ended, and the camera converged
* to a new focus (and possibly exposure and white balance).
*/
@Override
public void onFocusEnd(boolean successful, PointF point) {}
/**
* Noitifies that a finger gesture just caused the camera zoom
* to be changed. This can be used, for example, to draw a seek bar.
*/
@Override
public void onZoomChanged(float newValue, float[] bounds, PointF[] fingers) {}
/**
* Noitifies that a finger gesture just caused the camera exposure correction
* to be changed. This can be used, for example, to draw a seek bar.
*/
@Override
public void onExposureCorrectionChanged(float newValue, float[] bounds, PointF[] fingers) {}
});
```
## Gestures
`CameraView` listen to lots of different gestures inside its bounds. You have the chance to map
these gestures to particular actions or camera controls, using `mapGesture()`.
This lets you emulate typical behaviors in a single line:
```java
cameraView.mapGesture(Gesture.PINCH, GestureAction.ZOOM); // Pinch to zoom!
cameraView.mapGesture(Gesture.TAP, GestureAction.FOCUS_WITH_MARKER); // Tap to focus!
cameraView.mapGesture(Gesture.LONG_TAP, GestureAction.CAPTURE); // Long tap to shoot!
```
Simple as that. More gestures are coming. There are two things to be noted:
- Not every mapping is valid. For example, you can't control zoom with long taps, or start focusing by pinching.
- Some actions might not be supported by the sensor. Check out `CameraOptions` to know what's legit and what's not.
|Gesture (XML)|Description|Can be mapped to|
|-------------|-----------|----------------|
|`PINCH` (`cameraGesturePinch`)|Pinch gesture, typically assigned to the zoom control.|`zoom` `exposureCorrection` `none`|
|`TAP` (`cameraGestureTap`)|Single tap gesture, typically assigned to the focus control.|`focus` `focusWithMarker` `capture` `none`|
|`LONG_TAP` (`cameraGestureLongTap`)|Long tap gesture.|`focus` `focusWithMarker` `capture` `none`|
|`SCROLL_HORIZONTAL` (`cameraGestureScrollHorizontal`)|Horizontal movement gesture.|`zoom` `exposureCorrection` `none`|
|`SCROLL_VERTICAL` (`cameraGestureScrollVertical`)|Vertical movement gesture.|`zoom` `exposureCorrection` `none`|
### Gesture APIs
|Method|Description|
|------|-----------|
|`mapGesture(Gesture, GestureAction)`|Maps a certain gesture to a certain action. No-op if the action is not supported.|
|`getGestureAction(Gesture)`|Returns the action currently mapped to the given gesture.|
|`clearGesture(Gesture)`|Clears any action mapped to the given gesture.|
## Sizing Behavior
### Preview Size
`CameraView` has a smart measuring behavior that will let you do what you want with a few flags.
Measuring is controlled simply by `layout_width` and `layout_height` attributes, with this meaning:
|Value|Meaning|
|-----|-------|
|`WRAP_CONTENT`|CameraView will choose this dimension, in order to show the whole preview without cropping. The aspect ratio will be respected.|
|`MATCH_PARENT`|CameraView will fill this dimension. Part of the content *might* be cropped.
|Fixed values (e.g. `500dp`)|Same as `MATCH_PARENT`|
This means that your visible preview can be of any size, not just the presets.
Whatever you do, the preview will never be distorted - it can only be cropped
if needed.
#### Center Inside
By setting both dimensions to `WRAP_CONTENT`, you can emulate a **center inside** behavior.
The view will try to fill the available space, but respecting the stream aspect ratio.
```xml
<com.otaliastudios.cameraview.CameraView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
```
This means that the whole preview is visible, and the image output matches what was visible
during the preview.
#### Center Crop
By setting both dimensions to `MATCH_PARENT` or fixed values, you can emulate a **center crop**
behavior. The camera view will fill the rect. If your dimensions don't match the aspect ratio
of the internal preview surface, the surface will be cropped to fill the view.
```xml
<com.otaliastudios.cameraview.CameraView
android:layout_width="match_parent"
android:layout_height="match_parent" />
```
This means that part of the preview might be hidden, and the output might contain parts of the scene
that were not visible during the capture (unless it is taken as a **snapshot**).
### Capture Size
On top of the view size, you can control the actual size of the output.
This is (not considering rotations) the size of the final JPEG picture or video, and it must be chosen
among the available sizes provided by the sensor. This can be achieved directly using the `SizeSelector` class:
```java
// Set size for PICTURE mode.
// It will be the size of pictures taken with takePicture().
cameraView.setPictureSize(new SizeSelector() {
@Override
public List<Size> select(List<Size> source) {
// Receives a list of available sizes.
// Must return a list of acceptable sizes.
}
});
// Set size for VIDEO mode.
// It will be the size of videos taken with takeVideo().
cameraView.setVideoSize(new SizeSelector() {
@Override
public List<Size> select(List<Size> source) {
// Same here.
}
});
// See SizeSelectors below for handy utilities.
```
In practice, this is way easier using XML attributes or leveraging the `SizeSelectors` utilities:
|Constraint|XML attr|SizeSelector|
|----------|--------|------------|
|min. width|`app:cameraPictureSizeMinWidth="100"`|`SizeSelectors.minWidth(100)`|
|min. height|`app:cameraPictureSizeMinHeight="100"`|`SizeSelectors.minHeight(100)`|
|max. width|`app:cameraPictureSizeMaxWidth="3000"`|`SizeSelectors.maxWidth(3000)`|
|max. height|`app:cameraPictureSizeMaxHeight="3000"`|`SizeSelectors.maxHeight(3000)`|
|min. area|`app:cameraPictureSizeMinArea="1000000"`|`SizeSelectors.minArea(1000000)`|
|max. area|`app:cameraPictureSizeMaxArea="5000000"`|`SizeSelectors.maxArea(5000000)`|
|aspect ratio|`app:cameraPictureSizeAspectRatio="1:1"`|`SizeSelectors.aspectRatio(AspectRatio.of(1,1), 0)`|
|smallest|`app:cameraPictureSizeSmallest="true"`|`SizeSelectors.smallest()`|
|biggest (**default**)|`app:cameraPictureSizeBiggest="true"`|`SizeSelectors.biggest()`|
Similar attributes are declared for `VIDEO` mode sizing. If you declare more than one XML constraint,
the resulting selector will try to match **all** the constraints. Be careful - it is very likely that
applying lots of constraints will give empty results.
#### SizeSelectors utilities
For more versatility, or to address selection issues with multiple constraints,
we encourage you to use `SizeSelectors` utilities, that will let you merge different selectors.
This selector will try to find square sizes bigger than 1000x2000. If none is found, it falls back
to just square sizes:
```java
SizeSelector width = SizeSelectors.minWidth(1000);
SizeSelector height = SizeSelectors.minHeight(2000);
SizeSelector dimensions = SizeSelectors.and(width, height); // Matches sizes bigger than 1000x2000.
SizeSelector ratio = SizeSelectors.aspectRatio(AspectRatio.of(1, 1), 0); // Matches 1:1 sizes.
SizeSelector result = SizeSelectors.or(
SizeSelectors.and(ratio, dimensions), // Try to match both constraints
ratio, // If none is found, at least try to match the aspect ratio
SizeSelectors.biggest() // If none is found, take the biggest
);
camera.setPictureSize(result);
camera.setVideoSize(result);
```
### Size APIs
|Method|Description|
|------|-----------|
|`setPictureSize(SizeSelector)`|Provides a size selector for the capture size in `PICTURE` mode.|
|`setVideoSize(SizeSelector)`|Provides a size selector for the capture size in `VIDEO` mode.|
|`getPictureSize()`|Returns the size of the output picture, including any rotation. Returns null in `VIDEO` mode.|
|`getVideoSize()`|Returns the size of the output video, including any rotation. Returns null in `PICTURE` mode.|
|`getSnapshotSize()`|Returns the size of snapshots (pictures or video), including any rotation and cropping.|
## Camera controls
Most camera parameters can be controlled through XML attributes or linked methods.
```xml
<com.otaliastudios.cameraview.CameraView
android:id="@+id/camera"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:keepScreenOn="true"
app:cameraFacing="back"
app:cameraFlash="off"
app:cameraGrid="off"
app:cameraVideoQuality="max480p"
app:cameraMode="picture"
app:cameraVideoCodec="deviceDefault"
app:cameraWhiteBalance="auto"
app:cameraHdr="off"
app:cameraAudio="on"
app:cameraPlaySounds="true"
app:cameraVideoMaxSize="0"
app:cameraVideoMaxDuration="0"/>
```
|XML Attribute|Method|Values|Default Value|
|-------------|------|------|-------------|
|[`cameraMode`](#cameramode)|`setMode()`|`picture` `video`|`picture`|
|[`cameraFacing`](#camerafacing)|`setFacing()`|`back` `front`|`back`|
|[`cameraFlash`](#cameraflash)|`setFlash()`|`off` `on` `auto` `torch`|`off`|
|[`cameraGrid`](#cameragrid)|`setGrid()`|`off` `draw3x3` `draw4x4` `drawPhi`|`off`|
|[`cameraVideoCodec`](#cameravideocodec)|`setVideoCodec()`|`deviceDefault` `h263` `h264`|`deviceDefault`|
|[`cameraWhiteBalance`](#camerawhitebalance)|`setWhiteBalance()`|`auto` `incandescent` `fluorescent` `daylight` `cloudy`|`auto`|
|[`cameraHdr`](#camerahdr)|`setHdr()`|`off` `on`|`off`|
|[`cameraAudio`](#cameraaudio)|`setAudio()`|`off` `on`|`on`|
|[`cameraPlaySounds`](#cameraplaysounds)|`setPlaySounds()`|`true` `false`|`true`|
|[`cameraVideoMaxSize`](#cameravideomaxsize)|`setVideoMaxSize()`|number|`0`|
|[`cameraVideoMaxDuration`](#cameravideomaxduration)|`setVideoMaxDuration()`|number|`0`|
#### cameraMode
What to capture - either picture or video. This has a couple of consequences:
- Sizing: the capture size is chosen among the available picture or video sizes,
depending on the flag, according to the given [size selector](#capture-size).
- Capturing: while in picture mode, `takeVideo` will throw an exception.
- Capturing: while in video mode, `takePicture` will throw an exception, but picture snapshots are supported.
- Permission behavior: when requesting a `video` session, the record audio permission will be requested.
If this is needed, the audio permission should be added to your manifest or the app will crash.
```java
cameraView.setMode(Mode.PICTURE);
cameraView.setMode(Mode.VIDEO);
```
#### cameraFacing
Which camera to use, either back facing or front facing.
```java
cameraView.setFacing(Facing.BACK);
cameraView.setFacing(Facing.FRONT);
```
#### cameraFlash
Flash mode, either off, on, auto or *torch*.
```java
cameraView.setFlash(Flash.OFF);
cameraView.setFlash(Flash.ON);
cameraView.setFlash(Flash.AUTO);
cameraView.setFlash(Flash.TORCH);
```
#### cameraGrid
Lets you draw grids over the camera preview. Supported values are `off`, `draw3x3` and `draw4x4`
for regular grids, and `drawPhi` for a grid based on the golden ratio constant, often used in photography.
Read the [official website](https://natario1/github.io/CameraView) for setup instructions and documentation.
```java
cameraView.setGrid(Grid.OFF);
cameraView.setGrid(Grid.DRAW_3X3);
cameraView.setGrid(Grid.DRAW_4X4);
cameraView.setGrid(Grid.DRAW_PHI);
```
#### cameraVideoCodec
Sets the encoder for video recordings.
```java
cameraView.setVideoCodec(VideoCodec.DEVICE_DEFAULT);
cameraView.setVideoCodec(VideoCodec.H_263);
cameraView.setVideoCodec(VideoCodec.H_264);
```
#### cameraWhiteBalance
Sets the desired white balance for the current session.
```java
cameraView.setWhiteBalance(WhiteBalance.AUTO);
cameraView.setWhiteBalance(WhiteBalance.INCANDESCENT);
cameraView.setWhiteBalance(WhiteBalance.FLUORESCENT);
cameraView.setWhiteBalance(WhiteBalance.DAYLIGHT);
cameraView.setWhiteBalance(WhiteBalance.CLOUDY);
```
#### cameraHdr
Turns on or off HDR captures.
```java
cameraView.setHdr(Hdr.OFF);
cameraView.setHdr(Hdr.ON);
```
#### cameraAudio
Turns on or off audio stream while recording videos.
```java
cameraView.setAudio(Audio.OFF);
cameraView.setAudio(Audio.ON);
```
#### cameraPlaySounds
Controls whether we should play platform-provided sounds during certain events
(shutter click, focus completed). Please note that:
- on API < 16, this flag is always set to `false`
- the Camera1 engine will always play shutter sounds regardless of this flag
```java
cameraView.setPlaySounds(true);
cameraView.setPlaySounds(false);
```
- Coming from v1? Take a look at the [migration guide](https://natario1/github.io/CameraView/extra/v1-migration-guide.html)
- Changelog is hosted [here](https://natario1/github.io/CameraView/about/changelog.html)
#### cameraVideoMaxSize
Defines the maximum size in bytes for recorded video files.
Once this size is reached, the recording will automatically stop.
Defaults to unlimited size. Use 0 or negatives to disable.
```java
cameraView.setVideoMaxSize(100000);
cameraView.setVideoMaxSize(0); // Disable
```
#### cameraVideoMaxDuration
Defines the maximum duration in milliseconds for video recordings.
Once this duration is reached, the recording will automatically stop.
Defaults to unlimited duration. Use 0 or negatives to disable.
```java
cameraView.setVideoMaxDuration(100000);
cameraView.setVideoMaxDuration(0); // Disable
```
## Frame Processing
We support frame processors that will receive data from the camera preview stream:
```java
cameraView.addFrameProcessor(new FrameProcessor() {
@Override
@WorkerThread
public void process(Frame frame) {
byte[] data = frame.getData();
int rotation = frame.getRotation();
long time = frame.getTime();
Size size = frame.getSize();
int format = frame.getFormat();
// Process...
}
}
```
For your convenience, the `FrameProcessor` method is run in a background thread so you can do your job
in a synchronous fashion. Once the process method returns, internally we will re-use the `Frame` instance and
apply new data to it. So:
- you can do your job synchronously in the `process()` method
- if you must hold the `Frame` instance longer, use `frame = frame.freeze()` to get a frozen instance
that will not be affected
### Frame Processing APIs
|Frame API|Type|Description|
|---------|----|-----------|
|`camera.addFrameProcessor(FrameProcessor)`|`-`|Register a `FrameProcessor`.|
|`frame.getData()`|`byte[]`|The current preview frame, in its original orientation.|
|`frame.getTime()`|`long`|The preview timestamp, in `System.currentTimeMillis()` reference.|
|`frame.getRotation()`|`int`|The rotation that should be applied to the byte array in order to see what the user sees.|
|`frame.getSize()`|`Size`|The frame size, before any rotation is applied, to access data.|
|`frame.getFormat()`|`int`|The frame `ImageFormat`. This will always be `ImageFormat.NV21` for now.|
|`frame.freeze()`|`Frame`|Clones this frame and makes it immutable. Can be expensive because requires copying the byte array.|
|`frame.release()`|`-`|Disposes the content of this frame. Should be used on frozen frames to release memory.|
## Other APIs
Other APIs not mentioned above are provided, and are well documented and commented in code.
|Method|Description|
|------|-----------|
|`isStarted()`|Returns true if `start()` was called succesfully. This does not mean that camera is open or showing preview.|
|`isTakingVideo()`|Returns true if the camera is currently recording a video.|
|`isTakingPicture()`|Returns true if the camera is currently capturing a picture.|
|`getCameraOptions()`|If camera was started, returns non-null object with information about what is supported.|
|`setZoom(float)`, `getZoom()`|Sets a zoom value, where 0 means camera zoomed out and 1 means zoomed in. No-op if zoom is not supported, or camera not started.|
|`setExposureCorrection(float)`, `getExposureCorrection()`|Sets exposure compensation EV value, in camera stops. No-op if this is not supported. Should be between the bounds returned by CameraOptions.|
|`toggleFacing()`|Toggles the facing value between `Facing.FRONT` and `Facing.BACK`.|
|`setLocation(Location)`|Sets location data to be appended to picture/video metadata.|
|`setLocation(double, double)`|Sets latitude and longitude to be appended to picture/video metadata.|
|`getLocation()`|Retrieves location data previously applied with setLocation().|
|`startAutoFocus(float, float)`|Starts an autofocus process at the given coordinates, with respect to the view dimensions.|
Take also a look at public methods in `CameraUtils`, `CameraOptions`.
## Permissions behavior
`CameraView` needs two permissions:
- `android.permission.CAMERA` : required for capturing pictures and videos
- `android.permission.RECORD_AUDIO` : required for capturing videos with `Audio.ON` (the default)
### Declaration
The library manifest file declares the `android.permission.CAMERA` permission, but not the audio one.
This means that:
- If you wish to record videos with `Audio.ON` (the default), you should also add
`android.permission.RECORD_AUDIO` to required permissions
```xml
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
```
- If you want your app to be installed only on devices that have a camera, you should add:
```xml
<uses-feature
android:name="android.hardware.camera"
android:required="true"/>
```
If you don't request this feature, you can use `CameraUtils.hasCameras()` to detect if current
device has cameras, and then start the camera view.
### Handling
On Marshmallow+, the user must explicitly approve our permissions. You can
- handle permissions yourself and then call `cameraView.start()` once they are acquired
- or call `cameraView.start()` anyway: `CameraView` will present a permission request to the user based on
whether they are needed or not with the current configuration.
In the second case, you should restart the camera if you have a successful response from `onRequestPermissionResults()`.
## Logging
`CameraView` will log a lot of interesting events related to the camera lifecycle. These are important
to identify bugs. The default logger will simply use Android `Log` methods posting to logcat.
You can attach and detach external loggers using `CameraLogger.registerLogger()`:
```java
CameraLogger.registerLogger(new Logger() {
@Override
public void log(@LogLevel int level, String tag, String message, @Nullable Throwable throwable) {
// For example...
Crashlytics.log(message);
}
});
```
Make sure you enable the logger using `CameraLogger.setLogLevel(@LogLevel int)`. The default will only
log error events.
# Contributing and licenses
The original project which served as a starting point for this library,
[CameraKit-Android](https://github.com/wonderkiln/CameraKit-Android), is licensed under the
[MIT](https://github.com/wonderkiln/CameraKit-Android/blob/master/LICENSE) license.
Additional work is now licensed under the [MIT](https://github.com/natario1/CameraView/blob/master/LICENSE)
license as well.
You are welcome to contribute with suggestions or pull requests, this is under active development.
To contact me, <a href="mailto:mat.iavarone@gmail.com">send an email.</a>
<p>
<img src="docs/static/screen1.jpg" width="250" vspace="20" hspace="5">
<img src="docs/static/screen2.jpg" width="250" vspace="20" hspace="5">
<img src="docs/static/screen3.jpg" width="250" vspace="20" hspace="5">
</p>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

@ -71,16 +71,16 @@ public class CameraViewTest extends BaseTest {
//region testLifecycle
@Test
public void testStart() {
cameraView.start();
public void testOpen() {
cameraView.open();
verify(mockPreview, times(1)).onResume();
// Can't verify controller, depends on permissions.
// See to-do at the end.
}
@Test
public void testStop() {
cameraView.stop();
public void testClose() {
cameraView.close();
verify(mockPreview, times(1)).onPause();
verify(mockController, times(1)).stop();
}
@ -96,7 +96,7 @@ public class CameraViewTest extends BaseTest {
@Test
public void testNullBeforeStart() {
assertFalse(cameraView.isStarted());
assertFalse(cameraView.isOpened());
assertNull(cameraView.getCameraOptions());
assertNull(cameraView.getSnapshotSize());
assertNull(cameraView.getPictureSize());

@ -98,7 +98,7 @@ public class IntegrationTest extends BaseTest {
}
private CameraOptions waitForOpen(boolean expectSuccess) {
camera.start();
camera.open();
final Task<CameraOptions> open = new Task<>(true);
doEndTask(open, 0).when(listener).onCameraOpened(any(CameraOptions.class));
CameraOptions result = open.await(4000);
@ -111,7 +111,7 @@ public class IntegrationTest extends BaseTest {
}
private void waitForClose(boolean expectSuccess) {
camera.stop();
camera.close();
final Task<Boolean> close = new Task<>(true);
doEndTask(close, true).when(listener).onCameraClosed();
Boolean result = close.await(4000);
@ -185,10 +185,10 @@ public class IntegrationTest extends BaseTest {
doCountDown(latch).when(listener).onCameraOpened(any(CameraOptions.class));
doCountDown(latch).when(listener).onCameraClosed();
camera.start();
camera.stop();
camera.start();
camera.stop();
camera.open();
camera.close();
camera.open();
camera.close();
boolean did = latch.await(10, TimeUnit.SECONDS);
assertTrue("Handles concurrent calls to start & stop, " + latch.getCount(), did);

@ -68,4 +68,20 @@ public class CameraException extends RuntimeException {
public int getReason() {
return reason;
}
/**
* Whether this error is unrecoverable. If this function returns true,
* the Camera has been closed and it is likely showing a black preview.
* This is the right moment to show an error dialog to the user.
*
* @return true if this error is unrecoverable
*/
public boolean isUnrecoverable() {
switch (getReason()) {
case REASON_FAILED_TO_CONNECT: return true;
case REASON_FAILED_TO_START_PREVIEW: return true;
case REASON_DISCONNECTED: return true;
default: return false;
}
}
}

@ -31,14 +31,10 @@ public abstract class CameraListener {
/**
* Notifies about an error during the camera setup or configuration.
* At the moment, errors that are passed here are unrecoverable. When this is called,
* the camera has been released and is presumably showing a black preview.
*
* This is the right moment to show an error dialog to the user.
* You can try calling start() again, but that is not guaranteed to work - if it doesn't,
* this callback will be invoked again.
*
* In the future, more information will be passed through the {@link CameraException} instance.
* At this point you should inspect the {@link CameraException} reason using
* {@link CameraException#getReason()} and see what should be done, if anything.
* If the error is unrecoverable, this is the right moment to show an error dialog, for example.
*
* @param exception the error
*/

@ -504,7 +504,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isStarted()) return true;
if (!isOpened()) return true;
// Pass to our own GestureLayouts
CameraOptions options = mCameraController.getCameraOptions(); // Non null
@ -570,17 +570,17 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
* Returns whether the camera has started showing its preview.
* @return whether the camera has started
*/
public boolean isStarted() {
public boolean isOpened() {
return mCameraController.getState() >= CameraController.STATE_STARTED;
}
private boolean isStopped() {
private boolean isClosed() {
return mCameraController.getState() == CameraController.STATE_STOPPED;
}
/**
* Sets the lifecycle owner for this view. This means you don't need
* to call {@link #start()}, {@link #stop()} or {@link #destroy()} at all.
* to call {@link #open()}, {@link #close()} or {@link #destroy()} at all.
*
* @param owner the owner activity or fragment
*/
@ -596,7 +596,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
* This should be called onResume(), or when you are ready with permissions.
*/
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void start() {
public void open() {
if (!isEnabled()) return;
if (mCameraPreview != null) mCameraPreview.onResume();
@ -667,7 +667,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
* This should be called onPause().
*/
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void stop() {
public void close() {
mCameraController.stop();
if (mCameraPreview != null) mCameraPreview.onPause();
}
@ -1020,7 +1020,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
*/
public void setAudio(@NonNull Audio audio) {
if (audio == getAudio() || isStopped()) {
if (audio == getAudio() || isClosed()) {
// Check did took place, or will happen on start().
mCameraController.setAudio(audio);
@ -1033,7 +1033,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
// Stop the camera so it can be restarted by the developer onPermissionResult.
// Developer must also set the audio value again...
// Not ideal but good for now.
stop();
close();
}
}
@ -1072,7 +1072,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
*/
public void setMode(@NonNull Mode mode) {
if (mode == getMode() || isStopped()) {
if (mode == getMode() || isClosed()) {
// Check did took place, or will happen on start().
mCameraController.setMode(mode);
@ -1085,7 +1085,7 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
// Stop the camera so it can be restarted by the developer onPermissionResult.
// Developer must also set the session type again...
// Not ideal but good for now.
stop();
close();
}
}

@ -96,16 +96,6 @@
<enum name="video" value="1" />
</attr>
<attr name="cameraVideoQuality" format="enum">
<enum name="lowest" value="0" />
<enum name="highest" value="1" />
<enum name="maxQvga" value="2" />
<enum name="max480p" value="3" />
<enum name="max720p" value="4" />
<enum name="max1080p" value="5" />
<enum name="max2160p" value="6" />
</attr>
<attr name="cameraAudio" format="enum">
<enum name="off" value="0" />
<enum name="on" value="1" />

@ -2,16 +2,13 @@ package com.otaliastudios.cameraview.demo;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomSheetBehavior;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.Toast;
import com.otaliastudios.cameraview.CameraException;
@ -23,8 +20,6 @@ import com.otaliastudios.cameraview.PictureResult;
import com.otaliastudios.cameraview.Mode;
import com.otaliastudios.cameraview.VideoResult;
import java.io.File;
public class CameraActivity extends AppCompatActivity implements View.OnClickListener, ControlView.Callback {
@ -219,8 +214,8 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
for (int grantResult : grantResults) {
valid = valid && grantResult == PackageManager.PERMISSION_GRANTED;
}
if (valid && !camera.isStarted()) {
camera.start();
if (valid && !camera.isOpened()) {
camera.open();
}
}

5
docs/.gitignore vendored

@ -0,0 +1,5 @@
_site
_pages
*.sw?
.sass-cache
.jekyll-metadata

@ -0,0 +1,2 @@
source 'https://rubygems.org'
gem 'github-pages', group: :jekyll_plugins

@ -0,0 +1,248 @@
GEM
remote: https://rubygems.org/
specs:
activesupport (4.2.10)
i18n (~> 0.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
coffee-script (2.4.1)
coffee-script-source
execjs
coffee-script-source (1.11.1)
colorator (1.1.0)
commonmarker (0.17.13)
ruby-enum (~> 0.5)
concurrent-ruby (1.1.4)
dnsruby (1.61.2)
addressable (~> 2.5)
em-websocket (0.5.1)
eventmachine (>= 0.12.9)
http_parser.rb (~> 0.6.0)
ethon (0.11.0)
ffi (>= 1.3.0)
eventmachine (1.2.7)
execjs (2.7.0)
faraday (0.15.4)
multipart-post (>= 1.2, < 3)
ffi (1.9.25)
forwardable-extended (2.6.0)
gemoji (3.0.0)
github-pages (193)
activesupport (= 4.2.10)
github-pages-health-check (= 1.8.1)
jekyll (= 3.7.4)
jekyll-avatar (= 0.6.0)
jekyll-coffeescript (= 1.1.1)
jekyll-commonmark-ghpages (= 0.1.5)
jekyll-default-layout (= 0.1.4)
jekyll-feed (= 0.11.0)
jekyll-gist (= 1.5.0)
jekyll-github-metadata (= 2.9.4)
jekyll-mentions (= 1.4.1)
jekyll-optional-front-matter (= 0.3.0)
jekyll-paginate (= 1.1.0)
jekyll-readme-index (= 0.2.0)
jekyll-redirect-from (= 0.14.0)
jekyll-relative-links (= 0.5.3)
jekyll-remote-theme (= 0.3.1)
jekyll-sass-converter (= 1.5.2)
jekyll-seo-tag (= 2.5.0)
jekyll-sitemap (= 1.2.0)
jekyll-swiss (= 0.4.0)
jekyll-theme-architect (= 0.1.1)
jekyll-theme-cayman (= 0.1.1)
jekyll-theme-dinky (= 0.1.1)
jekyll-theme-hacker (= 0.1.1)
jekyll-theme-leap-day (= 0.1.1)
jekyll-theme-merlot (= 0.1.1)
jekyll-theme-midnight (= 0.1.1)
jekyll-theme-minimal (= 0.1.1)
jekyll-theme-modernist (= 0.1.1)
jekyll-theme-primer (= 0.5.3)
jekyll-theme-slate (= 0.1.1)
jekyll-theme-tactile (= 0.1.1)
jekyll-theme-time-machine (= 0.1.1)
jekyll-titles-from-headings (= 0.5.1)
jemoji (= 0.10.1)
kramdown (= 1.17.0)
liquid (= 4.0.0)
listen (= 3.1.5)
mercenary (~> 0.3)
minima (= 2.5.0)
nokogiri (>= 1.8.2, < 2.0)
rouge (= 2.2.1)
terminal-table (~> 1.4)
github-pages-health-check (1.8.1)
addressable (~> 2.3)
dnsruby (~> 1.60)
octokit (~> 4.0)
public_suffix (~> 2.0)
typhoeus (~> 1.3)
html-pipeline (2.9.1)
activesupport (>= 2)
nokogiri (>= 1.4)
http_parser.rb (0.6.0)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
jekyll (3.7.4)
addressable (~> 2.4)
colorator (~> 1.0)
em-websocket (~> 0.5)
i18n (~> 0.7)
jekyll-sass-converter (~> 1.0)
jekyll-watch (~> 2.0)
kramdown (~> 1.14)
liquid (~> 4.0)
mercenary (~> 0.3.3)
pathutil (~> 0.9)
rouge (>= 1.7, < 4)
safe_yaml (~> 1.0)
jekyll-avatar (0.6.0)
jekyll (~> 3.0)
jekyll-coffeescript (1.1.1)
coffee-script (~> 2.2)
coffee-script-source (~> 1.11.1)
jekyll-commonmark (1.2.0)
commonmarker (~> 0.14)
jekyll (>= 3.0, < 4.0)
jekyll-commonmark-ghpages (0.1.5)
commonmarker (~> 0.17.6)
jekyll-commonmark (~> 1)
rouge (~> 2)
jekyll-default-layout (0.1.4)
jekyll (~> 3.0)
jekyll-feed (0.11.0)
jekyll (~> 3.3)
jekyll-gist (1.5.0)
octokit (~> 4.2)
jekyll-github-metadata (2.9.4)
jekyll (~> 3.1)
octokit (~> 4.0, != 4.4.0)
jekyll-mentions (1.4.1)
html-pipeline (~> 2.3)
jekyll (~> 3.0)
jekyll-optional-front-matter (0.3.0)
jekyll (~> 3.0)
jekyll-paginate (1.1.0)
jekyll-readme-index (0.2.0)
jekyll (~> 3.0)
jekyll-redirect-from (0.14.0)
jekyll (~> 3.3)
jekyll-relative-links (0.5.3)
jekyll (~> 3.3)
jekyll-remote-theme (0.3.1)
jekyll (~> 3.5)
rubyzip (>= 1.2.1, < 3.0)
jekyll-sass-converter (1.5.2)
sass (~> 3.4)
jekyll-seo-tag (2.5.0)
jekyll (~> 3.3)
jekyll-sitemap (1.2.0)
jekyll (~> 3.3)
jekyll-swiss (0.4.0)
jekyll-theme-architect (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-cayman (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-dinky (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-hacker (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-leap-day (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-merlot (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-midnight (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-minimal (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-modernist (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-primer (0.5.3)
jekyll (~> 3.5)
jekyll-github-metadata (~> 2.9)
jekyll-seo-tag (~> 2.0)
jekyll-theme-slate (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-tactile (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-theme-time-machine (0.1.1)
jekyll (~> 3.5)
jekyll-seo-tag (~> 2.0)
jekyll-titles-from-headings (0.5.1)
jekyll (~> 3.3)
jekyll-watch (2.1.2)
listen (~> 3.0)
jemoji (0.10.1)
gemoji (~> 3.0)
html-pipeline (~> 2.2)
jekyll (~> 3.0)
kramdown (1.17.0)
liquid (4.0.0)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
mercenary (0.3.6)
mini_portile2 (2.4.0)
minima (2.5.0)
jekyll (~> 3.5)
jekyll-feed (~> 0.9)
jekyll-seo-tag (~> 2.1)
minitest (5.11.3)
multipart-post (2.0.0)
nokogiri (1.9.1)
mini_portile2 (~> 2.4.0)
octokit (4.13.0)
sawyer (~> 0.8.0, >= 0.5.3)
pathutil (0.16.2)
forwardable-extended (~> 2.6)
public_suffix (2.0.5)
rb-fsevent (0.10.3)
rb-inotify (0.10.0)
ffi (~> 1.0)
rouge (2.2.1)
ruby-enum (0.7.2)
i18n
ruby_dep (1.5.0)
rubyzip (1.2.2)
safe_yaml (1.0.4)
sass (3.7.2)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
sawyer (0.8.1)
addressable (>= 2.3.5, < 2.6)
faraday (~> 0.8, < 1.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thread_safe (0.3.6)
typhoeus (1.3.1)
ethon (>= 0.9.0)
tzinfo (1.2.5)
thread_safe (~> 0.1)
unicode-display_width (1.4.0)
PLATFORMS
ruby
DEPENDENCIES
github-pages
BUNDLED WITH
1.17.2

@ -0,0 +1 @@
Read the docs at https://natario1.github.io/CameraView .

@ -0,0 +1,70 @@
# Glide: https://github.com/bumptech/glide/blob/gh-pages/_config.yml
# Source repo: https://github.com/bruth/jekyll-docs-template
# Source site: http://bruth.github.io/jekyll-docs-template/
# Ref guide: https://visualstudiomagazine.com/Articles/2015/03/01/GitHub-Pages.aspx?Page=2
title: CameraView v2
subtitle: A well documented, high-level Android interface that makes capturing pictures and videos easy, addressing all of the common issues and needs.
github: [metadata]
author:
name: Mattia Iavarone
email: mat.iavarone@gmail.com
github: natario1
# if you wish to integrate disqus on pages set your shortname here
disqus_shortname: ''
# if you use google analytics, add your tracking id here
google_analytics_id: ''
# Enable/show navigation. There are there options:
# 0 - always hide
# 1 - always show
# 2 - show only if posts are present
navigation: 2
# URL to source code, used in _includes/footer.html
codeurl: 'https://github.com/natario1/CameraView'
# Default categories (in order) to appear in the navigation
# (Missing: DOCS and DONATE info)
sections: [
['about', 'Overview'],
['docs', 'Documentation'],
['extra', 'More']
]
# Keep as an empty string if served up at the root. If served up at a specific
# path (e.g. on GitHub pages) leave off the trailing slash, e.g. /my-project
baseurl: '/CameraView'
# Dates are not included in permalinks
permalink: none
# Syntax highlighting
highlighter: rouge
# Since these are pages, it doesn't really matter
future: true
# Exclude non-site files
exclude: ['script', 'README.md']
# Use the kramdown Markdown renderer
markdown: kramdown
redcarpet:
extensions: [
'no_intra_emphasis',
'fenced_code_blocks',
'autolink',
'strikethrough',
'superscript',
'with_toc_data',
'tables',
'hardwrap'
]
# Use Github Flavored Markdown
kramdown:
input: GFM

@ -0,0 +1,12 @@
<div id="disqus_thread"></div>
<script type="text/javascript">
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
var disqus_shortname = '{{ site.disqus_shortname }}'; // required: replace example with your forum shortname
/* * * DON'T EDIT BELOW THIS LINE * * */
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>

@ -0,0 +1 @@
View on GitHub: <a href="{% if site.codeurl %}{{ site.codeurl }}{% else %}{{ site.baseurl }}{% endif %}">{{ site.title }}</a>

@ -0,0 +1,8 @@
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', '{{ site.google_analytics_id }}', 'auto');
ga('send', 'pageview');
</script>

@ -0,0 +1,3 @@
<h4><a class="brand" href="{{ site.baseurl }}/">{{ site.title }}</a>
{% if site.subtitle %}<small>{{ site.subtitle }}</small>{% endif %}
</h4>

@ -0,0 +1,16 @@
<ul class="nav nav-pills nav-stacked">
<li><a href="{{ site.baseurl }}/">Home</a></li>
{% for section in site.sections %}
{% assign attr = section[0] %}
{% assign label = section[1] %}
{% for page in site.categories[attr] %}
{% if forloop.first %}
<li class="nav-header">{{ label }}</li>
{% endif %}
<li data-order="{{ page.order }}"><a href="{{ site.baseurl }}{{ page.url }}">{{ page.title }}</a></li>
{% endfor %}
{% endfor %}
<!-- List additional links. It is recommended to add a divider
e.g. <li class="divider"></li> first to break up the content. -->
</ul>

@ -0,0 +1,115 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width">
<title>{{ site.title }}{% if page.title %} : {{ page.title }}{% endif %}</title>
<meta name="description" content="{{ site.subtitle }}">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="{{ site.baseurl }}/css/syntax.css">
<link rel="stylesheet" href="{{ site.baseurl }}/css/main.css">
</head>
<body>
<div class="container">
<div class="row">
<div id="header" class="col-sm-12">
{% include header.html %}
</div>
</div>
<div class="row">
{% assign post_count = site.posts|size %}
{% if site.navigation != 0 and site.navigation == 1 or post_count > 0 %}
<div id="navigation" class="col-sm-2">
{% include navigation.html %}
</div>
<div id="content" class="col-sm-10">
{{ content }}
</div>
{% else %}
<div id="content" class="col-sm-12">
{{ content }}
</div>
{% endif %}
</div>
{% if page.disqus == 1 %}
<div class="row">
{% if site.navigation == 1 or post_count > 0 %}
<div id="navigation" class="col-sm-2"></div>
<div id="disqus" class="col-sm-10">
{% include disqus.html %}
</div>
{% else %}
<div id="disqus" class="col-sm-12">
{% include disqus.html %}
</div>
{% endif %}
</div>
{% endif %}
<div class="row">
<div id="footer" class="col-sm-12">
{% include footer.html %}
</div>
</div>
</div>
<script>
function orderNav() {
var list,
section,
header,
sections = [],
lists = {},
headers = {};
var locationObj = document.location.href;
var navUl = document.querySelectorAll('#navigation ul')[0],
navLis = document.querySelectorAll('#navigation ul li');
if (!navUl) return;
for (var i = 0; i < navLis.length; i++) {
var order, li = navLis[i];
if (li.classList.contains('nav-header')) {
section = li.textContent || li.innerText;
sections.push(section);
headers[section] = li;
continue;
}
var link = li.childNodes[0];
if (locationObj && link && locationObj.endsWith(link.getAttribute('href'))) {
li.classList.add('active');
}
if (!lists[section]) {
lists[section] = [];
}
order = parseFloat(li.getAttribute('data-order'))
lists[section].push([order, li]);
}
for (var i = 0; i < sections.length; i++) {
section = sections[i];
list = lists[section].sort(function(a, b) {
return a[0] - b[0];
});
if (header = headers[section]) {
navUl.appendChild(header);
}
for (var j = 0; j < list.length; j++) {
navUl.appendChild(list[j][1]);
}
}
}
if (document.querySelectorAll) orderNav();
</script>
{% if site.google_analytics_id != "" %}
{% include google_analytics.html %}
{% endif %}
</body>
</html>

@ -0,0 +1,11 @@
---
layout: default
---
<div class="page-header">
<h2>{{ page.title }}
{% if page.subtitle %}<small>{{ page.subtitle }}</small>{% endif %}
</h2>
</div>
{{ content }}

@ -0,0 +1,78 @@
---
layout: page
title: "Camera Events"
subtitle: "Events and lifecycle"
category: docs
date: 2018-12-20 20:02:08
order: 1
---
The camera engine will notify anyone about camera events that took place, either on their own or
after developer action. To access these events, set up one or more `CameraListener` instances.
All actions taken on a `CameraView` instance are asynchronous, which means that the callback can be
executed at any time in the future. For convenience, all of them are executed on the UI thread.
```java
camera.addCameraListener(new CameraListener() {
public void onCameraOpened(CameraOptions options) {}
public void onCameraClosed() {}
public void onCameraError(CameraException error) {}
public void onPictureTaken(PictureResult result) {}
public void onVideoTaken(VideoResult result) {}
public void onOrientationChanged(int orientation) {}
public void onFocusStart(PointF point) {}
public void onFocusEnd(boolean successful, PointF point) {}
public void onZoomChanged(float newValue, float[] bounds, PointF[] fingers) {}
public void onExposureCorrectionChanged(float newValue, float[] bounds, PointF[] fingers) {}
});
```
### Lifecycle
CameraView has its own lifecycle, which is basically made of an open and a closed state.
You will listen to these events using `onCameraOpened` and `onCameraClosed` callbacks:
```java
camera.addCameraListener(new CameraListener() {
/**
* Notifies that the camera was opened.
* The options object collects all supported options by the current camera.
*/
@Override
public void onCameraOpened(CameraOptions options) {}
/**
* Notifies that the camera session was closed.
*/
@Override
public void onCameraClosed() {}
});
```
The open callback is especially important because the `CameraOptions` includes all the available
options of the current sensor. This can be used to adjust the UI, for example, show a flash icon
if flash is supported.
### Related APIs
|Method|Description|
|------|-----------|
|`open()`|Starts the engine. This will cause a future call to `onCameraOpened()` (or an error)|
|`close()`|Stops the engine. This will cause a future call to `onCameraClosed()`|
|`isOpened()`|Returns true if `open()` was called successfully. This does not mean that camera is showing preview already.|
|`getCameraOptions()`|If camera was opened, returns non-null object with information about what is supported.|
Take a look at public methods in `CameraOptions` to know more.

@ -0,0 +1,105 @@
---
layout: page
title: "Capture Size"
subtitle: "Set size of output media"
category: docs
order: 8
date: 2018-12-20 22:07:22
---
If you are planning to use the snapshot APIs, the size of the media output is that of the preview,
accounting for any cropping made when [measuring the view](preview-size.html).
If you are planning to use the standard APIs for capturing, then what follows applies.
### Controlling Size
Size is controlled using `setPictureSize` and `setVideoSize` for, respectively, picture and video
output. These method will accept a `SizeSelector`. The point of a `SizeSelector` is to analyze the
available sizes that the sensor offers, and choose the ones it prefers.
```java
// This will be the size of pictures taken with takePicture().
cameraView.setPictureSize(new SizeSelector() {
@Override
public List<Size> select(List<Size> source) {
// Receives a list of available sizes.
// Must return a list of acceptable sizes.
}
});
// This will be the size of videos taken with takeVideo().
cameraView.setVideoSize(new SizeSelector() {
@Override
public List<Size> select(List<Size> source) {
// Same here.
}
});
```
In practice, this is way easier using XML attributes or leveraging the `SizeSelectors` utilities.
### XML attributes
```xml
<com.otaliastudios.cameraview.CameraView
app:cameraPictureSizeMinWidth="100"
app:cameraPictureSizeMinHeight="100"
app:cameraPictureSizeMaxWidth="3000"
app:cameraPictureSizeMaxHeight="3000"
app:cameraPictureSizeMinArea="10000000"
app:cameraPictureSizeMaxArea="50000000"
app:cameraPictureSizeAspectRatio="1:1"
app:cameraPictureSizeSmallest="true|false"
app:cameraPictureSizeBiggest="true|false"
app:cameraVideoSizeMinWidth="100"
app:cameraVideoSizeMinHeight="100"
app:cameraVideoSizeMaxWidth="3000"
app:cameraVideoSizeMaxHeight="3000"
app:cameraVideoSizeMinArea="10000000"
app:cameraVideoSizeMaxArea="50000000"
app:cameraVideoSizeAspectRatio="1:1"
app:cameraVideoSizeSmallest="true|false"
app:cameraVideoSizeBiggest="true|false"
/>
```
The `cameraPicture*` attributes are used in picture mode, while the `cameraVideo*` attributes are used in video mode.
Note that, for each mode, if you declare more than one XML constraint, the resulting selector will try
to match **all** the constraints. Be careful - it is very likely that applying lots of constraints will give empty results.
### SizeSelectors utilities
All these XML attrs are actually shorthands to some `SizeSelectors` utility method.
For more versatility, or to address selection issues with multiple constraints,
we encourage you to use `SizeSelectors` to get a selector, and then apply it to the `CameraView` as seen.
The utilities will even let you merge different selectors with `or` or `and` logic, in a very
intuitive way. For example:
```java
SizeSelector width = SizeSelectors.minWidth(1000);
SizeSelector height = SizeSelectors.minHeight(2000);
SizeSelector dimensions = SizeSelectors.and(width, height); // Matches sizes bigger than 1000x2000.
SizeSelector ratio = SizeSelectors.aspectRatio(AspectRatio.of(1, 1), 0); // Matches 1:1 sizes.
SizeSelector result = SizeSelectors.or(
SizeSelectors.and(ratio, dimensions), // Try to match both constraints
ratio, // If none is found, at least try to match the aspect ratio
SizeSelectors.biggest() // If none is found, take the biggest
);
camera.setPictureSize(result);
camera.setVideoSize(result);
```
This selector will try to find square sizes bigger than 1000x2000. If none is found, it falls back
to just square sizes.
### Related APIs
|Method|Description|
|------|-----------|
|`setPictureSize(SizeSelector)`|Provides a size selector for the capture size in `PICTURE` mode.|
|`setVideoSize(SizeSelector)`|Provides a size selector for the capture size in `VIDEO` mode.|
|`getPictureSize()`|Returns the size of the output picture, including any rotation. Returns null in `VIDEO` mode.|
|`getVideoSize()`|Returns the size of the output video, including any rotation. Returns null in `PICTURE` mode.|

@ -0,0 +1,82 @@
---
layout: page
title: "Capturing Media"
subtitle: "Taking pictures and videos"
category: docs
order: 3
date: 2018-12-20 20:53:17
---
This section introduces some key concepts about media capturing, and about the `Mode` control.
### Mode control
The mode control determines what can be captured with the standard APIs (read below). It can be set through XML
or dynamically changed using `cameraView.setMode()`. The current mode value has a few consequences:
- Sizing: the capture size is chosen among the available picture or video sizes,
depending on the flag, according to the given size selector.
- Capturing: while in picture mode, `takeVideo` will throw an exception.
- Capturing: while in video mode, `takePicture` will throw an exception.
- Permission behavior: when requesting a `video` session, the record audio permission will be requested.
If this is needed, the audio permission should be added to your manifest or the app will crash.
Please read the [permissions page](runtime-permissions.html).
```java
cameraView.setMode(Mode.PICTURE); // for pictures
cameraView.setMode(Mode.VIDEO); // for video
```
### Capturing media
The library supports 4 capture APIs, two for pictures and two for videos.
- Standard APIs: `takePicture()` and `takeVideo()`. These take a high quality picture or video, depending
on the configuration values that were used. The standard APIs **must** be called in the appropriate `Mode`.
- Snapshot APIs: `takePictureSnapshot()` and `takeVideoSnapshot()`. These take a super fast, reliable
snapshot of the camera preview. The snapshot APIs can be called in any `Mode` (you can snap videos in picture mode).
Beyond being extremely fast, and small in size (though low quality), snapshot APIs have the benefit
that the result is automatically cropped to match the view bounds. This means that, if `CameraView` is square,
resulting snapshots are square as well, no matter what the sensor available sizes are.
|Method|Takes|Quality|Callable in `Mode.PICTURE`|Callable in `Mode.VIDEO`|Auto crop|Output size|
|------|-----|-------|--------------------------|------------------------|---------|-----------|
|`takePicture()`|Pictures|Standard|`yes`|`no`|`no`|That of `setPictureSize`|
|`takeVideo(File)`|Videos|Standard|`no`|`yes`|`no`|That of `setVideoSize`|
|`takePictureSnapshot()`|Pictures|Snapshot|`yes`|`yes`|`yes`|That of the view|
|`takeVideoSnapshot(File)`|Videos|Snapshot|`yes`|`yes`|`yes`|That of the view|
Please note that the video snaphot features requires:
- API 18. If called before, it throws
- An OpenGL preview (see [previews](previews.html)). If not, it throws
### Capturing pictures while recording
This is allowed at the following conditions:
- `takePictureSnapshot()` is used (no HQ pictures)
- the OpenGL preview is used (see [previews](previews.html))
### Related XML attributes
```xml
<com.otaliastudios.cameraview.CameraView
app:cameraMode="picture|video"/>
```
### Related APIs
|Method|Description|
|------|-----------|
|`setMode()`|Either `Mode.VIDEO` or `Mode.PICTURE`.|
|`isTakingVideo()`|Returns true if the camera is currently recording a video.|
|`isTakingPicture()`|Returns true if the camera is currently capturing a picture.|
|`takePicture()`|Takes a high quality picture.|
|`takeVideo(File)`|Takes a high quality video.|
|`takePictureSnapshot()`|Takes a picture snapshot.|
|`takeVideoSnapshot(File)`|Takes a video snapshot.|
|`getPictureSize()`|Returns the output picture size, accounting for any rotation. Null while in `VIDEO` mode.|
|`getVideoSize()`|Returns the output video size, accounting for any rotation. Null while in `PICTURE` mode.|
|`getSnapshotSize()`|Returns the size of pictures taken with `takePictureSnapshot()` or videos taken with `takeVideoSnapshot()`. Accounts for rotation and cropping.|

@ -0,0 +1,13 @@
---
layout: page
title: "Changelog"
category: about
date: 2018-12-20 17:49:29
order: 3
---
New versions are released through GitHub, so the reference page is the [GitHub Releases](https://github.com/natario1/CameraView/releases) page.
### v2.0.0-beta01
This is the first beta release. For changes with respect to v1, please take a look at the [migration guide](../extra/v1-migration-guide.html).

@ -0,0 +1,14 @@
---
layout: page
title: "Contact"
category: extra
date: 2018-12-20 19:47:10
order: 4
---
This library was mostly developed by [Mattia Iavarone](https://github.com/natario1) (@natario1),
that you can also contact personally by <a href="mailto:mat.iavarone@gmail.com">sending an email.</a>
You can use the [project GitHub page](https://github.com/natario1/CameraView) for any kind of communication
regarding the library. For non-issues, please refer to the email address above.

@ -0,0 +1,52 @@
---
layout: page
title: "Contributing & License"
category: extra
date: 2018-12-20 19:28:45
order: 3
---
Everyone is welcome to contribute with suggestions or pull requests, as the library is under active development,
although it has reached a high level of stability.
We are grateful to anyone who has contributed with fixes, features or feature requests.
## Bug reports
Please make sure to fill the bug report issue template on GitHub.
We highly recommend to try to reproduce the bug in the demo app, as this helps a lot in debugging
and excludes programming errors from your side.
Make sure to include:
- A clear and concise description of what the bug is
- CameraView version, device type, Android API level
- Exact steps to reproduce the issue
- Description of the expected behavior
Recommended extras:
- Screenshots
- LogCat logs (use `CameraLogger.setLogLevel(LEVEL_VERBOSE)` to print all)
- Link to a GitHub repo where the bug is reproducible
## Pull Requests
Please open an issue first.
Unless your PR is a simple fix (typos, documentation, bugs with obvious solution), opening an issue
will let us discuss the problem, take design decisions and have a reference to the issue description.
Please write tests.
Unless the code was already not covered by tests, updated tests are required for merging. The lib
has a few unit tests and more robust tests in the `androidTest` folder, which can be run by Android Studio.
## License
CameraView was formally born as a fork of [CameraKit-Android](https://github.com/wonderkiln/CameraKit-Android)
and [Google's CameraView](https://github.com/google/cameraview), but has been completely rewritten since.
CameraKit's source code is licensed under the [MIT](https://github.com/wonderkiln/CameraKit-Android/blob/master/LICENSE) license.
CameraView is licensed under the [MIT](https://github.com/natario1/CameraView/blob/master/LICENSE) license as well.

@ -0,0 +1,139 @@
---
layout: page
title: "Controls"
subtitle: "Output parameters and capture options"
category: docs
order: 2
date: 2018-12-20 21:08:53
---
CameraView supports a wide range of controls that will control the behavior of the sensor or the
quality of the output.
Everything can be controlled through XML parameters or programmatically. For convenience, most options
are represented by `enum` classes extending the `Control` class. This makes it possible to use
`CameraView.set(Control)` to set the given control, or `CameraOptions.supports(Control)` to see if it is supported.
### XML Attributes
```xml
<com.otaliastudios.cameraview.CameraView
app:cameraFacing="front|back"
app:cameraFlash="off|on|auto|torch"
app:cameraWhiteBalance="auto|incandescent|fluorescent|daylight|cloudy"
app:cameraHdr="off|on"
app:cameraAudio="on|off"
app:cameraAudioBitRate="0"
app:cameraVideoCodec="deviceDefault|h263|h264"
app:cameraVideoMaxSize="0"
app:cameraVideoMaxDuration="0"
app:cameraVideoBitRate="0"/>
```
### APIs
##### cameraFacing
Which camera to use, either back facing or front facing.
Defaults to the first available value (tries `BACK` first).
```java
cameraView.setFacing(Facing.BACK);
cameraView.setFacing(Facing.FRONT);
```
##### cameraFlash
Flash mode, either off, on, auto or torch. Defaults to `OFF`.
```java
cameraView.setFlash(Flash.OFF);
cameraView.setFlash(Flash.ON);
cameraView.setFlash(Flash.AUTO);
cameraView.setFlash(Flash.TORCH);
```
##### cameraVideoCodec
Sets the encoder for video recordings. Defaults to `DEVICE_DEFAULT`,
which should typically be H_264.
```java
cameraView.setVideoCodec(VideoCodec.DEVICE_DEFAULT);
cameraView.setVideoCodec(VideoCodec.H_263);
cameraView.setVideoCodec(VideoCodec.H_264);
```
##### cameraWhiteBalance
Sets the desired white balance for the current session.
Defaults to `AUTO`.
```java
cameraView.setWhiteBalance(WhiteBalance.AUTO);
cameraView.setWhiteBalance(WhiteBalance.INCANDESCENT);
cameraView.setWhiteBalance(WhiteBalance.FLUORESCENT);
cameraView.setWhiteBalance(WhiteBalance.DAYLIGHT);
cameraView.setWhiteBalance(WhiteBalance.CLOUDY);
```
##### cameraHdr
Turns on or off HDR captures. Defaults to `OFF`.
```java
cameraView.setHdr(Hdr.OFF);
cameraView.setHdr(Hdr.ON);
```
##### cameraAudio
Turns on or off audio stream while recording videos.
Defaults to `ON`.
```java
cameraView.setAudio(Audio.OFF);
cameraView.setAudio(Audio.ON);
```
##### cameraAudioBitRate
Controls the audio bit rate in bits per second.
Use 0 or a negative value to fallback to the encoder default. Defaults to 0.
```java
cameraView.setAudioBitRate(0);
cameraView.setAudioBitRate(64000);
```
##### cameraVideoMaxSize
Defines the maximum size in bytes for recorded video files.
Once this size is reached, the recording will automatically stop.
Defaults to unlimited size. Use 0 or negatives to disable.
```java
cameraView.setVideoMaxSize(100000);
cameraView.setVideoMaxSize(0); // Disable
```
##### cameraVideoMaxDuration
Defines the maximum duration in milliseconds for video recordings.
Once this duration is reached, the recording will automatically stop.
Defaults to unlimited duration. Use 0 or negatives to disable.
```java
cameraView.setVideoMaxDuration(100000);
cameraView.setVideoMaxDuration(0); // Disable
```
##### cameraVideoBitRate
Controls the video bit rate in bits per second.
Use 0 or a negative value to fallback to the encoder default. Defaults to 0.
```java
cameraView.setVideoBitRate(0);
cameraView.setVideoBitRate(4000000);
```

@ -0,0 +1,25 @@
---
layout: page
title: "Debugging"
category: docs
order: 10
date: 2018-12-20 20:02:38
---
`CameraView` will log a lot of interesting events related to the camera lifecycle. These are important
to identify bugs. The default logger will simply use Android `Log` methods posting to logcat.
You can attach and detach external loggers using `CameraLogger.registerLogger()`:
```java
CameraLogger.registerLogger(new Logger() {
@Override
public void log(@LogLevel int level, String tag, String message, @Nullable Throwable throwable) {
// For example...
Crashlytics.log(message);
}
});
```
Make sure you enable the logger using `CameraLogger.setLogLevel(@LogLevel int)`. The default will only
log error events.

@ -0,0 +1,49 @@
---
layout: page
title: "Error Handling"
category: docs
order: 9
date: 2018-12-20 20:02:31
---
Errors are posted to the registered `CameraListener`s callback:
```java
@Override
public void onCameraError(CameraException error) {
// Got error!
};
```
You are supposed to inspect the `CameraException` object as it contains useful information about
what happened and what should be done, if anything. All things that fail can end up throwing this
exception, which includes temporary actions like taking a picture, or functional actions like
starting the camera preview.
### Unrecoverable errors
You can exclude unrecoverable errors using `CameraException.isUnrecoverable()`.
If this function returns true, at this point the camera has been released and it is likely showing
a black preview. The operation can't go on.
You can try to call `camera.start()` again, but that's not guaranteed to work. For example, the
camera sensor might be in use by another application, so there's nothing we could do.
### Other errors
For more fine grained control over what happened, inspect the reason using `CameraException.getReason()`.
This will return one of the `CameraException.REASON_` constants:
|Constant|Description|Unrecoverable|
|--------|-----------|-------------|
|`REASON_UNKNOWN`|Unknown error. No other info available.|No|
|`REASON_FAILED_TO_CONNECT`|Failed to connect to the camera service.|Yes|
|`REASON_FAILED_TO_START_PREVIEW`|Failed to start the camera preview.|Yes|
|`REASON_DISCONNECTED`|Camera was forced to disconnect by the system.|Yes|
|`REASON_PICTURE_FAILED`|Could not take a picture or picture snapshot.|No|
|`REASON_VIDEO_FAILED`|Could not take a video or video snapshot.|No|
|`REASON_NO_CAMERA`|Could not find a camera for this `Facing` value. You can try another.|No|

@ -0,0 +1,51 @@
---
layout: page
title: "Frame Processing"
subtitle: "Process each frame in real time"
category: docs
order: 5
date: 2018-12-20 20:45:42
---
We support frame processors that will receive data from the camera preview stream. This is a useful
feature with a wide range of applications. For example, the frames can be sent to a face detector,
a QR code detector, the
[Firebase Machine Learning Kit](https://firebase.google.com/products/ml-kit/), or any other frame consumer.
```java
cameraView.addFrameProcessor(new FrameProcessor() {
@Override
@WorkerThread
public void process(Frame frame) {
byte[] data = frame.getData();
int rotation = frame.getRotation();
long time = frame.getTime();
Size size = frame.getSize();
int format = frame.getFormat();
// Process...
}
}
```
For your convenience, the `FrameProcessor` method is run in a background thread so you can do your job
in a synchronous fashion. Once the process method returns, internally we will re-use the `Frame` instance and
apply new data to it. So:
- you can do your job synchronously in the `process()` method
- if you must hold the `Frame` instance longer, use `frame = frame.freeze()` to get a frozen instance
that will not be affected
### Related APIs
|Frame API|Type|Description|
|---------|----|-----------|
|`camera.addFrameProcessor(FrameProcessor)`|`-`|Register a `FrameProcessor`.|
|`frame.getData()`|`byte[]`|The current preview frame, in its original orientation.|
|`frame.getTime()`|`long`|The preview timestamp, in `System.currentTimeMillis()` reference.|
|`frame.getRotation()`|`int`|The rotation that should be applied to the byte array in order to see what the user sees.|
|`frame.getSize()`|`Size`|The frame size, before any rotation is applied, to access data.|
|`frame.getFormat()`|`int`|The frame `ImageFormat`. This will always be `ImageFormat.NV21` for now.|
|`frame.freeze()`|`Frame`|Clones this frame and makes it immutable. Can be expensive because requires copying the byte array.|
|`frame.release()`|`-`|Disposes the content of this frame. Should be used on frozen frames to release memory.|

@ -0,0 +1,51 @@
---
layout: page
title: "Gestures"
subtitle: "Gestures control"
category: docs
order: 4
date: 2018-12-20 20:49:35
---
`CameraView` listen to lots of different gestures inside its bounds. You have the chance to map
these gestures to particular actions or camera controls, using the `mapGesture()` method.
This lets you emulate typical behaviors in a single line:
```java
cameraView.mapGesture(Gesture.PINCH, GestureAction.ZOOM); // Pinch to zoom!
cameraView.mapGesture(Gesture.TAP, GestureAction.FOCUS_WITH_MARKER); // Tap to focus!
cameraView.mapGesture(Gesture.LONG_TAP, GestureAction.CAPTURE); // Long tap to shoot!
```
Simple as that. There are two things to be noted:
- Not every mapping is valid. For example, you can't control zoom with long taps, or start focusing by pinching.
- Some actions might not be supported by the sensor. Check out `CameraOptions` to know what's legit and what's not.
|Gesture|Description|Can be mapped to|
|-------------|-----------|----------------|
|`PINCH`|Pinch gesture, typically assigned to the zoom control.|`zoom` `exposureCorrection` `none`|
|`TAP`|Single tap gesture, typically assigned to the focus control.|`focus` `focusWithMarker` `capture` `none`|
|`LONG_TAP`|Long tap gesture.|`focus` `focusWithMarker` `capture` `none`|
|`SCROLL_HORIZONTAL`|Horizontal movement gesture.|`zoom` `exposureCorrection` `none`|
|`SCROLL_VERTICAL`|Vertical movement gesture.|`zoom` `exposureCorrection` `none`|
### XML Attributes
```xml
<com.otaliastudios.cameraview.CameraView
app:cameraGesturePinch="zoom|exposureCorrection|none"
app:cameraGestureTap="focus|focusWithMarker|capture|none"
app:cameraGestureLongTap="focus|focusWithMarker|capture|none"
app:cameraGestureScrollHorizontal="zoom|exposureCorrection|none"
app:cameraGestureScrollVertical="zoom|exposureCorrection|none"/>
```
### Related APIs
|Method|Description|
|------|-----------|
|`mapGesture(Gesture, GestureAction)`|Maps a certain gesture to a certain action. No-op if the action is not supported.|
|`getGestureAction(Gesture)`|Returns the action currently mapped to the given gesture.|
|`clearGesture(Gesture)`|Clears any action mapped to the given gesture.|

@ -0,0 +1,146 @@
---
layout: page
title: "Getting Started"
subtitle: "Simple guide to take your first picture"
category: about
date: 2018-12-20 17:48:58
order: 2
---
To use the CameraView engine, simply add a `CameraView` to your layout:
```xml
<com.otaliastudios.cameraview.CameraView
android:id="@+id/camera"
android:keepScreenOn="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
```
This is the one and only interface to the engine, and is meant to be hosted inside a UI component
like `Fragment` or `Activity`. The camera component is bound to the host lifecycle, so, as soon as possible,
you should register the host:
```java
// For activities
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CameraView camera = findViewById(R.id.camera);
camera.setLifecycleOwner(this);
}
// For fragments
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
CameraView camera = findViewById(R.id.camera);
camera.setLifecycleOwner(getViewLifecycleOwner());
}
```
Can't resolve the lifecycle owner interface? Read [below](#without-support-libraries).
### Set up a CameraListener
The next thing to do is to add a new `CameraListener` to be notified about camera events.
You can do this on a per-action basis, but it's easier to just add one when the UI is created:
```java
camera.addCameraListener(new CameraListener() {
@Override
public void onPictureTaken(PictureResult result) {
// A Picture was taken!
}
@Override
public void onVideoTaken(VideoResult result) {
// A Video was taken!
}
// And much more
})
```
### Take a picture
We're done. To take a picture upon user input, just call `takePicture()`.
```java
camera.addCameraListener(new CameraListener() {
@Override
public void onPictureTaken(PictureResult result) {
// Picture was taken!
// If planning to save a file, just get the jpeg array.
byte[] jpeg = result.getJpeg();
// If planning to show a Bitmap, we will take care of
// EXIF rotation and background threading for you...
result.asBitmap(maxWidth, maxHeight, callback);
}
});
camera.takePicture();
```
Read the docs about `takePictureSnapshot()` for a super fast, lower quality alternative.
### Take a video
Taking a video is just the same thing, except that you must make sure that camera is in `Mode.VIDEO` mode,
and that you have write permissions to write the file:
```java
camera.addCameraListener(new CameraListener() {
@Override
public void onVideoTaken(VideoResult result) {
// Video was taken!
// Use result.getFile() to access a file holding
// the recorded video.
}
});
// Select output file. Make sure you have write permissions.
camera.setMode(Mode.VIDEO);
camera.takeVideo(file);
// Later... stop recording. This will trigger onVideoTaken().
camera.stopVideo();
```
Read the docs about `takeVideoSnapshot()` for a super fast, lower quality alternative.
### Configuration and more
This was it, but there is a ton of other options available to customize the camera behavior,
to control the sensor, the UI appearance, the quality and size of the output, or to live process
frames. Keep reading the documentation!
For runtime permissions and Manifest setup, please read the [permissions page]().
### Without support libraries
If you are not using support libraries and you can't resolve the LifecycleOwner interface,
make sure you override `onResume`, `onPause` and `onDestroy` in your activity (`onDestroyView`
in your fragment), and call `CameraView.open()`, `close()` and `destroy()`.
```java
@Override
protected void onResume() {
super.onResume();
cameraView.open();
}
@Override
protected void onPause() {
super.onPause();
cameraView.close();
}
@Override
protected void onDestroy() {
super.onDestroy();
cameraView.destroy();
}
```

@ -0,0 +1,29 @@
---
layout: page
title: "Install"
subtitle: "Integrate in your project"
category: about
order: 1
date: 2018-12-20 17:47:32
---
The library works on API 15+, which is the only requirement and should be met by most projects nowadays.
It is publicly hosted on [JCenter](https://bintray.com/natario/android/CameraView), where you
can download the AAR package. To fetch with Gradle, make sure you add the JCenter repository in your root projects `build.gradle` file:
```groovy
allprojects {
repositories {
jcenter()
}
}
```
Then simply download the latest version:
```groovy
api 'com.otaliastudios:cameraview:2.0.0-beta01'
```
No other configuration steps are needed.

@ -0,0 +1,77 @@
---
layout: page
title: "More features"
subtitle: "Undocumented features & more"
category: docs
order: 11
date: 2018-12-20 20:41:20
---
### Extra controls
```xml
<com.otaliastudios.cameraview.CameraView
app:cameraPlaySounds="true|false"
app:cameraGrid="off|draw3x3|draw4x4|drawPhi"
app:cameraGridColor="@color/black"/>
```
#### cameraPlaySounds
Controls whether we should play platform-provided sounds during certain events
(shutter click, focus completed). Please note that:
- on API < 16, this flag is always set to `false`
- the Camera1 engine will always play shutter sounds regardless of this flag
Defaults to true.
```java
cameraView.setPlaySounds(true);
cameraView.setPlaySounds(false);
```
#### cameraGrid
Lets you draw grids over the camera preview. Supported values are `off`, `draw3x3` and `draw4x4`
for regular grids, and `drawPhi` for a grid based on the golden ratio constant, often used in photography.
Defaults to `OFF`.
```java
cameraView.setGrid(Grid.OFF);
cameraView.setGrid(Grid.DRAW_3X3);
cameraView.setGrid(Grid.DRAW_4X4);
cameraView.setGrid(Grid.DRAW_PHI);
```
#### cameraGridColor
Lets you choose the color for grid lines.
Defaults to a shade of grey.
```java
cameraView.setGridColor(Color.WHITE);
cameraView.setGridColor(Color.BLACK);
```
### Undocumented features
Some features and APIs were not documented in this document, including:
- `CameraUtils` utilities
- `CameraOptions` options
- `CameraView.setLocation` APIs
For informations, please take a look at the javadocs or the source code.
|Method|Description|
|------|-----------|
|`setZoom(float)`, `getZoom()`|Sets a zoom value, where 0 means camera zoomed out and 1 means zoomed in. No-op if zoom is not supported, or camera not started.|
|`setExposureCorrection(float)`, `getExposureCorrection()`|Sets exposure compensation EV value, in camera stops. No-op if this is not supported. Should be between the bounds returned by CameraOptions.|
|`setLocation(Location)`|Sets location data to be appended to picture/video metadata.|
|`setLocation(double, double)`|Sets latitude and longitude to be appended to picture/video metadata.|
|`getLocation()`|Retrieves location data previously applied with setLocation().|
|`startAutoFocus(float, float)`|Starts an autofocus process at the given coordinates, with respect to the view dimensions.|

@ -0,0 +1,53 @@
---
layout: page
title: "Preview Size"
subtitle: "Measuring behavior"
category: docs
order: 7
date: 2018-12-20 22:07:17
---
`CameraView` has a smart measuring behavior that will let you do what you want with a few flags.
Measuring is controlled simply by `layout_width` and `layout_height` attributes, with this meaning:
|Value|Meaning|
|-----|-------|
|`WRAP_CONTENT`|CameraView will choose this dimension, in order to show the whole preview without cropping. The aspect ratio will be respected.|
|`MATCH_PARENT`|CameraView will fill this dimension. Part of the content *might* be cropped.
|Fixed values (e.g. `500dp`)|Same as `MATCH_PARENT`|
This means that your visible preview can be of any size, not just the presets.
Whatever you do, the preview will never be distorted - it can only be cropped
if needed.
### Examples
##### Center Inside
By setting both dimensions to `WRAP_CONTENT`, you can emulate a **center inside** behavior.
The view will try to fill the available space, but respecting the stream aspect ratio.
```xml
<com.otaliastudios.cameraview.CameraView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
```
This means that the whole preview is visible, and the image output matches what was visible
during the preview.
##### Center Crop
By setting both dimensions to `MATCH_PARENT` or fixed values, you can emulate a **center crop**
behavior. The camera view will fill the rect. If your dimensions don't match the aspect ratio
of the internal preview surface, the surface will be cropped to fill the view.
```xml
<com.otaliastudios.cameraview.CameraView
android:layout_width="match_parent"
android:layout_height="match_parent" />
```
This means that part of the preview might be hidden, and the output might contain parts of the scene
that were not visible during the capture, **unless it is taken as a snapshot, since snapshots account for cropping**.

@ -0,0 +1,43 @@
---
layout: page
title: "Previews"
subtitle: "Camera preview implementations"
category: docs
order: 6
date: 2018-12-20 21:58:16
---
CameraView supports different types of previews, configurable either through the `cameraPreview`
XML attribute or programmatically with the `Preview` control class.
This defaults to the new `GL_SURFACE` and it is highly recommended that you do not change this
to use all the features available. However, experienced user might prefer a different solution.
### Options
|Preview|Backed by|Info|
|-------|---------|----|
|`Preview.SURFACE`|A `SurfaceView`|This might be better for battery, but will not work well (AFAIR) with dynamic layout changes and similar things. No support for video snapshots.|
|`Preview.TEXTURE`|A `TextureView`|Better. Requires hardware acceleration. No support for video snapshots.|
|`Preview.GL_SURFACE`|A `GLSurfaceView`|Supports video snapshots. Might support GL real time filters in the future.|
The GL surface, as an extra benefit, has a much more efficient way of capturing picture snapshots,
that avoids OOM errors, rotating the image on the fly, reading EXIF, and other horrible things belonging to v1.
These picture snapshots will also work while taking videos.
### XML Attributes
```xml
<com.otaliastudios.cameraview.CameraView
app:cameraPreview="surface|texture|glSurface"/>
```
### Related APIs
The preview method should only be called once and if the `CameraView` was never added to a window,
for example if you just created it programmatically. Otherwise, it has no effect.
|Method|Description|
|------|-----------|
|`setPreview(Preview)`|Sets the preview implementation.|

@ -0,0 +1,46 @@
---
layout: page
title: "Runtime Permissions"
subtitle: "Permissions and Manifest setup"
category: docs
order: 8
date: 2018-12-20 20:03:03
---
`CameraView` needs two permissions:
- `android.permission.CAMERA` : required for capturing pictures and videos
- `android.permission.RECORD_AUDIO` : required for capturing videos with `Audio.ON` (the default)
### Declaration
The library manifest file declares the `android.permission.CAMERA` permission, but not the audio one.
This means that:
- If you wish to record videos with `Audio.ON` (the default), you should also add
`android.permission.RECORD_AUDIO` to required permissions
```xml
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
```
- If you want your app to be installed only on devices that have a camera, you should add:
```xml
<uses-feature
android:name="android.hardware.camera"
android:required="true"/>
```
If you don't request this feature, you can use `CameraUtils.hasCameras()` to detect if current
device has cameras, and then start the camera view.
### Handling
On Marshmallow+, the user must explicitly approve our permissions. You can
- handle permissions yourself and then call `cameraView.start()` once they are acquired
- or call `cameraView.start()` anyway: `CameraView` will present a permission request to the user based on
whether they are needed or not with the current configuration.
In the second case, you should restart the camera if you have a successful response from `onRequestPermissionResults()`.

@ -1,4 +1,11 @@
# Migrating to v2.X.X
---
layout: page
title: "v1 Migration Guide"
subtitle: "Breaking Changes & concepts"
category: extra
date: 2018-12-20 19:01:55
order: 1
---
CameraView v2 introduces various breaking changes that will allow for more flexibility in the future,
removes useless features and makes method names consistent. Upgrading will require addressing these
@ -6,7 +13,9 @@ in your app, plus understanding new concepts.
Until the final v2 release, these things might change, but likely they will not.
## Removals
### Open, not start
The `start()` method has been renamed to `open()`, and the `stop()` method to `close()`. This was
done for consistency with the `onCameraOpened` callback.
### Jpeg Quality
Both `cameraJpegQuality` and `setJpegQuality()` have been removed. They were working only with specific
@ -22,29 +31,25 @@ This was an opaque option packaging various parameters. It has been removed.
You are expected to control the video quality by choosing the video size and setting video parameters
with new APIs (see below).
### Other removals
- `ExtraProperties`: This has been removed as it was useless.
## CameraUtils
### CameraUtils
- The `BitmapCallback` has been moved into a separate class.
- The `BitmapCallback` result is now `@Nullable`! This will happen if we encounter an `OutOfMemoryError` during decoding.
You should consider passing a maxWidth and maxHeight instead of loading the full image.
## CameraOptions
### CameraOptions
- Methods returning a `Set` now return a `Collection` instead.
- `isVideoSnapshotSupported()` was removed, as we do not rely on internal video snapshot feature anymore. See below.
- In addition to `getSupportedPictureSizes` and `getSupportedPictureAspectRatio`, we now have equivalent methods for video. See below.
## Session type
### Session type
The `SessionType` has been renamed to `Mode` which has a clearer meaning.
- `setSessionType()` is now `setMode()`
- `cameraSessionType` is now `cameraMode`
## Sizing
### Sizing
- `getPreviewSize()` was removed.
- `getPictureSize()`: now returns the real output picture size. This means that it accounts for rotation.
@ -59,9 +64,9 @@ It works exactly like the picture one, so please refer to the size selector docu
The engine will use the video size selector when mode is `VIDEO`, and the picture size selector when mode is `PICTURE`.
## Picture and videos
### Picture and videos
### Take, not capture
#### Take, not capture
- `capturePicture()` is now `takePicture()`
- `captureSnapshot()` is now `takePictureSnapshot()`
@ -70,7 +75,7 @@ The engine will use the video size selector when mode is `VIDEO`, and the pictur
The new `isTakingPicture()` method was added for symmetry with videos.
### Snapshots
#### Snapshots
This is the major improvement over v1. There are now 4 capture APIs, two for pictures and two for videos.
- Standard APIs: `takePicture()` and `takeVideo()`. These take a high quality picture or video, depending
@ -95,13 +100,13 @@ which makes it a powerful tool. The drawback is that it needs:
- API 18. If called before, it throws
- An OpenGL preview (see below). If not, it throws
### Video capturing
#### Video capturing
Some new APIs were introduced, which are respected by both standard videos and snapshot videos:
- `setAudioBitRate()` and `cameraAudioBitRate`: sets the audio bit rate in bit/s
- `setVideoBitRate()` and `cameraVideoBitRate`: sets the video bit rate in bit/s
## Camera Preview
### Camera Preview
The type of preview is now configurable with `cameraPreview` XML attribute and `Preview` control class.
This defaults to the new `GL_SURFACE` and it is highly recommended that you do not change this.
@ -115,7 +120,7 @@ The GL surface, as an extra benefit, has a much more efficient way of capturing
that avoids OOM errors, rotating the image on the fly, reading EXIF, and other horrible things belonging to v1.
These picture snapshots will also work while taking videos.
## CameraListener
### CameraListener
The listener interface brings two breaking signature changes:
- `onPictureTaken()` now returns a `PictureResult`. Use `result.getJpeg()` to access the jpeg stream.
@ -124,25 +129,18 @@ The listener interface brings two breaking signature changes:
- `onVideoTaken()` now returns a `VideoResult`. Use `result.getFile()` to access the video file.
The result class includes rich information about the video (or video snapshot) that was taken.
## Experimental mode
### Experimental mode
The v2 version introduces a `cameraExperimental` XML flag that you can use to enable experimental features.
Might be used in the future to speed up development.
## Other improvements
### Other improvements & changes
- Added `@Nullable` and `@NonNull` annotations pretty much everywhere. This might **break** your Kotlin build.
- Added `setGridColor()` and `cameraGridColor` to control the grid color
- Default `Facing` value is not `BACK` anymore but rather a value that guarantees that you have cameras (if possible).
If device has no `BACK` cameras, defaults to `FRONT`.
TODO: do we want getPreviewSize() / setPreviewSize() ? probably not the getter.
TODO: opencollective
TODO: document cameraGridColor
TODO: document setVideoBitRate
TODO: document setAudioBitRate
TODO: document takeVideoSnapshot
TODO: document that takePictureSnaphot works while taking videos, if GL_SURFACE
TODO: document the camera previews
TODO: new docs?
- Removed `ExtraProperties` as it was useless.
TODO: opencollective?
TODO: improve the focus marker drawing, move out of XML (accept a drawable?)
TODO: do we want getPreviewSize() / setPreviewSize() ? probably not the getter.

@ -0,0 +1,117 @@
body {
font-weight: 400;
}
pre, code, pre code {
border: none;
border-radius: 0;
background-color: #f9f9f9;
font-size: 0.85em;
}
.highlight {
background-color: #f9f9f9;
}
/* This changes inline code and hopefully nothing else */
.highlighter-rouge {
color: #336699
}
pre {
font-size: 1em;
}
code {
color: inherit;
}
#header {
border-bottom: 1px solid #eee;
margin-bottom: 20px;
}
#header a:hover {
text-decoration: none;
}
#footer {
margin: 20px 0;
font-size: 0.85em;
color: #999;
text-align: center;
}
#content > .page-header:first-child {
margin-top: 0;
}
#content > .page-header:first-child h2 {
margin-top: 0;
}
#navigation {
font-size: 0.9em;
}
#navigation li a {
padding-left: 10px;
padding-right: 10px;
}
#navigation .nav-header {
padding-top: 16px;
padding-bottom: 4px;
padding-left: 0;
padding-right: 0;
}
.nav-header {
font-size: 1em;
cursor: default;
text-transform: uppercase;
font-weight: bold;
}
body.rtl {
direction: rtl;
}
body.rtl #header .brand {
float: right;
margin-left: 5px;
}
body.rtl .row-fluid [class*="span"] {
float: right !important;
margin-left: 0;
margin-right: 2.564102564102564%;
}
body.rtl .row-fluid [class*="span"]:first-child {
margin-right: 0;
}
body.rtl ul, body.rtl ol {
margin: 0 25px 10px 0;
}
table {
margin-top: 25px;
margin-bottom: 25px;
border: 1px solid #e5e5e5;
border-collapse: collapse;
}
thead {
background-color: #f9f9f9;
}
td {
padding: .25rem .5rem;
border: 1px solid #e5e5e5;
}
th {
padding: 8px 12px;
border: 1px solid #e5e5e5;
}

@ -0,0 +1,61 @@
.highlight .hll { background-color: #ffffcc }
.highlight { background: #ffffff; }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */

@ -0,0 +1,36 @@
---
layout: default
title: "CameraView v2"
---
# CameraView
CameraView is a well documented, high-level library that makes capturing pictures and videos easy,
addressing most of the common issues and needs, and still leaving you with flexibility where needed.
- Fast & reliable
- Gestures support
- Frame processing support
- OpenGL powered preview
- Take high-quality content with `takePicture` and `takeVideo`
- Take super-fast snapshots with `takePictureSnapshot` and `takeVideoSnapshot`
- Smart sizing: create a `CameraView` of any size
- Control HDR, flash, zoom, white balance, exposure, location, grid drawing & more
- Lightweight: the only dep. is support `ExifInterface`
- Works down to API level 15
- Well tested
<p align="center">
<img src="static/icon.png" vspace="10" width="200" height="200">
</p>
### Get started
Get started with [install info](about/install.html), [quick setup](about/getting-started.html), or
read the in-depth [documentation]().
### Older versions
This website contains documentation and informations about version 2.X.X of the library.
For older versions, please take a look at the v1 branch in the [project page](https://github.com/natario1/CameraView).

@ -0,0 +1,4 @@
#!/usr/bin/env bash
#
# Run a local instance of the site.
bundle exec jekyll serve

@ -0,0 +1,107 @@
#!/usr/bin/env ruby
require 'date'
require 'optparse'
options = {
# Expects to be in the bin/ sub-directory by default
:path => File.dirname(File.dirname(__FILE__))
}
parser = OptionParser.new do |opt|
opt.banner = 'usage: jekyll-page TITLE CATEGORY [FILENAME] [OPTIONS]'
opt.separator ''
opt.separator 'Options'
opt.on('-e', '--edit', 'Edit the page') do |edit|
options[:edit] = true
end
opt.on('-l', '--link', 'Relink pages') do |link|
options[:link] = true
end
opt.on('-p PATH', '--path PATH', String, 'Path to project root') do |path|
options[:path] = path
end
opt.separator ''
end
parser.parse!
title = ARGV[0]
category = ARGV[1]
filename = ARGV[2]
# Resolve any relative links
BASE_DIR = File.expand_path(options[:path])
POSTS_DIR = "#{BASE_DIR}/_posts"
PAGES_DIR = "#{BASE_DIR}/_pages"
# Ensure the _posts directory exists (we are in the correct directory)
if not Dir.exists?(POSTS_DIR)
puts "#{POSTS_DIR} directory does not exists"
exit
end
# Create _pages directory if it doesn't exist
if not Dir.exists?(PAGES_DIR)
Dir.mkdir(PAGES_DIR)
end
if options[:link]
Dir.foreach(POSTS_DIR) do |name|
next if name[0] == '.'
nodate = name[/\d{4}-\d{2}-\d{2}-(?<rest>.*)/, 'rest']
if File.symlink?("#{PAGES_DIR}/#{nodate}")
File.delete("#{PAGES_DIR}/#{nodate}")
end
abspath = File.absolute_path("#{POSTS_DIR}/#{name}")
File.symlink(abspath, "#{PAGES_DIR}/#{nodate}")
end
end
if not title or not category
# This flag can be used by itself, exit silently if no arguments
# are defined
if not options[:link]
puts parser
end
exit
end
if not filename
filename = title.downcase.gsub(/[^a-z0-9\s]/, '').gsub(/\s+/, '-')
end
today=Date.today().strftime('%F')
now=DateTime.now().strftime('%F %T')
filepath = "#{POSTS_DIR}/#{today}-#{filename}.md"
symlink = "#{PAGES_DIR}/#{filename}.md"
if File.exists?(filepath)
puts "File #{filepath} already exists"
exit
end
content = <<END
---
layout: page
title: \"#{title}\"
category: #{category}
date: #{now}
---
END
File.open(filepath, 'w') do |file|
file.puts content
end
File.symlink("../_posts/#{today}-#{filename}.md", symlink)
if options[:edit]
if not ENV['EDITOR']
puts 'No $EDITOR variable set'
exit
end
puts ENV['EDITOR']
exec("#{ENV['EDITOR']} #{filename}")
end

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 122 KiB

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Loading…
Cancel
Save