| 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 |