Before Width: | Height: | Size: 1.5 MiB |
Before Width: | Height: | Size: 106 KiB |
@ -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,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()`. |
@ -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 |