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 |