Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:52:14 +01:00
parent cc5fdc9b3a
commit 1ab535ea8c
589 changed files with 130568 additions and 0 deletions

26
.gitignore vendored Normal file
View file

@ -0,0 +1,26 @@
_archive/
_devdocs/
_docs/*.jpg
_docs/ads.txt
_docs/app-ads.txt
_docs/gplay.html
_other/
_saved/
.gradle/
.idea/*
!.idea/inspectionProfiles
build/
app/release/
gfx/
testdata/
*.db
*.iml
*.apk
*.ap_
local.properties
uninstall.bat

View file

@ -0,0 +1,41 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="AndroidLintUnusedResources" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="BooleanMethodIsAlwaysInverted" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CharsetObjectCanBeUsed" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CommentedOutCode" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ConstantConditions" enabled="false" level="WARNING" enabled_by_default="false">
<option name="SUGGEST_NULLABLE_ANNOTATIONS" value="false" />
<option name="DONT_REPORT_TRUE_ASSERT_STATEMENTS" value="false" />
</inspection_tool>
<inspection_tool class="ConstantValue" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EmptyMethod" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EmptyStatementBody" enabled="false" level="WARNING" enabled_by_default="false">
<option name="m_reportEmptyBlocks" value="true" />
</inspection_tool>
<inspection_tool class="OptionalOfNullableMisuseMerged" />
<inspection_tool class="PointlessBooleanExpression" enabled="false" level="WARNING" enabled_by_default="false">
<option name="m_ignoreExpressionsContainingConstants" value="true" />
</inspection_tool>
<inspection_tool class="SameParameterValue" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TrivialIf" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnnecessaryLocalVariable" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_ignoreImmediatelyReturnedVariables" value="true" />
<option name="m_ignoreAnnotatedVariables" value="false" />
</inspection_tool>
<inspection_tool class="UnusedReturnValue" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="unused" enabled="true" level="WARNING" enabled_by_default="true" method="protected" parameter="protected" checkParameterExcludingHierarchy="false">
<option name="LOCAL_VARIABLE" value="true" />
<option name="FIELD" value="true" />
<option name="METHOD" value="true" />
<option name="CLASS" value="true" />
<option name="PARAMETER" value="true" />
<option name="REPORT_PARAMETER_FOR_PUBLIC_METHODS" value="false" />
<option name="ADD_MAINS_TO_ENTRIES" value="true" />
<option name="ADD_APPLET_TO_ENTRIES" value="true" />
<option name="ADD_SERVLET_TO_ENTRIES" value="true" />
<option name="ADD_NONJAVA_TO_ENTRIES" value="true" />
</inspection_tool>
</profile>
</component>

3
_docs/.htaccess Normal file
View file

@ -0,0 +1,3 @@
RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

97
_docs/credits.html Normal file
View file

@ -0,0 +1,97 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="en">
<head>
<title>Open Camera
</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<!-- viewport recommended by Google for mobile design, also required for Google's mobile-friendly test at https://www.google.co.uk/webmasters/tools/mobile-friendly/ -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="stylesheet.css" type="text/css">
<!-- EU cookie law, needed for analytics and adsense -->
<!-- Begin Cookie Consent plugin by Silktide - http://silktide.com/cookieconsent -->
<script type="text/javascript">
window.cookieconsent_options = {
"message":"This website uses cookies, including for Google Analytics and to display ads",
"dismiss":"Got it!",
"learnMore":"More info",
"link":"privacy_oc.html",
"theme":"dark-bottom"};
</script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/1.0.9/cookieconsent.min.js"></script>
<!-- End Cookie Consent plugin -->
<!-- Google Analytics -->
<!-- anonymize_ip is set: unclear if this is needed for EU GDPR, but just in case -->
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-38364448-5"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-38364448-5', { 'anonymize_ip': true });
</script>
</head>
<body>
<a href="index.html"><img align="left" src="ic_launcher.png" alt="Open Camera icon" title="Open Camera icon" width="72" height="72"></a>
<hr>
<div style="text-align: center;"><h2>Open Camera Credits</h2></div>
<hr>
<p>&lt; <small><a href="index.html">Main Page.</a></small></p>
<p>Open Camera is written by Mark Harman. Additional credits:</p>
<ul>
<li>App icon by Adam Lapinski.</li>
<li>Improvements/help for video frame rates (including high speed) by George Joseph.</li>
<li>Support for the video picture profiles implementation and custom Log profiles JTVideo, JTLog and JTLog2 design by JT Haapala.</li>
<li>Smart Housing Bluetooth LE support by Edouard Lafargue</li>
<li>Improved selfie stick button support by Lau Keat Hwa.</li>
<li>Option to storing yaw/pitch/roll in Exif user comment by Joshua.</li>
<li>Option for filenames to be based on UTC (Zulu) time by David Pletcher ( lpm_sourceforge AT cathedralcanyon DOT net , https://www.cathedralcanyon.net ).</li>
<li>Support for manual ISO for old camera API on Asus Zenphone 2 Z00A and Z008 by Fl&aacute;vio Keglevich ( fkeglevich AT gmail DOT com ).</li>
<li>Changing icons for pause/resume video by Johan Ejdemark ( johanejdemark AT hotmail DOT com).</li>
<li>Various improvements (including for lock screen behaviour) by Daniel Zhang.</li>
<li>Option to use milliseconds in filenames by Rob Emery ( opencam AT mintsoft DOT net).</li>
<li>Azerbaijani translation by Eldost ( l-dost AT mail DOT ru ).</li>
<li>Brazilian tranlation by Kaio Duarte.</li>
<li>Catalan translation by Cambrells.</li>
<li>Chinese Simplified translation by Michael Lu ( yeskky AT gmail DOT com ), tumuyan ( tumuyan AT gmail DOT com ) and Tommy He.</li>
<li>Chinese Traditional translation by You-Cheng Hsieh ( yochenhsieh AT gmail DOT com ) and Hsiu-Ming Chang.</li>
<li>Belarusian translation by Zmicer Turok.</li>
<li>Czech translation by Jaroslav Svoboda ( multi DOT flexi AT gmail DOT com , http://jaroslavsvoboda.eu ).</li>
<li>French translation by Olivier Seiler ( oseiler AT nebuka DOT net ) and Eric Lassauge ( lassauge AT users DOT sf DOT net ).</li>
<li>German translation by Ronny Steiner, Sebastian Ahlborn, Carsten Schlote, Wilhelm Stein, Jochen Wiesel.</li>
<li>Greek translation by Wasilis Mandratzis-Walz.</li>
<li>Hungarian translation by Báthory Péter.</li>
<li>Italian tranlation by Valerio Bozzolan, Stefano Gualmo ( s DOT gualmo AT gmail DOT com ), Renato Giliberti.</li>
<li>Japanese translation by Mitsuse and Yanagimoto Yoshiaki.</li>
<li>Korean translation by Halcyonest.</li>
<li>Norwegian Bokmål translation by Imre Kristoffer Eilertsen ( imreeil42 AT gmail DOT com ).</li>
<li>Polish translation by Jacek Buczyński and Grzegorz Koryga.</li>
<!-- Portugese translation by johnny, says no need to credit -->
<li>Russian translation by maksnogin ( maksnogin AT gmail DOT com ), Grigorii Chirkov, Dmitry Vahnin aka JSBmanD, Aleksey Khlybov, Ilya Pogrebenko<!--and Vitamin - Vitamin said no need to credit -->.</li>
<li>Slovenian translation by Peter Klofutar.</li>
<li>Spanish translation by Mario Sanoguera ( sanogueralorenzo AT gmail DOT com , https://play.google.com/store/apps/developer?id=Mario+Sanoguera ; Sebastian05067, https://forum.xda-developers.com/member.php?u=6302705 ) and Gonzalo Prieto Vega.</li>
<li>Turkish translation by Serdar Erkoc ( serdarerkoc2004 AT yahoo DOT com ).</li>
<li>Ukranian translation by Olexandr ( https://sourceforge.net/u/olexn13/ ).</li>
<li>Vietnamese translation by Khánh Trần ( crhanh AT gmail DOT com ).</li>
<li>Earlier versions (pre-Material Design) have an icon/logo by Cosmin Saveanu ( http://aboutfoto.wordpress.com/ ).</li>
</ul>
<p>Also see <a href="index.html#licence">licence</a> for third party files.</p>
<hr>
<p><a href="privacy_oc.html">Open Camera Privacy Policy.</a></p>
<p>This website uses icons from third party sources, see <a href="index.html#licence">licences.</a></p>
<p><a href="https://sourceforge.net/projects/opencamera/">Open Camera on Sourceforge.</a></p>
<hr>
</body>
</html>

221
_docs/devices.html Normal file
View file

@ -0,0 +1,221 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="en">
<head>
<title>Open Camera Device Compatibility
</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<!-- viewport recommended by Google for mobile design, also required for Google's mobile-friendly test at https://www.google.co.uk/webmasters/tools/mobile-friendly/ -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="stylesheet.css" type="text/css">
<!-- EU cookie law, needed for analytics and adsense -->
<!-- Begin Cookie Consent plugin by Silktide - http://silktide.com/cookieconsent -->
<script type="text/javascript">
window.cookieconsent_options = {
"message":"This website uses cookies, including for Google Analytics and to display ads",
"dismiss":"Got it!",
"learnMore":"More info",
"link":"privacy_oc.html",
"theme":"dark-bottom"};
</script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/1.0.9/cookieconsent.min.js"></script>
<!-- End Cookie Consent plugin -->
<!-- Google Analytics -->
<!-- anonymize_ip is set: unclear if this is needed for EU GDPR, but just in case -->
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-38364448-5"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-38364448-5', { 'anonymize_ip': true });
</script>
</head>
<body>
<a href="index.html"><img align="left" src="ic_launcher.png" alt="Open Camera icon" title="Open Camera icon" width="72" height="72"></a>
<hr>
<div style="text-align: center;"><h2>Open Camera Device Compatibility</h2></div>
<hr>
<p>&lt; <small><a href="index.html">Main Page.</a></small></p>
<p>This page provides some guidance on possible issues of Open Camera on various Android&trade; devices. Please note the following:</p>
<ul>
<li>Sometimes behaviour can be affected by things like operating system version or can differ between variants of the same
model. Behaviour may also change over time depending on different versions of Open Camera, and different
versions of device operating system. So something listed here isn't a guarantee of that behaviour on a
particular model.</li>
<li>This information is provided "as is" with no warranties - if you need to be certain of how Open Camera works on a
particular device, ultimately you'll have to test it yourself.</li>
</ul>
<h2>General notes</h2>
<p>If you're just interested in taking photos with non-advanced features (without using "Camera2 API"), then most things
should work on most devices, as far as I can tell. I do occasionally get bug reports of things which seem to be device
specific, but not enough to draw conclusions about things not working on particular devices. The most commonly reported
issues seems to be:</p>
<ul>
<li>Video is one of the most difficult things working across different Android devices - on some devices recording comes out
corrupted. In some cases this is only on some resolutions, or it may be
all.</li>
<li>If you're wanting to save to external SD cards, you'll need to follow the advice at
<a href="help.html#faq">the FAQ</a> . Note that in some cases, it seems that SD cards
can't be selected even when using the Storage Access Framework option - this is a device issue, and
something out of my control. If you're wanting to have lots
of storage for taking photos or videos, it's probably better to make sure you get a device with plenty of
internal storage (internal storage is faster anyway, so means faster taking of photos, and more reliable high
resolution video recording).</li>
</ul>
<p>If you're interested in enabling Camera2 support for advanced features (manual focus, manual exposure, expo bracketing, HDR),
be aware that some devices have poor support for Camera2 (even if they support the API, the
implementations seem to have problems). Thankfully things seem to be improving on that front with newer
devices. Please read the details below on what I know about different devices.</p>
<p>Also note that just because a manufacturer advertises a particular camera feature, it doesn't mean that Open Camera can use
it. Unfortunately some manufacturers limit some features to the "stock" camera application, and don't make it available
through to third party cameras. This tends to be more advanced features - 4K video, high photo resolutions, high frame
rate video, RAW.</p>
<h2>Device specific notes</h2>
<h3>Google Nexuses/Pixels</h3>
<p>In general, Google Nexuses and Pixels have worked well for Open Camera.</p>
<p>Camera2 API on the Nexus 6 works well (there are some minor issues, e.g., manual exposure doesn't work well when recording
video). It's hard to be sure about other Nexuses though.</p>
<p>Similarly Camera2 API works well on the Pixel 6 Pro. Open Camera also supports Google's HDR+ mode on the Pixels with Pixel Visual Core
(including the Pixel 6 Pro). As of Open Camera 1.50, Night Sight on the Pixel 6 Pro is available via the photo mode X-Night.
As of Open Camera 1.50, all of the Pixel 6 Pro's cameras are available to use by zooming in or out.
As of Open Camera 1.54, you can also select individual cameras directly (tested on the Pixel 6 Pro).</p>
<p>Color effects don't work on the Nexus 7.</p>
<h3>Huawei</h3>
<p>I've had reports of expo bracketing and HDR in Camera2 mode not working properly on some Huawei devices
(Huawei P8 lite 2017, P9). See <a href="https://sourceforge.net/p/opencamera/tickets/368/">here</a> for details.</p>
<p>I've also had reports of RAW/DNG images being saved with red/blue swapped. See
<a href="https://sourceforge.net/p/opencamera/tickets/422/">here</a> for details.</p>
<h3>Nokia</h3>
<p>I've tested Open Camera with the Nokia 8. Everything seems to work as far as I can tell, including Camera2 API with full
manual controls, RAW and 120fps video.</p>
<h3>OnePlus</h3>
<p>I've tested Open Camera with the OnePlus Pad. Everything seems to work as far as I can tell, including Camera2 API with full
manual controls, and RAW.</p>
<p>The OnePlus 3T had problems related to Camera2 API and manual exposure:</p>
<ul>
<li>Manual exposure sometimes fails (the preview corrupts or the device may freeze for a few moments), this also
includes HDR.</li>
<li>Low light scenes show the wrong ISO and shutter speed, both on-screen and in the saved Exif info (although the
photos themselves still come out okay); also a knock on effect of this bug is that HDR and expo bracketing
don't work in low light. Manual ISO also doesn't work above 799 (the images still come out with ISO at 799).</li>
</ul>
<p>See <a href="https://community.oneplus.com/thread/506100">this thread</a> for more
details.</p>
<p>The OnePlus 5 seems to have the same problems with Camera2 API as the OnePlus 3T (see above). Issues with RAW images have been
reported for third party camera applications - see
<a href="https://sourceforge.net/p/opencamera/discussion/general/thread/0a9b5bef/">here</a>,
<a href="https://forums.oneplus.net/threads/pink-tint-on-dng.562049/">here</a> and
<a href="https://forums.oneplus.net/threads/raw-camera-purple-halo.565588/">here</a>. As of August 2017,
<a href="https://forums.oneplus.net/threads/pink-cast-on-dng-files-when-shooting-with-other-apps.580035/">this
seems to have been fixed</a>.
But as of December 2017, there seem to be
<a href="https://forums.oneplus.net/threads/raw-pictures-with-third-party-camera-app-are-greenish.727594/">additional
problems with RAW on Android 8.</a>
</p>
<p>The OnePlus 3T and 5 are rather old devices - as I say, the more recent OnePlus Pad does not seem to have these problems.</p>
<h3>Samsung</h3>
<p>I have tested Open Camera on a Samsung Galaxy S24+ (Exynos SM-S926B) and Galaxy S10e (Exynos SM-G970F). Mostly things work, including with Camera2
API. Known issues are:</p>
<ul>
<li>Night mode is available via X-Night.</li>
<li>Slow motion and high speed frame rate video doesn't work on the Galaxy S10e (see below for more details), but fine on the Galaxy S24+.</li>
<li>The "Image quality" setting has no effect for JPEGs on the Galaxy S10e (unless post-processing options such as auto-level or
photo stamp are applied). This has also been reported for other Samsung devices; I also have the same
issue with other third party camera applications on my S10e. Howevever the image quality setting does work on the Galaxy S24+. See
<a href="https://sourceforge.net/p/opencamera/discussion/general/thread/1abb56eb/">this thread</a>
for details.</li>
<li>The photo shutter sound always plays at maximum volume on Camera2 API, this seems to be
<a href="https://issuetracker.google.com/issues/182811267">a Samsung issue</a> - a workaround is to turn off the shutter sound
via Settings/More camera controls/"Shutter sound".</li>
</ul>
<p>All the Galaxy S10e and Galaxy S24+ rear cameras are available (including telephoto for the S24+), you can switch by zooming in or out.
As of Open Camera 1.54, you can also select individual cameras directly.
Also the two modes for the front camera
("cropped" and "wide") are available to Open Camera.</p>
<p>At least some Samsung Galaxy devices support the camera extension modes (X-Night, X-Bokeh, X-Bty) (including the Galaxy S10e and Galaxy S24+;
in general this is more likely to be available for the flagship S devices running Android 12+).</p>
<p>The Samsung Galaxy S24+ at least supports Ultra HDR JPEG image format.</p>
<p>More generally I have occasionally tested on various Samsung devices using their remote test labs - although useful, this is limited
compared to owning a real device (especially when the test labs are dark!)</p>
<p>I've had reports of the audio being out of sync with video on the Galaxy S7 and S8 when in Camera2 API mode.</p>
<p>Older Samsung devices (e.g., Galaxy S5) didn't have 4K video recording available for third party camera applications. In some
cases it could be enabled with the "Force 4K" option, but this only works on some devices (in some cases whether it works
depends on which variant of a device). As of the Galaxy S10e at least, 4K video is available.</p>
<p>Some Samsung devices do not have any "scene modes" (in some cases this can depend on even which variant of a device is
used).</p>
<!--<p>I've had reports of picture quality being poor on Camera2 API the Galaxy S7 and S7 Edge, though improvements were made in
v1.42.1 onwards of Open Camera. See
<a href="https://sourceforge.net/p/opencamera/discussion/general/thread/48bd836b/">here</a> for details.</p>
-->
<p>At least some Samsung devices don't seem to have support for high speed frame rates for video for third party camera applications.
Filmic have documented issues for the
<a href="https://filmicpro.helpscoutdocs.com/article/41-samsung-s9-and-s9-filmic-pro-v6-compatibility-guide">S9 and S9+</a>, and
<a href="https://filmicpro.helpscoutdocs.com/article/42-samsung-note-9-filmic-pro-v6-compatibility-guide">Note 9</a> (these articles
are for Filmic Pro, but the issues faced likely affect all third party camera applications, including Open Camera).</p>
<p>On a related note, the Galaxy Note 4 and 5 were used with Open Camera to film
<a href="https://sourceforge.net/p/opencamera/blog/2015/10/cai-lan-gong---worlds-first-feature-film-shot-with-a-smartphone-at-4k-resolution/">the
world's first 4K feature film shot on a phone</a>.</p>
<h3>Sony Xperia</h3>
<p>To enable the 23MP photo resolution, you need to set Settings/"Camera API" to "Camera2 API". Someone has reported to me this works on the
Sony XA1 (G3123) (Android 8), I'm not sure about older devices.</p>
<!--<p>23MP photo resolution isn't available, the maximum seems to be 8MP. This seems to be due to
<a href="http://talk.sonymobile.com/t5/Xperia-Z5-Z5-Compact-Z5-Premium/Full-camera-res-in-third-party-apps/td-p/1045437/highlight/true/page/4">Sony
not making this available for third party camera applications</a>.</p>-->
<p>Sony devices <a href="https://talk.sonymobile.com/t5/Software-Updates/Camera2-API/td-p/974742/page/122">don't seem to support
RAW/DNG</a> at the time of writing.</p>
<p>I've had a report that manual white balance temperature doesn't work (Sony Xperia X Compact).</p>
<hr>
<p><a href="privacy_oc.html">Open Camera Privacy Policy.</a></p>
<p>This website uses icons from third party sources, see <a href="index.html#licence">licences.</a></p>
<p><a href="https://sourceforge.net/projects/opencamera/">Open Camera on Sourceforge.</a></p>
<hr>
</body>
</html>

BIN
_docs/exposure_locked.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

BIN
_docs/exposure_unlocked.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
_docs/focus_mode_auto.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
_docs/focus_mode_edof.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
_docs/focus_mode_fixed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 933 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
_docs/focus_mode_locked.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
_docs/focus_mode_manual.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

1546
_docs/help.html Normal file

File diff suppressed because it is too large Load diff

1946
_docs/history.html Normal file

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 969 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
_docs/ic_launcher.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
_docs/ic_mic_white_48dp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 678 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

326
_docs/index.html Normal file
View file

@ -0,0 +1,326 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="en">
<head>
<title>Open Camera
</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<!-- viewport recommended by Google for mobile design, also required for Google's mobile-friendly test at https://www.google.co.uk/webmasters/tools/mobile-friendly/ -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="stylesheet.css" type="text/css">
<!--<script>
var old_domain = "//opencamera.sourceforge.net";
if( window.location.href.indexOf(old_domain) >= 0 ) {
window.location.href = window.location.href.replace(old_domain, "//opencamera.org.uk");
}
</script>-->
<script>
var old_domain = "//opencamera.sourceforge.io";
if( window.location.href.indexOf(old_domain) >= 0 ) {
window.location.href = window.location.href.replace(old_domain, "//opencamera.org.uk");
}
</script>
<!-- EU cookie law, needed for analytics and adsense -->
<!-- Begin Cookie Consent plugin by Silktide - http://silktide.com/cookieconsent -->
<script type="text/javascript">
window.cookieconsent_options = {
"message":"This website uses cookies, including for Google Analytics and to display ads",
"dismiss":"Got it!",
"learnMore":"More info",
"link":"privacy_oc.html",
"theme":"dark-bottom"};
</script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/1.0.9/cookieconsent.min.js"></script>
<!-- End Cookie Consent plugin -->
<!-- Google Analytics -->
<!-- anonymize_ip is set: unclear if this is needed for EU GDPR, but just in case -->
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-38364448-5"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-38364448-5', { 'anonymize_ip': true });
</script>
<!-- Google adsense auto ads -->
<!-- If removing/changing this code, check the "Revoke or change cookie consent" link still works -->
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<script>
(adsbygoogle = window.adsbygoogle || []).push({
google_ad_client: "ca-pub-1960368062214160",
enable_page_level_ads: true
});
</script>
</head>
<body>
<img align="left" src="ic_launcher.png" alt="Open Camera icon" title="Open Camera icon" width="72" height="72">
<hr>
<div style="text-align: center;"><h2>Open Camera</h2></div>
<hr>
<!--<p><a href="screenshot_galaxy_nexus_3.jpg">
<img align="right" src="screenshot_galaxy_nexus_3.jpg" title="Screenshot" width="360" height="640"></a></p>-->
<!--<p><a href="Screenshot_2014-07-02-14-16-35.jpg">
<img align="right" src="Screenshot_2014-07-02-14-16-35.jpg" title="Screenshot" width="640" height="360"></a></p>-->
<!--<p><a href="Screenshot_2014-09-22-14-34-48.jpg">
<img align="right" src="Screenshot_2014-09-22-14-34-48_thumbversion.jpg" title="Screenshot" style="max-width:33%">
</a></p>-->
<div style="display:block;float:right;margin: 5px 0px 5px 0px;width:40%">
<!-- div to contain the screenshot and the ads. Important to use width and not max-width, otherwise
responsive ads won't show (at least on my Nexus 6)!
If any code is changed here, make sure layout is still good and ads show on laptop, Nexus 6 and Galaxy Nexus.
-->
<!-- Remember that ad must not be more than 30% of total page content height (i.e., total page, not just what's
on screen in the viewport. -->
<!--<a href="Screenshot_2014-09-22-14-34-48.jpg">
<img src="Screenshot_2014-09-22-14-34-48_thumbversion.jpg" alt="Screenshot" title="Screenshot" style="max-width:100%">
</a>-->
<!-- we set max width of 100%, as this is in terms of the outer div (which has width 33%), and we don't want this being larger than the div -->
<!-- "Aligning images with ads" not allowed - unclear how much separation/distinction is required between an image and ad, for now comment out the image -->
<!-- Make sure that this ad doesn't doesn't occupy full page, "Site layout that pushes content below the fold" -->
<div style="min-width:125px;margin-top:5px">
<!-- need min-width of 125px for responsive ads; optionally set a max-width if necessary so that ads aren't huge on non-mobile
browsers -->
<!-- If removing/changing this code, check the "Revoke or change cookie consent" link still works -->
<!-- start adsense code -->
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- ad_top_responsive -->
<ins class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-1960368062214160"
data-ad-slot="3383301936"
data-ad-format="auto"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- end adsense code -->
</div>
</div>
<p><small><a href="help.html">Jump to Instructions.</a></small></p>
<p>Open Camera is an Open Source Camera app for Android&trade; phones and tablets. Features:</p>
<ul style="font-size:small">
<li>Option to <a href="help.html#auto_stabilise">auto-level</a> so your pictures are perfectly level no matter what.</li>
<li>Expose your camera's functionality: support for scene modes, color effects, white balance, ISO, exposure compensation/lock, selfie with "screen flash", HD video and more.</li>
<li>Handy remote controls: timer (with optional voice countdown), auto-repeat mode (with configurable delay).</li>
<li>Option to take photo remotely by making a noise<!--, or by voice command "cheese"-->.</li>
<li>Configurable volume keys and user interface.</li>
<li>Upside-down preview option for use with attachable lenses.</li>
<li>Overlay a choice of grids and crop guides.</li>
<li>Optional GPS location tagging (geotagging) of photos and videos; for photos this includes compass direction (GPSImgDirection, GPSImgDirectionRef).</li>
<li>Apply date and timestamp, location coordinates, and custom text to photos; store date/time and location as video subtitles (.SRT).</li>
<li>Option to remove device exif metadata from photos.</li>
<li>Panorama, including for front camera.</li>
<li>Support for <a href="help.html#hdr">HDR</a> (with auto-alignment and ghost removal) and Exposure Bracketing.</li>
<li>Support for Camera2 API: manual controls (with optional focus assist); burst mode; RAW (DNG) files; camera vendor extensions; slow motion video; log profile video.</li>
<li>Noise reduction (including low light night mode) and Dynamic range optimisation modes.</li>
<li>Options for on-screen histogram, zebra stripes, focus peaking.</li>
<li>Focus bracketing mode.</li>
<li>Completely free, and no third party ads in the app (I only run third party ads on the website). Open Source.</li>
</ul>
<p>(Some features may not be available on all devices, as they may depend on hardware or camera features, the Android version, etc.)</p>
<!-- we don't use the Google Play badge because it looks awful - comes out way too big?! -->
<p><a href="https://play.google.com/store/apps/details?id=net.sourceforge.opencamera">Get it on Google Play.</a></p>
<hr>
<p>
<a href="https://sourceforge.net/p/opencamera/blog/">Open Camera Blog</a> ~
<a href="https://sourceforge.net/p/opencamera/discussion/">Discussion Forums</a> ~
<a href="https://sourceforge.net/p/opencamera/code/">Code Repository (Git)</a>
</p>
<!-- if changing the a name="contents", remember to update links to this, specifically the link from privacy policy -->
<p><a name="contents"><b>Contents:</b></a></p>
<ul>
<li><a href="#requirements">Requirements</a></li>
<li><a href="help.html">Instructions</a></li>
<li><a href="#credits">Credits</a></li>
<li><a href="privacy_oc.html">Privacy policy</a></li>
<li><a href="javascript:googlefc.callbackQueue.push(googlefc.showRevocationMessage)">Revoke or change cookie consent for personal data (for EU/GDPR countries)</a></li>
<!-- Needed for adsense policies! If we change the wording of the above link, remember to update the privacy policy that refers to this. -->
<li><a href="#licence">Licence and Terms of Service</a></li>
<li><a href="history.html">History</a></li>
</ul>
<!-- don't have ads too close to download links! -->
<hr>
<!-- Remember that ad must not be more than 30% of total page content height (i.e., total page, not just what's
on screen in the viewport. -->
<!-- If removing/changing this code, check the "Revoke or change cookie consent" link still works -->
<!-- start adsense code -->
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<!-- contents_ad -->
<ins class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-1960368062214160"
data-ad-slot="6626649930"
data-ad-format="auto"></ins>
<script>
(adsbygoogle = window.adsbygoogle || []).push({});
</script>
<!-- end adsense code -->
<hr>
<h2><a name="requirements"><b>Requirements</b></a></h2>
<p>Open Camera requires Android 5.0 or better (versions 1.53.1 or earlier also supported 4.0.3 or better).
Some features may only be available
on some devices (it may depend on Android version, or require specific support from the camera/device).</p>
<p>Note that it's not possible for me to test Open Camera on every Android device out there, let alone in combination with different
Android versions (or especially alternative ROMs). Please test before using Open Camera to photo/video
your wedding etc :)</p>
<p>See <a href="devices.html">here</a> for some details on issues with various devices.</p>
<h2><a href="help.html">Instructions</a></h2>
<h2><a name="credits"><b>Credits</b></a></h2>
<p>Open Camera is written by Mark Harman with additional contributors, see <a href="credits.html">credits</a> for details.</p>
<h2><a name="privacy"><b>Privacy policy</b></a></h2>
<!-- keep this #privacy link, due to old sites or versions of Open Camera that used to link to this -->
<p>See <a href="privacy_oc.html">my privacy policy</a> for details.</p>
<h2><a name="licence"><b>Licence and Terms of Service</b></a></h2>
<!-- note this is linked to inside of Open Camera, so be careful of changing the link -->
<!-- also linked from other pages on this site, and Google Play store listing -->
<!-- we also say Terms of Service due to the Google unwanted software policy -->
<p>Open Camera is released under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPL v3 or later</a>. The source code is
available from
<!-- use &#8203; to limit widths on continuous text to 30 characters, to avoid messing up view on mobile devices -->
<a href="https://sourceforge.net/projects/opencamera/files/">https://sourceforge.net/&#8203;projects/opencamera/files/</a> .
<!--The file exposure.png is also dual licensed under GPL v3 or later, and <a href="http://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a> or later (attribute to Mark Harman and please link to http://opencamera.sourceforge.net/ ).-->
Also see <i>"Can I use the Open Camera source code in my app?"</i> under the <a href="help.html#usesource">FAQ</a>.
</p>
<p>Open Camera uses the AndroidX/Jetpack libraries, under <a href="https://www.apache.org/licenses/LICENSE-2.0.html">Apache license version 2.0</a>.</p>
<p>The following files are used in Open Camera:</p>
<ul style="font-size:small">
<!-- use &#8203; to limit widths on continuous text to 30 characters, to avoid messing up view on mobile devices -->
<!-- also use small to further reduce chance of problem on mobile devices, and reduce amount of space taken up -->
<li>Open Camera uses icons from Google's Material Design icons - from
<a href="https://developer.android.com/design/downloads/index.html">https://developer.android.com/&#8203;design/downloads/index.html</a> /
<a href="https://design.google.com/icons/">https://design.google.com/icons/</a> /
<a href="https://github.com/google/material-design-icons/">https://github.com/google/material-design-icons/</a> /
<a href="https://google.github.io/material-design-icons/">https://google.github.io/material-design-icons/</a> /
<a href="https://fonts.google.com/icons">https://fonts.google.com/icons</a> ,
by Google, under <a href="https://www.apache.org/licenses/LICENSE-2.0.html">Apache license version 2.0</a>
(licence text also available <a href="google_material_design_icons_LICENSE-2.0.txt">here</a>.)
(some cases include modifications, no need to credit me).
In particular:
baseline_add_a_photo_white_48.png,
baseline_bedtime_white_48.png
baseline_bluetooth_white_48.png, baseline_check_white_48.png, baseline_close_white_48.png,
baseline_delete_white_48.png,
baseline_face_retouching_natural_white_48.png,
baseline_filter_vintage_white_48.png,
baseline_folder_open_white_48.png,
baseline_highlight_white_48.png,
baseline_panorama_horizontal_white_48.png,
baseline_photo_library_white_48.png,
baseline_portrait_white_48.png,
baseline_remove_red_eye_white_48.png,
baseline_rotate_left_white_48.png, baseline_rotate_right_white_48.png,
baseline_shutter_speed_white_48.png,
baseline_switch_camera_white_48.png, <!-- baseline_switch_camera_white_48.png no longer used in v1.53 onwards -->
baseline_text_fields_red_48.png (modified from baseline_text_fields_white_48), baseline_text_fields_white_48.png,
exposure_locked.png (modified from baseline_lock_white_48 and ic_exposure_white_48dp),
exposure_unlocked.png (modified from baseline_lock_open_white_48 and ic_exposure_white_48dp),
flash_auto.png (from baseline_flash_auto_white_48), flash_off.png (from baseline_flash_off_white_48),
flash_on.png (from ic_action_flash_on),
focus_mode_continuous_picture.png and focus_mode_continuous_video.png (from baseline_loop_white_48),
focus_mode_infinity (from baseline_loop_white_48),
focus_mode_locked.png (modified from baseline_lock_white_48),
ic_burst_mode_white_48dp.png, ic_colorize_white_48dp.png,
ic_exposure_red_48dp.png, ic_exposure_white_48dp.png, ic_face_red_48dp.png (modified from ic_face_white_48dp), ic_face_white_48dp.png,
ic_fast_forward_white_48dp.png,
ic_gps_fixed_red_48dp.png (modified from ic_gps_fixed_white_48dp), ic_gps_fixed_white_48dp.png,
ic_gps_off_white_48dp.png, ic_hdr_on_white_48dp.png, ic_help_outline_white_48dp.png, ic_info_outline_white_48dp.png,
ic_launcher_take_photo.png (modified from ic_photo_camera_white_48dp),
ic_mic_off_white_48dp.png, ic_mic_red_48dp.png (modified from ic_mic_white_48dp), ic_mic_white_48dp.png,
ic_more_horiz_white_48dp.png,
ic_pause_circle_outline_white_48dp.png, ic_photo_camera_white_48dp.png, ic_photo_size_select_large_white_48dp.png,
ic_play_circle_outline_white_48dp.png,
ic_power_settings_new_white_48dp.png, ic_save_white_48dp.png,
ic_slow_motion_video_white_48dp.png,
ic_text_format_red_48dp.png (modified from ic_text_format_white_48dp), ic_text_format_white_48dp.png,
ic_timelapse_white_48dp.png, ic_timer_white_48dp.png,
ic_touch_app_white_48dp.png, ic_videocam_white_48dp.png,
ic_stat_notify_take_photo.png (modified from ic_photo_camera_white_48dp),
key_visualizer_red.xml (modified from key_visualizer), key_visualizer.xml,
popup*.png (modified from ic_more_vert_white, baseline_highlight_white, baseline_remove_red_eye_white, baseline_flash_auto_white,
baseline_flash_off_white, ic_action_flash_on),
settings.png (from ic_action_settings), share.png (from ic_action_share),
switch_camera.png (modified from baseline_loop_white_48),
take_photo.png (modified from ic_photo_camera_white_48dp), take_photo_pref.png (modified from ic_photo_camera_white_48dp),
take_photo_pressed.png (modified from ic_photo_camera_white_48dp), take_photo_when_video_recording.png (modified from ic_photo_camera_white_48dp),
take_video.png (modified from baseline_videocam_white_48), take_video_pref.png (modified from baseline_videocam_white_48),
take_video_pressed.png (modified from baseline_videocam_white_48), take_video_recording.png (modified from baseline_videocam_white_48),
white_balance_locked.png (modified from baseline_lock_white_48),
white_balance_unlocked.png (modified from baseline_lock_open_white_48).
<br>Modified versions of some of these icons are also used on this website.
<br>Open Camera's app icon/logo also makes use of ic_photo_camera by Google (also Apache license version 2.0).</li>
</ul>
<p>Note that old versions of Open Camera also used the following:</p>
<ul style="font-size:small">
<!-- use &#8203; to limit widths on continuous text to 30 characters, to avoid messing up view on mobile devices -->
<!-- also use small to further reduce chance of problem on mobile devices, and reduce amount of space taken up -->
<!-- useful to attribute for old versions in case people still using older versions, not to mention things like screenshots
of old versions; but no need to mention CC0 media which doesn't require attribution -->
<!--<li>Icon is from <a href="http://commons.wikimedia.org/wiki/File:Camera2_mgx.svg">http://commons.wikimedia.org/&#8203;wiki/File:Camera2_mgx.svg</a> , by AlphaZeta, under CC0 (public domain).</li>-->
<!--<li>Icon/logo is from <a href="http://aboutfoto.wordpress.com/2014/09/20/a-logo-for-open-camera/">http://aboutfoto.wordpress.com/&#8203;2014/09/20/&#8203;a-logo-for-open-camera/</a> , by Cosmin Saveanu, under CC0 (public domain).</li>-->
<!--<li>earth.png, earth_off.png from <a href="http://commons.wikimedia.org/wiki/File:NASA_Earth_America_2010.jpg">http://commons.wikimedia.org/&#8203;wiki/&#8203;File:NASA_Earth_America_2010.jpg</a> , public domain.</li>-->
<!--<li>beep.ogg, beep_hi.ogg - from <a href="http://opengameart.org/content/interface-beeps">http://opengameart.org/&#8203;content/interface-beeps</a> , by bart, under <a href="http://creativecommons.org/publicdomain/zero/1.0/">CC0 (public domain)</a>.</li>-->
<li>exposure_locked.png, focus_mode_locked.png, white_balance_locked.png modified from https://www.iconfinder.com/&#8203;icons/128411/&#8203;antivirus_close_forbid_hide_&#8203;lock_locked_password_privacy_&#8203;private_protection_restriction&#8203;_safe_secure_security_icon#&#8203;size=64 , by Aha-Soft, under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a> (no need to credit me).</li>
<li>exposure_unlocked.png, white_balance_unlocked.png modified from https://www.iconfinder.com/&#8203;icons/128416/&#8203;free_freedom_hack_lock_open_&#8203;padlock_password_secure_&#8203;security_unlock_unlocked_icon#&#8203;size=64 , by Aha-Soft, under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a> (no need to credit me).</li>
<li>flash_off.png, flash_auto.png, flash_on.png from https://www.iconfinder.com/&#8203;icons/62201/flash_icon#size=64, by The Working Group, under <a href="http://creativecommons.org/licenses/by-sa/3.0/">CC BY-SA 3.0</a>.</li>
<li>flash_red_eye.png, popup_flash_red_eye.png from https://www.iconfinder.com/&#8203;icons/103177/&#8203;eye_see_view_watch_icon#&#8203;size=128 , by Designmodo / Andrian Valeanu, under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a> (no need to credit me).</li>
<li>flash_torch.png, popup_torch.png from https://www.iconfinder.com/&#8203;icons/51924/&#8203;bulb_light_icon#size=128 , by IconFinder - http://www.iconfinder.net , by <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>.</li>
<li>focus_mode_macro.png from https://www.iconfinder.com/&#8203;icons/81105/&#8203;macro_mb_icon#size=128 , by Yankoa - http://yankoa.deviantart.com/ , under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>.</li>
<li>gallery.png from https://www.iconfinder.com/&#8203;icons/6915/&#8203;book_gallery_images_photos_&#8203;pictures_icon#size=128, by Alessandro Rei, under <a href="http://www.gnu.org/copyleft/gpl.html">GPL v3</a>.</li>
<li>settings.png from https://www.iconfinder.com/&#8203;icons/115801/&#8203;settings_icon#size=128, by Designmodo / Andrian Valeanu, under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>.</li>
<li>share.png from https://www.iconfinder.com/&#8203;icons/111030/&#8203;share_icon#size=128, by WPZOOM, under <a href="http://creativecommons.org/licenses/by-sa/3.0/">CC BY-SA 3.0</a>.</li>
<li>switch_camera.png from https://www.iconfinder.com/&#8203;icons/103031/&#8203;3d_rotate_icon#size=64, by Valera Zvonko, under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>.</li>
<li>switch_video.png from https://www.iconfinder.com/&#8203;icons/92787/&#8203;film_photo_icon#size=32, by FatCow Web Hosting, under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>.</li>
<li>switch_video.png - merged from images https://www.iconfinder.com/&#8203;icons/81087/&#8203;mb_photo_icon#size=128 and https://www.iconfinder.com/&#8203;icons/81197/&#8203;mb_rec_video_icon#size=128 by Yankoa, under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a> (no need to credit me).</li>
<li>take_video.png, take_video_pref.png, take_video_pressed.png, take_video_recording.png from https://www.iconfinder.com/&#8203;icons/81197/&#8203;mb_rec_video_icon#size=128 , by Yankoa - http://yankoa.deviantart.com/ , under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>.</li>
<li>App icon/logo, take_photo.png, take_photo_pressed.png from https://www.iconfinder.com/&#8203;icons/81087/&#8203;mb_photo_icon#size=128, by Yankoa, under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>.</li>
<li>trash.png from https://www.iconfinder.com/&#8203;icons/115789/&#8203;trash_icon#size=128, by Designmodo / Andrian Valeanu, under <a href="http://creativecommons.org/licenses/by/3.0/">CC BY 3.0</a>.</li>
</ul>
<p>Android, Google Play and the Google Play logo are trademarks of Google LLC.</p>
<h2><a href="history.html">History</a></h2>
<hr>
<p><a href="privacy_oc.html">Open Camera Privacy Policy.</a></p>
<p>This website uses icons from third party sources, see <a href="index.html#licence">licences.</a></p>
<p><a href="https://sourceforge.net/projects/opencamera/">Open Camera on Sourceforge.</a></p>
<hr>
</body>
</html>

83
_docs/info.html Normal file
View file

@ -0,0 +1,83 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="en">
<head>
<title>Open Camera
</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<!-- viewport recommended by Google for mobile design, also required for Google's mobile-friendly test at https://www.google.co.uk/webmasters/tools/mobile-friendly/ -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="stylesheet.css" type="text/css">
<!-- EU cookie law, needed for analytics and adsense -->
<!-- Begin Cookie Consent plugin by Silktide - http://silktide.com/cookieconsent -->
<script type="text/javascript">
window.cookieconsent_options = {
"message":"This website uses cookies, including for Google Analytics and to display ads",
"dismiss":"Got it!",
"learnMore":"More info",
"link":"privacy_oc.html",
"theme":"dark-bottom"};
</script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/cookieconsent2/1.0.9/cookieconsent.min.js"></script>
<!-- End Cookie Consent plugin -->
<!-- Google Analytics -->
<!-- anonymize_ip is set: unclear if this is needed for EU GDPR, but just in case -->
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-38364448-5"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-38364448-5', { 'anonymize_ip': true });
</script>
</head>
<body>
<a href="index.html"><img align="left" src="ic_launcher.png" alt="Open Camera icon" title="Open Camera icon" width="72" height="72"></a>
<hr>
<div style="text-align: center;"><h2>Open Camera</h2></div>
<hr>
<p>&lt; <small><a href="index.html">Main Page.</a></small></p>
<!-- shouldn't link to this page from Google Play, or any apps on Google Play -->
<!-- seems best not to include adsense ads on this page! -->
<ul>
<li><a href="#supportme">Support me!</a></li>
<li><a href="#altdownload">Alternative download sites</a></li>
</ul>
<h2><a name="supportme"><b>Support me!</b></a></h2>
<!-- note this is also linked to from Sourceforge "external link" -->
<p>I am not currently accepting donations. Thanks to those who have supported me in the past!</p>
<h2><a name="altdownload"><b>Alternative download sites</b></a></h2>
<ul>
<li>Open Camera can also be installed from <a href="https://f-droid.org/en/packages/net.sourceforge.opencamera/">F-Droid</a>.</li>
<li>The APK files are also available from
<!-- use &#8203; to limit widths on continuous text to 30 characters, to avoid messing up view on mobile devices -->
<a href="https://sourceforge.net/projects/opencamera/files/">https://sourceforge.net/&#8203;projects/opencamera/files/</a> . To install the APK directly, you will likely have to enable "Unknown sources" to allow installation, on your device's Settings (usually under Security) - if so, you may wish to consider disabling the option again after installing, for security.</li>
</ul>
<hr>
<p><a href="privacy_oc.html">Open Camera Privacy Policy.</a></p>
<p>This website uses icons from third party sources, see <a href="index.html#licence">licences.</a></p>
<p><a href="https://sourceforge.net/projects/opencamera/">Open Camera on Sourceforge.</a></p>
<hr>
</body>
</html>

BIN
_docs/popup.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 706 B

117
_docs/privacy_oc.html Normal file
View file

@ -0,0 +1,117 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html lang="en">
<head>
<title>Open Camera Privacy Policy
</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<!-- viewport recommended by Google for mobile design, also required for Google's mobile-friendly test at https://www.google.co.uk/webmasters/tools/mobile-friendly/ -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="stylesheet.css" type="text/css">
<!-- don't show cookie consent popup on this page - as we don't want to use cookies at all -->
<!-- similarly important not to have anything like adsense or analytics that require any kind of EU
consent, see https://support.google.com/adsense/answer/10961370?sjid=9188374021042041432-EU
-->
</head>
<body>
<a href="index.html"><img align="left" src="ic_launcher.png" alt="Open Camera icon" title="Open Camera icon" width="72" height="72"></a>
<hr>
<div style="text-align: center;"><h2>Open Camera Privacy Policy</h2></div>
<hr>
<p>&lt; <small><a href="index.html">Main Page.</a></small></p>
<!-- note this page is linked to inside of Open Camera, so be careful of changing the link -->
<!-- also remember to update the URL in Google Adsense, see https://support.google.com/adsense/answer/10961370 -->
<!-- also linked from the cookie popup, other pages on this site, and Google Play store listing -->
<!-- if changing this, remember to consider if the in-application privacy policy needs updating -->
<!-- also consider if the adsense EU consent needs changing (e.g., if cookies are being used for additional purposes -->
<p>Open Camera is developed by Mark Harman.</p>
<p>Open Camera accesses and records camera sensor and microphone data, which is used for the purpose
of taking photos and recording videos, to fulfil its purpose as a camera. Microphone permission is also used for the optional "Audio control" options.</p>
<p>Open Camera requires permission (at least for Android 9 and earlier, or using versions of Open Camera older than 1.48.3) to
"access photos, media and files on your devices" (storage permission), as this permission is required for Android to save resultant files such as photos and videos to your device.</p>
<p>Location permission is requested in order to deliver the optional geotagging features (for photos and videos, including stamp and subtitles options).
When relevant option(s) are enabled, your device location will be stored in photo/video/subtitle files.</p>
<p>Bluetooth permissions are used to allow the optional feature to discover and connect to Bluetooth LE remote control devices;
the Bluetooth remote control feature also requires location permission (on Android 11 or earlier) or
Nearby Devices permission (on Android 12 or later).</p>
<p>Resultant data such as photos or videos can be shared with
other apps if you use the share option in Open Camera, or when Open Camera is called by
another app on your device, or when you use the Storage Access Framework option to save
to another app or service.</p>
<p>Data handling procedures, data retention and deletion policies: Open Camera
does not transmit personal or sensitive information to me.</p>
<p>Since Open Camera also uses operating system APIs, you should review relevant privacy policies
such as for your device, manufacturer, operating system and/or Google accounts. For example:</p>
<ul>
<li>For versions 1.49.2 or earlier: the optional voice control option used the Android
<a href="https://developer.android.com/reference/android/speech/SpeechRecognizer">speech recognition service</a>.
When enabled, audio data is likely to be sent to remote servers by Android to perform speech recognition.
This is subject to the Data Processing Addendum for Products where Google is a Data Processor,
located at
<small><a href="https://privacy.google.com/businesses/gdprprocessorterms/">https://privacy.google.com/businesses/gdprprocessorterms/</a></small> , as updated from time to time.
This option is no longer available in version 1.50 onwards.
</li>
<li>For versions 1.49.2 or earlier: The "addresses" option for photo stamp or video subtitles used the Android
<a href="https://developer.android.com/reference/android/location/Geocoder">Geocoder API</a>.
When this option is enabled, in order to deliver this functionality the API transmits your device location data across the Internet to a
third party (which may depend on what "backend services" are installed on your device).
This option is no longer available in version 1.50 onwards.
</li>
<li>Apps/services such as cloud services on your device may auto-upload photos and videos that are saved on your device.
</li>
</ul>
<p>If you have inquiries about my privacy policy, please contact me by email at
<a href="mailto:mark.harman.apps@gmail.com?subject=Open%20Camera%20privacy%20policy">mark.harman.apps@gmail.com</a>.</p>
<!-- from https://support.google.com/adsense/answer/1348695?hl=en-GB , for adsense -->
<p>Although the Open Camera application is ad-free, the Open Camera website has ads via Google Adsense: Third party vendors, including Google, use cookies to
serve ads based on a user's previous visits to this website or other websites. Google's use of advertising cookies enables it and
its partners to serve ads based on people's visit to this sites and/or other sites on the Internet. You may opt out of personalised
advertising by visiting <a href="https://www.google.co.uk/settings/ads">Google's Ads Settings.</a> The cookies of other third-party
vendors or ad networks may also be used to serve ads. You can opt out of some third-party vendors' uses of cookies for personalised advertising by visiting
<a href="http://www.aboutads.info/choices/">www.aboutads.info</a>.</p>
<!--<p><b>Update:</b> I have instructed Google to not display personalised ads to users in the EEA.</p>-->
<p>Note that cookies are still used for serving even non-personalised ads.</p>
<p>In countries where the GDPR is applicable, Google's Consent Management Platform (CMP) is used to obtain consent to use
personal data for Google Adsense. In such countries, you may update your choice by either clicking on the privacy and
cookie link at the bottom of other pages on this site that serve ads, or click "Revoke or change cookie consent" from
the <a href="index.html#contents">site menu</a>.</p>
<p>In US states with relevant privacy regulations, you should be able to opt out of personalised advertising by clicking the
Do Not Sell or Share My Personal Information link at the bottom of other pages on this site that serve ads, and selecting to
Opt Out.</p>
<p>The Open Camera website also uses <a href="https://analytics.google.com">Google Analytics</a> which uses cookies, please see their
<a href="https://support.google.com/analytics/answer/6004245">Privacy Policy</a> for more details.</p>
<p>Also see <a href="https://www.google.com/policies/technologies/partner-sites/">"How Google uses information from sites or apps
that use our services"</a>.</p>
<p>Android is a trademark of Google LLC.</p>
<hr>
<p><a href="privacy_oc.html">Open Camera Privacy Policy.</a></p>
<p>This website uses icons from third party sources, see <a href="index.html#licence">licences.</a></p>
<p><a href="https://sourceforge.net/projects/opencamera/">Open Camera on Sourceforge.</a></p>
<hr>
</body>
</html>

BIN
_docs/settings.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
_docs/share.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 936 B

5
_docs/stylesheet.css Normal file
View file

@ -0,0 +1,5 @@
body {
color: #000000;
background-color: rgb(245,236,220);
font-family: Tahoma, Geneva, sans-serif;
}

BIN
_docs/switch_camera.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
_docs/take_photo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

BIN
_docs/take_video.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

202
androidx_LICENSE-2.0.txt Normal file
View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

67
app/build.gradle Normal file
View file

@ -0,0 +1,67 @@
apply plugin: 'com.android.application'
android {
compileSdk 35
compileOptions.encoding = 'UTF-8'
defaultConfig {
applicationId "net.sourceforge.opencamera"
minSdkVersion 21
targetSdkVersion 35
//compileSdkVersion 31 // needed to support appcompat:1.4.0 (which we need for emoji policy support, and not yet ready to target SDK 30)
testApplicationId "net.sourceforge.opencamera.test"
//testInstrumentationRunner "android.test.InstrumentationTestRunner"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
// needed to use android.test package (ActivityInstrumentationTestCase2 etc) when targetting sdk 28 (Android 9) -
// see https://developer.android.com/training/testing/set-up-project
useLibrary 'android.test.runner'
useLibrary 'android.test.base'
lint {
abortOnError false
checkReleaseBuilds false
}
namespace 'net.sourceforge.opencamera'
buildFeatures {
}
//useLibrary 'android.test.mock'
}
dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
//implementation 'androidx.activity:activity:1.9.3' // needed for EdgeToEdge.enable(this)
// appcompat version must be 1.4.0 or later to satisfy emoji policy!
implementation 'androidx.appcompat:appcompat:1.7.1'
// needed to fix errors since upgrading to appcompat:1.7.0, see https://stackoverflow.com/questions/75263047/duplicate-class-in-kotlin-android
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.9.0"))
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.exifinterface:exifinterface:1.4.1'
testImplementation 'junit:junit:4.13.2'
// newer AndroidJUnit4 InstrumentedTest
androidTestImplementation "androidx.test:runner:1.7.0"
androidTestImplementation "androidx.test:rules:1.7.0"
androidTestImplementation "androidx.test.espresso:espresso-core:3.7.0"
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}

View file

@ -0,0 +1,17 @@
package net.sourceforge.opencamera;
import org.junit.experimental.categories.Categories;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
/** Tests for Avg algorithm - only need to run on a single device
* Should manually look over the images dumped onto DCIM/
* To use these tests, the testdata/ subfolder should be manually copied to the test device in the DCIM/testOpenCamera/
* folder (so you have DCIM/testOpenCamera/testdata/). We don't use assets/ as we'd end up with huge APK sizes which takes
* time to transfer to the device every time we run the tests.
* On Android 10+, scoped storage permission needs to be given to Open Camera for the DCIM/testOpenCamera/ folder.
*/
@RunWith(Categories.class)
@Categories.IncludeCategory(AvgTests.class)
@Suite.SuiteClasses({InstrumentedTest.class})
public class AvgInstrumentedTests {}

View file

@ -0,0 +1,17 @@
package net.sourceforge.opencamera;
import org.junit.experimental.categories.Categories;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
/** Tests for HDR algorithm - only need to run on a single device
* Should manually look over the images dumped onto DCIM/
* To use these tests, the testdata/ subfolder should be manually copied to the test device in the DCIM/testOpenCamera/
* folder (so you have DCIM/testOpenCamera/testdata/). We don't use assets/ as we'd end up with huge APK sizes which takes
* time to transfer to the device every time we run the tests.
* On Android 10+, scoped storage permission needs to be given to Open Camera for the DCIM/testOpenCamera/ folder.
*/
@RunWith(Categories.class)
@Categories.IncludeCategory(HDRTests.class)
@Suite.SuiteClasses({InstrumentedTest.class})
public class HDRInstrumentedTests {}

View file

@ -0,0 +1,17 @@
package net.sourceforge.opencamera;
import org.junit.experimental.categories.Categories;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
/** Tests for HDR algorithm with more than 3 images - only need to run on a single device
* Should manually look over the images dumped onto DCIM/
* To use these tests, the testdata/ subfolder should be manually copied to the test device in the DCIM/testOpenCamera/
* folder (so you have DCIM/testOpenCamera/testdata/). We don't use assets/ as we'd end up with huge APK sizes which takes
* time to transfer to the device every time we run the tests.
* On Android 10+, scoped storage permission needs to be given to Open Camera for the DCIM/testOpenCamera/ folder.
*/
@RunWith(Categories.class)
@Categories.IncludeCategory(HDRNTests.class)
@Suite.SuiteClasses({InstrumentedTest.class})
public class HDRNInstrumentedTests {}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,13 @@
package net.sourceforge.opencamera;
import org.junit.experimental.categories.Categories;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
/** Tests that don't fit into another of the Test suites.
*/
@RunWith(Categories.class)
@Categories.IncludeCategory(MainTests.class)
@Suite.SuiteClasses({InstrumentedTest.class})
public class MainInstrumentedTests {}

View file

@ -0,0 +1,17 @@
package net.sourceforge.opencamera;
import org.junit.experimental.categories.Categories;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
/** Tests for Panorama algorithm - only need to run on a single device
* Should manually look over the images dumped onto DCIM/
* To use these tests, the testdata/ subfolder should be manually copied to the test device in the DCIM/testOpenCamera/
* folder (so you have DCIM/testOpenCamera/testdata/). We don't use assets/ as we'd end up with huge APK sizes which takes
* time to transfer to the device every time we run the tests.
* On Android 10+, scoped storage permission needs to be given to Open Camera for the DCIM/testOpenCamera/ folder.
*/
@RunWith(Categories.class)
@Categories.IncludeCategory(PanoramaTests.class)
@Suite.SuiteClasses({InstrumentedTest.class})
public class PanoramaInstrumentedTests {}

View file

@ -0,0 +1,13 @@
package net.sourceforge.opencamera;
import org.junit.experimental.categories.Categories;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
/** Tests related to taking photos; note that tests to do with photo mode that don't take photos are still part of MainInstrumentedTests.
*/
@RunWith(Categories.class)
@Categories.IncludeCategory(PhotoTests.class)
@Suite.SuiteClasses({InstrumentedTest.class})
public class PhotoInstrumentedTests {}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,13 @@
package net.sourceforge.opencamera;
import org.junit.experimental.categories.Categories;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
/** Tests related to video recording; note that tests to do with video mode that don't record are still part of MainTests.
*/
@RunWith(Categories.class)
@Categories.IncludeCategory(VideoTests.class)
@Suite.SuiteClasses({InstrumentedTest.class})
public class VideoInstrumentedTests {}

View file

@ -0,0 +1,71 @@
package net.sourceforge.opencamera.test;
import junit.framework.Test;
import junit.framework.TestSuite;
public class AvgTests {
/** Tests for Avg algorithm - only need to run on a single device
* Should manually look over the images dumped onto DCIM/
* To use these tests, the testdata/ subfolder should be manually copied to the test device in the DCIM/testOpenCamera/
* folder (so you have DCIM/testOpenCamera/testdata/). We don't use assets/ as we'd end up with huge APK sizes which takes
* time to transfer to the device every time we run the tests.
* On Android 10+, scoped storage permission needs to be given to Open Camera for the DCIM/testOpenCamera/ folder.
* UPDATE: now deprecated, replaced with AvgInstrumentedTests.
*/
public static Test suite() {
TestSuite suite = new TestSuite(MainTests.class.getName());
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg1"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg2"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg3"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg4"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg5"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg6"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg7"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg8"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg9"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg10"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg11"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg12"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg13"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg14"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg15"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg16"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg17"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg18"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg19"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg20"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg21"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg22"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg23"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg24"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg25"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg26"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg27"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg28"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg29"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg30"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg31"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg32"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg33"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg34"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg35"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg36"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg37"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg38"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg39"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg40"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg41"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg42"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg43"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg44"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg45"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg46"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg47"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg48"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg49"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg50"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg51"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAvg52"));
return suite;
}
}

View file

@ -0,0 +1,47 @@
package net.sourceforge.opencamera.test;
import junit.framework.Test;
import junit.framework.TestSuite;
public class HDRNTests {
/** Tests for HDR algorithm with more than 3 images - only need to run on a single device
* Should manually look over the images dumped onto DCIM/
* To use these tests, the testdata/ subfolder should be manually copied to the test device in the DCIM/testOpenCamera/
* folder (so you have DCIM/testOpenCamera/testdata/). We don't use assets/ as we'd end up with huge APK sizes which takes
* time to transfer to the device every time we run the tests.
* On Android 10+, scoped storage permission needs to be given to Open Camera for the DCIM/testOpenCamera/ folder.
* UPDATE: now deprecated, replaced with HDRNInstrumentedTests.
*/
public static Test suite() {
TestSuite suite = new TestSuite(MainTests.class.getName());
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR23_exp2"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR23_exp2b"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR47_exp2"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR49_exp2"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR45"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR46"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR47"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR48"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR49"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR23_exp4"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR49_exp4"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR1_exp5"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR23_exp5"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR45_exp5"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR46_exp5"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR47_exp5"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR48_exp5"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR49_exp5"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR23_exp6"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR23_exp7"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR45_exp7"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR47_exp7"));
return suite;
}
}

View file

@ -0,0 +1,85 @@
package net.sourceforge.opencamera.test;
import junit.framework.Test;
import junit.framework.TestSuite;
public class HDRTests {
/** Tests for HDR algorithm - only need to run on a single device
* Should manually look over the images dumped onto DCIM/
* To use these tests, the testdata/ subfolder should be manually copied to the test device in the DCIM/testOpenCamera/
* folder (so you have DCIM/testOpenCamera/testdata/). We don't use assets/ as we'd end up with huge APK sizes which takes
* time to transfer to the device every time we run the tests.
* On Android 10+, scoped storage permission needs to be given to Open Camera for the DCIM/testOpenCamera/ folder.
* UPDATE: now deprecated, replaced with HDRInstrumentedTests.
*/
public static Test suite() {
TestSuite suite = new TestSuite(MainTests.class.getName());
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testDROZero"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testDRODark0"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testDRODark1"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR1"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR2"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR3"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR4"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR5"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR6"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR7"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR8"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR9"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR10"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR11"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR12"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR13"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR14"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR15"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR16"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR17"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR18"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR19"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR20"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR21"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR22"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR23"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR24"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR25"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR26"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR27"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR28"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR29"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR30"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR31"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR32"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR33"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR34"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR35"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR36"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR37"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR38Filmic"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR39"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR40"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR40Exponential"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR40Filmic"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR41"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR42"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR43"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR44"));
// don't include testHDR45, this is tested as part of HDRNTests
// don't include testHDR46, this is tested as part of HDRNTests
// don't include testHDR47, this is tested as part of HDRNTests
// don't include testHDR48, this is tested as part of HDRNTests
// don't include testHDR49, this is tested as part of HDRNTests
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR50"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR51"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR52"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR53"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR54"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR55"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR56"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR57"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR58"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR59"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR60"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDR61"));
return suite;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,109 @@
package net.sourceforge.opencamera.test;
import android.os.Build;
import junit.framework.Test;
import junit.framework.TestSuite;
import net.sourceforge.opencamera.TestUtils;
public class MainTests {
// Tests that don't fit into another of the Test suites
public static Test suite() {
/*return new TestSuiteBuilder(AllTests.class)
.includeAllPackagesUnderHere()
.build();*/
TestSuite suite = new TestSuite(MainTests.class.getName());
// put these tests first as they require various permissions be allowed, that can only be set by user action
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSwitchVideo"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testLocationSettings"));
// other tests:
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testScopedStorageChecks1"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testScopedStorageChecks2"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testScopedStorageChecks3"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testScopedStorageChecks4"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPause"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testImmediatelyQuit"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testStartCameraPreviewCount"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testCamera2PrefUpgrade"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSaveModes"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testFlashVideoMode"));
//suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSaveFlashTorchSwitchCamera"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testFlashStartup"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testFlashStartup2"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testHDRRestart"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPreviewSize"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPreviewSizeWYSIWYG"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testResolutionMaxMP"));
if( TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testResolutionBurst"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAutoFocus"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAutoFocusCorners"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPopup"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPopupLeftLayout"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testRightLayout"));
//suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPopupLayout")); // don't autotest for now, see comments for the test
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSwitchResolution"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testFaceDetection"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testFocusFlashAvailability"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testFocusSwitchVideoSwitchCameras"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testFocusRemainMacroSwitchCamera"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testFocusRemainMacroSwitchPhoto"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testFocusSaveMacroSwitchPhoto"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testFocusSwitchVideoResetContinuous"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testContinuousPictureFocus"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testContinuousPictureRepeatTouch"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testContinuousPictureSwitchAuto"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testContinuousVideoFocusForPhoto"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testStartupAutoFocus"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSaveQuality"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testZoom"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testZoomIdle"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testZoomSwitchCamera"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSwitchCameraIdle"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSwitchCameraRepeat"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTouchFocusQuick"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testGallery"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSettings"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSettingsSaveLoad"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testFolderChooserNew"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testFolderChooserInvalid"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSaveFolderHistory"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSaveFolderHistorySAF"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSettingsPrivacyPolicy"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPreviewRotation"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testLayoutNoLimits"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testLayoutNoLimitsStartup"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testCameraModes"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testFailOpenCamera"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testAudioControlIcon"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testIconsAgainstCameras"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testOnError"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testGPSString"));
}
if( TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPreviewBitmap"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testVideoFPSHighSpeed"));
}
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ) {
// intensive test, can crash when run as suite on older devices (Nexus 6, Nexus 7) with Camera2 at least
// also run this test last, just in case
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSwitchCameraRepeat2"));
}
return suite;
}
}

View file

@ -0,0 +1,18 @@
package net.sourceforge.opencamera.test;
import junit.framework.Test;
import junit.framework.TestSuite;
public class MultiCameraTests {
// Tests to run specifically on devices where MainActivity.isMultiCamEnabled() returns true.
public static Test suite() {
TestSuite suite = new TestSuite(MainTests.class.getName());
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testIconsAgainstCameras"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoFrontCameraAll"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoFrontCamera"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoFrontCameraMulti"));
return suite;
}
}

View file

@ -0,0 +1,36 @@
package net.sourceforge.opencamera.test;
import android.os.Build;
import junit.framework.Test;
import junit.framework.TestSuite;
import net.sourceforge.opencamera.TestUtils;
public class Nexus7Tests {
// Tests to run specifically on Nexus 7
public static Test suite() {
TestSuite suite = new TestSuite(MainTests.class.getName());
// we run the following tests on the Nexus 7 as a device that supports SAF, but doesn't have Android 7+ (where we use alternative methods for read/writing Exif tags without needing File)
// update: we now (as of 1.48.2) use the same codepaths for exif tags for before and after Android 7, but might as well keep these tests here anyway
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoSAF"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPhotoStampSAF"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testDirectionOnSAF"));
// tests useful for device with no flash, and only 1 camera
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSwitchVideo"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testFocusFlashAvailability"));
// tests for testing Camera2 API with LEGACY Camera2 functionality
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhoto"));
if( TestUtils.isEmulator() && ( Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP || Build.VERSION.SDK_INT == Build.VERSION_CODES.M ) ) {
// video doesn't work on Android 5 or 6 emulator!
}
else {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideo"));
}
return suite;
}
}

View file

@ -0,0 +1,48 @@
package net.sourceforge.opencamera.test;
import android.os.Build;
import junit.framework.Test;
import junit.framework.TestSuite;
import net.sourceforge.opencamera.TestUtils;
public class OldDeviceTests {
// Small set of tests to run on very old devices.
public static Test suite() {
TestSuite suite = new TestSuite(MainTests.class.getName());
// put these tests first as they require various permissions be allowed, that can only be set by user action
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSwitchVideo"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPause"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSaveModes"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testFocusFlashAvailability"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testGallery"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSettings"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSettingsSaveLoad"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testFolderChooserNew"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testFolderChooserInvalid"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSaveFolderHistory"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testSettingsPrivacyPolicy"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testLocationOn"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhoto"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoAutoLevel"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoAutoLevelLowMemory"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoAutoLevelAngles"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoAutoLevelAnglesLowMemory"));
if( TestUtils.isEmulator() && ( Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP || Build.VERSION.SDK_INT == Build.VERSION_CODES.M ) ) {
// video doesn't work on Android 5 or 6 emulator!
}
else {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideo"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoSubtitles"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testIntentVideo"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testIntentVideoDurationLimit"));
}
return suite;
}
}

View file

@ -0,0 +1,61 @@
package net.sourceforge.opencamera.test;
import junit.framework.Test;
import junit.framework.TestSuite;
public class PanoramaTests {
/** Tests for Panorama algorithm - only need to run on a single device
* Should manually look over the images dumped onto DCIM/
* To use these tests, the testdata/ subfolder should be manually copied to the test device in the DCIM/testOpenCamera/
* folder (so you have DCIM/testOpenCamera/testdata/). We don't use assets/ as we'd end up with huge APK sizes which takes
* time to transfer to the device every time we run the tests.
* On Android 10+, scoped storage permission needs to be given to Open Camera for the DCIM/testOpenCamera/ folder.
* UPDATE: now deprecated, replaced with PanoramaInstrumentedTests.
*/
public static Test suite() {
TestSuite suite = new TestSuite(MainTests.class.getName());
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanoramaWhite"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama1"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama2"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama3"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama3_picsperscreen2"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama4"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama5"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama6"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama7"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama8"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama9"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama10"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama11"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama12"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama13"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama14"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama15"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama16"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama17"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama18"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama19"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama20"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama21"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama22"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama23"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama24"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama25"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama26"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama27"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama28"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama28_galaxys10e"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama29"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama30"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama30_galaxys10e"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama31"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama32"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama33"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama34"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama35"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama36"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama37"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPanorama38"));
return suite;
}
}

View file

@ -0,0 +1,42 @@
package net.sourceforge.opencamera.test;
import junit.framework.Test;
import junit.framework.TestSuite;
public class PhotoCamera2Tests {
// Tests related to taking photos that require Camera2 - only need to run this suite with Camera2
public static Test suite() {
TestSuite suite = new TestSuite(MainTests.class.getName());
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoAutoFocusReleaseDuringPhoto"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoManualFocus"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoManualISOExposure"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoManualWB"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoRaw"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoRawWaitCaptureResult"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoRawMulti"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoRawOnly"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoRawExpo"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoRawExpoWaitCaptureResult"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoRawOnlyExpo"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoPreviewPausedTrashRaw"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoPreviewPausedTrashRaw2"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoExpo5"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoHDRSlowBurst"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoHDRSaveExpoRaw"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoHDRSaveExpoRawOnly"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoFocusBracketing"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoFocusBracketingHeavy"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoFocusBracketingCancel"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoRawFocusBracketing"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoRawOnlyFocusBracketing"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoFastBurst"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoContinuousBurst"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoContinuousBurstSlow"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoNR"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoFlashAutoFakeMode"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoFlashOnFakeMode"));
// do testTakePhotoRawRepeat last, and is an intensive test, and if it fails for any reason it seems to cause the following test to crash, terminating the run (at least on Nexus 6)!
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoRawRepeat"));
return suite;
}
}

View file

@ -0,0 +1,109 @@
package net.sourceforge.opencamera.test;
import junit.framework.Test;
import junit.framework.TestSuite;
import net.sourceforge.opencamera.TestUtils;
public class PhotoTests {
// Tests related to taking photos; note that tests to do with photo mode that don't take photos are still part of MainTests
public static Test suite() {
TestSuite suite = new TestSuite(MainTests.class.getName());
// put these tests first as they require various permissions be allowed, that can only be set by user action
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoSAF"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testLocationOn"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testLocationDirectionOn"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testLocationOff"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testLocationOnSAF"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testDirectionOn"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testDirectionOnSAF"));
}
// then do memory intensive tests:
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoAutoLevel"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoAutoLevelLowMemory"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoAutoLevelAngles"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoAutoLevelAnglesLowMemory"));
// other tests:
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhoto"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoContinuous"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoContinuousNoTouch"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoFlashAuto"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoFlashOn"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoFlashTorch"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoAudioButton"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoNoAutofocus"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoNoThumbnail"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoFlashBug"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoFrontCameraAll"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoFrontCamera"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoFrontCameraMulti"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoFrontCameraScreenFlash"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoAutoFocus"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoLockedFocus"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoExposureCompensation"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoLockedLandscape"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoLockedPortrait"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoPreviewPaused"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoPreviewPausedAudioButton"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoPreviewPausedSAF"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoPreviewPausedTrash"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoPreviewPausedTrashSAF"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoPreviewPausedTrash2"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoQuickFocus"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoRepeatFocus"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoRepeatFocusLocked"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoAfterFocus"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoSingleTap"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoDoubleTap"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoAlt"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTimerBackground"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTimerSettings"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTimerPopup"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoRepeat"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testContinuousPicture1"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testContinuousPicture2"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testContinuousPictureFocusRepeat"));
if( TestUtils.test_camera2 ) {
// test_wait_capture_result only relevant for Camera2 API
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testContinuousPictureFocusRepeatWaitCaptureResult"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testKeyboardControls"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPhotoStamp"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPhotoStampSAF"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoDRO"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoDROPhotoStamp"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoHDR"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testPhotoBackgroundHDR"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoHDRSaveExpo"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoHDRFrontCamera"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoHDRAutoStabilise"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoHDRPhotoStamp"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoExpo"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoPanorama"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoPanoramaMax"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoPanoramaCancel"));
//suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoPanoramaCancelBySettings"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testCreateSaveFolder1"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testCreateSaveFolder2"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testCreateSaveFolder3"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testCreateSaveFolder4"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testCreateSaveFolderUnicode"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testCreateSaveFolderEmpty"));
}
// testTakePhotoPreviewPausedShare should be last, as sharing the image may sometimes cause later tests to hang
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakePhotoPreviewPausedShare"));
return suite;
}
}

View file

@ -0,0 +1,15 @@
package net.sourceforge.opencamera.test;
import junit.framework.Test;
import junit.framework.TestSuite;
public class TempTests {
// Dummy test suite for running an arbitrary subset of tests.
public static Test suite() {
TestSuite suite = new TestSuite(MainTests.class.getName());
//suite.addTest(TestSuite.createTest(MainActivityTest.class, "testZoom"));
return suite;
}
}

View file

@ -0,0 +1,95 @@
package net.sourceforge.opencamera.test;
import junit.framework.Test;
import junit.framework.TestSuite;
import net.sourceforge.opencamera.TestUtils;
public class VideoTests {
// Tests related to video recording; note that tests to do with video mode that don't record are still part of MainTests
public static Test suite() {
TestSuite suite = new TestSuite(MainTests.class.getName());
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideo"));
// put these tests first as they require various permissions be allowed, that can only be set by user action:
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoAudioControl"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoSAF"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoSubtitles"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoSubtitlesSAF"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoSubtitlesGPSSAF"));
}
if( TestUtils.test_camera2 ) {
// tests for video log profile (but these don't actually record video)
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testLogProfile1"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testLogProfile2"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testLogProfile3"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testLogProfile1_extra_strong"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testLogProfile2_extra_strong"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testLogProfile3_extra_strong"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testIntentVideo"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testIntentVideoDurationLimit"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testImmersiveMode"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testImmersiveModeEverything"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoStabilization"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoExposureLock"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoFocusArea"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoQuick"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoQuickSAF"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoMaxDuration"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoMaxDurationRestart"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoMaxDurationRestartInterrupt"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoSettings"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoMacro"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoPause"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoPauseStop"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoSnapshot"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoSnapshotTimer"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoSnapshotPausePreview"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoSnapshotMax"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoFlashVideo"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testVideoTimerInterrupt"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testVideoPopup"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testVideoTimerPopup"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoAvailableMemory"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoAvailableMemory2"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoMaxFileSize1"));
if( !TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoMaxFileSize2"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoMaxFileSize3"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoMaxFileSize4"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoMaxFileSize4SAF"));
}
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoTimeLapse"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoForceFailure"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoForceFailureSAF"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoForceIOException"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoForceCameraControllerException"));
if( TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testVideoLogProfile"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testVideoJTLogProfile"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testVideoGammaProfile"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testVideoEdgeModeNoiseReductionMode"));
}
// put tests which change bitrate, fps or test 4K at end
if( TestUtils.test_camera2 ) {
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoFPSHighSpeedManual"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoSlowMotion"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoFPS"));
}
// update: now deprecating these tests, as setting these settings can be dodgy on some devices
/*suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideoBitrate"));
suite.addTest(TestSuite.createTest(MainActivityTest.class, "testTakeVideo4K"));*/
return suite;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">OpenCamera.testTest</string>
</resources>

View file

@ -0,0 +1,159 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:versionCode="93"
android:versionName="1.55"
android:installLocation="auto"
tools:ignore="GoogleAppIndexingWarning">
<!-- ignore GoogleAppIndexingWarning as we don't want to implement that -->
<supports-screens android:xlargeScreens="true" android:largeScreens="true" android:normalScreens="true" android:smallScreens="true" android:anyDensity="true"/>
<uses-permission android:name="android.permission.BLUETOOTH"
android:maxSdkVersion="30"
/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
android:maxSdkVersion="30"
/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
android:usesPermissionFlags="neverForLocation"
tools:targetApi="s" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.microphone" />
<uses-feature android:name="android.hardware.bluetooth_le" android:required="false"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:name=".OpenCameraApplication"
android:theme="@style/AppTheme"
android:largeHeap="true"
>
<!-- should not change the android:name, including moving to a subpackage - see http://android-developers.blogspot.co.uk/2011/06/things-that-cannot-change.html -->
<activity
android:name="net.sourceforge.opencamera.MainActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:clearTaskOnLaunch="true"
android:exported="true"
>
<!-- clearTaskOnLaunch set to true, so if user goes to gallery then returns to home, we return to the camera rather than remaining in gallery if user relaunches Open Camera -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.media.action.IMAGE_CAPTURE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.media.action.IMAGE_CAPTURE_SECURE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.media.action.STILL_IMAGE_CAMERA" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.media.action.STILL_IMAGE_CAMERA_SECURE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.media.action.VIDEO_CAMERA" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.media.action.VIDEO_CAPTURE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts" />
</activity>
<activity
android:name="net.sourceforge.opencamera.remotecontrol.DeviceScanner"
android:label="@string/scan_ble"
android:exported="false"
>
</activity>
<!-- should not change the android:name, including moving to a subpackage - see http://android-developers.blogspot.co.uk/2011/06/things-that-cannot-change.html -->
<activity
android:name="TakePhoto"
android:label="@string/take_photo"
android:icon="@drawable/ic_launcher_take_photo"
android:configChanges="orientation|screenSize|keyboardHidden"
android:taskAffinity=""
android:excludeFromRecents="true"
android:exported="false"
>
</activity>
<!-- should not change the android:name, including moving to a subpackage - see http://android-developers.blogspot.co.uk/2011/06/things-that-cannot-change.html -->
<receiver
android:icon="@drawable/ic_launcher_take_photo"
android:label="@string/take_photo"
android:name="MyWidgetProviderTakePhoto"
android:exported="true">
<intent-filter >
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_info_take_photo" />
</receiver>
<!-- should not change the android:name, including moving to a subpackage - see http://android-developers.blogspot.co.uk/2011/06/things-that-cannot-change.html -->
<service
android:name="net.sourceforge.opencamera.MyTileService"
android:icon="@drawable/ic_photo_camera_white_48dp"
android:label="@string/camera"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
android:exported="true"
tools:targetApi="n">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
<!-- should not change the android:name, including moving to a subpackage - see http://android-developers.blogspot.co.uk/2011/06/things-that-cannot-change.html -->
<service
android:name="net.sourceforge.opencamera.MyTileServiceVideo"
android:icon="@drawable/ic_videocam_white_48dp"
android:label="@string/record_video"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
android:exported="true"
tools:targetApi="n">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
<!-- should not change the android:name, including moving to a subpackage - see http://android-developers.blogspot.co.uk/2011/06/things-that-cannot-change.html -->
<service
android:name="net.sourceforge.opencamera.MyTileServiceFrontCamera"
android:icon="@drawable/ic_face_white_48dp"
android:label="@string/selfie"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
android:exported="true"
tools:targetApi="n">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
<service android:name="net.sourceforge.opencamera.remotecontrol.BluetoothLeService"
android:enabled="true"
android:exported="false"
/>
</application>
<!-- needed for targetting Android 11 - see https://developer.android.com/about/versions/11/behavior-changes-11 -->
<queries>
<intent>
<action android:name="android.intent.action.TTS_SERVICE" />
</intent>
</queries>
</manifest>

View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

View file

@ -0,0 +1,185 @@
package net.sourceforge.opencamera;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.util.Log;
import androidx.annotation.RequiresPermission;
/** Sets up a listener to listen for noise level.
*/
class AudioListener {
private static final String TAG = "AudioListener";
private volatile boolean is_running = true; // should be volatile, as used to communicate between threads
private int buffer_size = -1;
private AudioRecord ar; // modification to ar should always be synchronized (on AudioListener.this), as the ar can be released in the AudioListener's own thread
private Thread thread;
public interface AudioListenerCallback {
void onAudio(int level);
}
/** Create a new AudioListener. The caller should call the start() method to start listening.
*/
@RequiresPermission(android.Manifest.permission.RECORD_AUDIO)
AudioListener(final AudioListenerCallback cb) {
if( MyDebug.LOG )
Log.d(TAG, "new AudioListener");
final int sample_rate = 8000;
int channel_config = AudioFormat.CHANNEL_IN_MONO;
int audio_format = AudioFormat.ENCODING_PCM_16BIT;
try {
buffer_size = AudioRecord.getMinBufferSize(sample_rate, channel_config, audio_format);
//buffer_size = -1; // test
if( MyDebug.LOG )
Log.d(TAG, "buffer_size: " + buffer_size);
if( buffer_size <= 0 ) {
if( MyDebug.LOG ) {
if( buffer_size == AudioRecord.ERROR )
Log.e(TAG, "getMinBufferSize returned ERROR");
else if( buffer_size == AudioRecord.ERROR_BAD_VALUE )
Log.e(TAG, "getMinBufferSize returned ERROR_BAD_VALUE");
}
return;
}
synchronized(AudioListener.this) {
ar = new AudioRecord(MediaRecorder.AudioSource.MIC, sample_rate, channel_config, audio_format, buffer_size);
AudioListener.this.notifyAll(); // probably not needed currently as no thread should be waiting for creation, but just for consistency
}
}
catch(Exception e) {
MyDebug.logStackTrace(TAG, "failed to create audiorecord", e);
return;
}
// check initialised
synchronized(AudioListener.this) {
if( ar.getState() == AudioRecord.STATE_INITIALIZED ) {
if( MyDebug.LOG )
Log.d(TAG, "audiorecord is initialised");
}
else {
Log.e(TAG, "audiorecord failed to initialise");
ar.release();
ar = null;
AudioListener.this.notifyAll(); // again probably not needed, but just in case
return;
}
}
final short[] buffer = new short[buffer_size];
ar.startRecording();
this.thread = new Thread() {
@Override
public void run() {
/*int sample_delay = (1000 * buffer_size) / sample_rate;
if( MyDebug.LOG )
Log.e(TAG, "sample_delay: " + sample_delay);*/
while( is_running ) {
/*try{
Thread.sleep(sample_delay);
}
catch(InterruptedException e) {
MyDebug.logStackTrace(TAG, "InterruptedException from sleep", e);
}*/
try {
int n_read = ar.read(buffer, 0, buffer_size);
if( n_read > 0 ) {
int average_noise = 0;
int max_noise = 0;
for(int i=0;i<n_read;i++){
int value = Math.abs(buffer[i]);
average_noise += value;
max_noise = Math.max(max_noise, value);
}
average_noise /= n_read;
/*if( MyDebug.LOG ) {
Log.d(TAG, "n_read: " + n_read);
Log.d(TAG, "average noise: " + average_noise);
Log.d(TAG, "max noise: " + max_noise);
}*/
cb.onAudio(average_noise);
}
else {
if( MyDebug.LOG ) {
Log.d(TAG, "n_read: " + n_read);
if( n_read == AudioRecord.ERROR_INVALID_OPERATION )
Log.e(TAG, "read returned ERROR_INVALID_OPERATION");
else if( n_read == AudioRecord.ERROR_BAD_VALUE )
Log.e(TAG, "read returned ERROR_BAD_VALUE");
}
}
}
catch(Exception e) {
MyDebug.logStackTrace(TAG, "failed to read from audiorecord", e);
}
}
if( MyDebug.LOG )
Log.d(TAG, "stopped running");
synchronized(AudioListener.this) {
if( MyDebug.LOG )
Log.d(TAG, "release ar");
ar.release();
ar = null;
AudioListener.this.notifyAll(); // notify in case release() is waiting
}
}
};
// n.b., not good practice to start threads in constructors, so we require the caller to call start() instead
}
/**
* @return Whether the audio recorder was created successfully.
*/
boolean status() {
boolean ok;
synchronized(AudioListener.this) {
ok = ar != null;
}
return ok;
}
/** Start listening.
*/
void start() {
if( MyDebug.LOG )
Log.d(TAG, "start");
if( thread != null ) {
thread.start();
}
}
/** Stop listening and release the resources.
* @param wait_until_done If true, this method will block until the resource is freed.
*/
void release(boolean wait_until_done) {
if( MyDebug.LOG ) {
Log.d(TAG, "release");
Log.d(TAG, "wait_until_done: " + wait_until_done);
}
is_running = false;
thread = null;
if( wait_until_done ) {
if( MyDebug.LOG )
Log.d(TAG, "wait until audio listener is freed");
synchronized(AudioListener.this) {
while( ar != null ) {
if( MyDebug.LOG )
Log.d(TAG, "ar still not freed, so wait");
try {
AudioListener.this.wait();
}
catch(InterruptedException e) {
MyDebug.logStackTrace(TAG, "interrupted while waiting for audio recorder to be freed", e);
}
}
}
if( MyDebug.LOG )
Log.d(TAG, "audio listener is now freed");
}
}
}

View file

@ -0,0 +1,665 @@
package net.sourceforge.opencamera;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/** Handles gyro sensor.
*/
public class GyroSensor implements SensorEventListener {
private static final String TAG = "GyroSensor";
final private SensorManager mSensorManager;
final private Sensor mSensor;
final private Sensor mSensorAccel;
private boolean is_recording;
private long timestamp;
private static final float NS2S = 1.0f / 1000000000.0f;
private final float [] deltaRotationVector = new float[4];
private boolean has_gyroVector;
private final float [] gyroVector = new float[3];
private final float [] currentRotationMatrix = new float[9];
private final float [] currentRotationMatrixGyroOnly = new float[9];
private final float [] deltaRotationMatrix = new float[9];
private final float [] tempMatrix = new float[9];
private final float [] temp2Matrix = new float[9];
private boolean has_init_accel = false;
private final float [] initAccelVector = new float[3];
private final float [] accelVector = new float[3];
private boolean has_original_rotation_matrix;
private final float [] originalRotationMatrix = new float[9];
private boolean has_rotationVector;
private final float [] rotationVector = new float[3];
// temporary vectors:
private final float [] tempVector = new float[3];
private final float [] inVector = new float[3];
public interface TargetCallback {
/** Called when the target has been achieved.
* @param indx Index of the target that has been achieved.
*/
void onAchieved(int indx);
/* Called when the orientation is significantly far from the target.
*/
void onTooFar();
}
private boolean hasTarget;
//private final float [] targetVector = new float[3];
private final List<float []> targetVectors = new ArrayList<>();
private float targetAngle; // target angle in radians
private float uprightAngleTol; // in radians
private boolean targetAchieved;
private float tooFarAngle; // in radians
private TargetCallback targetCallback;
private boolean has_lastTargetAngle;
private float lastTargetAngle;
private int is_upright; // if hasTarget==true, this stores whether the "upright" orientation of the device is close enough to the orientation when recording was started: 0 for yes, otherwise -1 for too anti-clockwise, +1 for too clockwise
GyroSensor(Context context) {
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
mSensorAccel = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
//mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR);
//mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GAME_ROTATION_VECTOR);
//mSensorAccel = null;
if( MyDebug.LOG ) {
Log.d(TAG, "GyroSensor");
if( mSensor == null )
Log.d(TAG, "gyroscope not available");
else if( mSensorAccel == null )
Log.d(TAG, "accelerometer not available");
}
setToIdentity();
}
boolean hasSensors() {
// even though the gyro sensor works if mSensorAccel is not present, for best behaviour we require them both
return mSensor != null && mSensorAccel != null;
}
private void setToIdentity() {
for(int i=0;i<9;i++) {
currentRotationMatrix[i] = 0.0f;
}
currentRotationMatrix[0] = 1.0f;
currentRotationMatrix[4] = 1.0f;
currentRotationMatrix[8] = 1.0f;
System.arraycopy(currentRotationMatrix, 0, currentRotationMatrixGyroOnly, 0, 9);
for(int i=0;i<3;i++) {
initAccelVector[i] = 0.0f;
// don't set accelVector, rotationVector, gyroVector to 0 here, as we continually smooth the values even when not recording
}
has_init_accel = false;
has_original_rotation_matrix = false;
}
/** Helper method to set a 3D vector.
*/
static void setVector(final float[] vector, float x, float y, float z) {
vector[0] = x;
vector[1] = y;
vector[2] = z;
}
/** Helper method to access the (i, j)th component of a 3x3 matrix.
*/
private static float getMatrixComponent(final float [] matrix, int row, int col) {
return matrix[row*3+col];
}
/** Helper method to set the (i, j)th component of a 3x3 matrix.
*/
private static void setMatrixComponent(final float [] matrix, int row, int col, float value) {
matrix[row*3+col] = value;
}
/** Helper method to multiply 3x3 matrix with a 3D vector.
*/
public static void transformVector(final float [] result, final float [] matrix, final float [] vector) {
// result[i] = matrix[ij] . vector[j]
for(int i=0;i<3;i++) {
result[i] = 0.0f;
for(int j=0;j<3;j++) {
result[i] += getMatrixComponent(matrix, i, j) * vector[j];
}
}
}
/** Helper method to multiply the transpose of a 3x3 matrix with a 3D vector.
* For 3x3 rotation (orthonormal) matrices, the transpose is the inverse.
*/
private void transformTransposeVector(final float [] result, final float [] matrix, final float [] vector) {
// result[i] = matrix[ji] . vector[j]
for(int i=0;i<3;i++) {
result[i] = 0.0f;
for(int j=0;j<3;j++) {
result[i] += getMatrixComponent(matrix, j, i) * vector[j];
}
}
}
/* We should enable sensors before startRecording(), so that we can apply smoothing to the
* sensors to reduce noise.
* This should be limited to when we might want to use the gyro, to help battery life.
*/
void enableSensors() {
if( MyDebug.LOG )
Log.d(TAG, "enableSensors");
has_rotationVector = false;
has_gyroVector = false;
for(int i=0;i<3;i++) {
accelVector[i] = 0.0f;
rotationVector[i] = 0.0f;
gyroVector[i] = 0.0f;
}
if( mSensor != null )
mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_UI);
if( mSensorAccel != null )
mSensorManager.registerListener(this, mSensorAccel, SensorManager.SENSOR_DELAY_UI);
}
void disableSensors() {
if( MyDebug.LOG )
Log.d(TAG, "disableSensors");
mSensorManager.unregisterListener(this);
}
void startRecording() {
if( MyDebug.LOG )
Log.d(TAG, "startRecording");
is_recording = true;
timestamp = 0;
setToIdentity();
}
void stopRecording() {
if( is_recording ) {
if( MyDebug.LOG )
Log.d(TAG, "stopRecording");
is_recording = false;
timestamp = 0;
}
}
public boolean isRecording() {
return this.is_recording;
}
void setTarget(float target_x, float target_y, float target_z, float targetAngle, float uprightAngleTol, float tooFarAngle, TargetCallback targetCallback) {
this.hasTarget = true;
this.targetVectors.clear();
addTarget(target_x, target_y, target_z);
this.targetAngle = targetAngle;
this.uprightAngleTol = uprightAngleTol;
this.tooFarAngle = tooFarAngle;
this.targetCallback = targetCallback;
this.has_lastTargetAngle = false;
this.lastTargetAngle = 0.0f;
}
void addTarget(float target_x, float target_y, float target_z) {
float [] vector = new float[]{target_x, target_y, target_z};
this.targetVectors.add(vector);
}
void clearTarget() {
this.hasTarget = false;
this.targetVectors.clear();
this.targetCallback = null;
this.has_lastTargetAngle = false;
this.lastTargetAngle = 0.0f;
}
void disableTargetCallback() {
this.targetCallback = null;
}
boolean hasTarget() {
return this.hasTarget;
}
boolean isTargetAchieved() {
return this.hasTarget && this.targetAchieved;
}
public int isUpright() {
return this.is_upright;
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
private void adjustGyroForAccel() {
if( timestamp == 0 ) {
// don't have a gyro matrix yet
return;
}
else if( !has_init_accel ) {
return;
}
/*if( true )
return;*/ // don't use accelerometer for now
//transformVector(tempVector, currentRotationMatrix, initAccelVector);
// tempVector is now the initAccelVector transformed by the gyro matrix
//transformTransposeVector(tempVector, currentRotationMatrix, initAccelVector);
transformVector(tempVector, currentRotationMatrix, accelVector);
// tempVector is now the accelVector transformed by the gyro matrix
double cos_angle = (tempVector[0] * initAccelVector[0] + tempVector[1] * initAccelVector[1] + tempVector[2] * initAccelVector[2]);
/*if( MyDebug.LOG ) {
Log.d(TAG, "adjustGyroForAccel:");
Log.d(TAG, "### currentRotationMatrix row 0: " + currentRotationMatrix[0] + " , " + currentRotationMatrix[1] + " , " + currentRotationMatrix[2]);
Log.d(TAG, "### currentRotationMatrix row 1: " + currentRotationMatrix[3] + " , " + currentRotationMatrix[4] + " , " + currentRotationMatrix[5]);
Log.d(TAG, "### currentRotationMatrix row 2: " + currentRotationMatrix[6] + " , " + currentRotationMatrix[7] + " , " + currentRotationMatrix[8]);
Log.d(TAG, "### initAccelVector: " + initAccelVector[0] + " , " + initAccelVector[1] + " , " + initAccelVector[2]);
Log.d(TAG, "### accelVector: " + accelVector[0] + " , " + accelVector[1] + " , " + accelVector[2]);
Log.d(TAG, "### tempVector: " + tempVector[0] + " , " + tempVector[1] + " , " + tempVector[2]);
Log.d(TAG, "### cos_angle: " + cos_angle);
}*/
if( cos_angle >= 0.99999999995 ) {
// gyroscope already matches accelerometer
return;
}
double angle = Math.acos(cos_angle);
angle *= 0.02f; // filter
cos_angle = Math.cos(angle);
/*
// compute matrix to transform tempVector to accelVector
// compute (tempVector X accelVector) normalised
double a_x = tempVector[1] * accelVector[2] - tempVector[2] * accelVector[1];
double a_y = tempVector[2] * accelVector[0] - tempVector[0] * accelVector[2];
double a_z = tempVector[0] * accelVector[1] - tempVector[1] * accelVector[0];
*/
// compute matrix to transform tempVector to initAccelVector
// compute (tempVector X initAccelVector) normalised
double a_x = tempVector[1] * initAccelVector[2] - tempVector[2] * initAccelVector[1];
double a_y = tempVector[2] * initAccelVector[0] - tempVector[0] * initAccelVector[2];
double a_z = tempVector[0] * initAccelVector[1] - tempVector[1] * initAccelVector[0];
double a_mag = Math.sqrt(a_x*a_x + a_y*a_y + a_z*a_z);
if( a_mag < 1.0e-5 ) {
// parallel or anti-parallel case
return;
}
a_x /= a_mag;
a_y /= a_mag;
a_z /= a_mag;
double sin_angle = Math.sqrt(1.0-cos_angle*cos_angle);
// from http://immersivemath.com/forum/question/rotation-matrix-from-one-vector-to-another/
setMatrixComponent(tempMatrix, 0, 0, (float)(a_x*a_x*(1.0-cos_angle)+cos_angle));
setMatrixComponent(tempMatrix, 0, 1, (float)(a_x*a_y*(1.0-cos_angle)-sin_angle*a_z));
setMatrixComponent(tempMatrix, 0, 2, (float)(a_x*a_z*(1.0-cos_angle)+sin_angle*a_y));
setMatrixComponent(tempMatrix, 1, 0, (float)(a_x*a_y*(1.0-cos_angle)+sin_angle*a_z));
setMatrixComponent(tempMatrix, 1, 1, (float)(a_y*a_y*(1.0-cos_angle)+cos_angle));
setMatrixComponent(tempMatrix, 1, 2, (float)(a_y*a_z*(1.0-cos_angle)-sin_angle*a_x));
setMatrixComponent(tempMatrix, 2, 0, (float)(a_x*a_z*(1.0-cos_angle)-sin_angle*a_y));
setMatrixComponent(tempMatrix, 2, 1, (float)(a_y*a_z*(1.0-cos_angle)+sin_angle*a_x));
setMatrixComponent(tempMatrix, 2, 2, (float)(a_z*a_z*(1.0-cos_angle)+cos_angle));
/*if( MyDebug.LOG ) {
// test:
System.arraycopy(tempVector, 0, inVector, 0, 3);
transformVector(tempVector, tempMatrix, inVector);
Log.d(TAG, "### tempMatrix row 0: " + tempMatrix[0] + " , " + tempMatrix[1] + " , " + tempMatrix[2]);
Log.d(TAG, "### tempMatrix row 1: " + tempMatrix[3] + " , " + tempMatrix[4] + " , " + tempMatrix[5]);
Log.d(TAG, "### tempMatrix row 2: " + tempMatrix[6] + " , " + tempMatrix[7] + " , " + tempMatrix[8]);
Log.d(TAG, "### rotated tempVector: " + tempVector[0] + " , " + tempVector[1] + " , " + tempVector[2]);
}*/
// replace currentRotationMatrix with tempMatrix.currentRotationMatrix
// since [tempMatrix.currentRotationMatrix].[initAccelVector] = tempMatrix.tempVector = accelVector
// since [tempMatrix.currentRotationMatrix].[accelVector] = tempMatrix.tempVector = initAccelVector
for(int i=0;i<3;i++) {
for(int j=0;j<3;j++) {
float value = 0.0f;
// temp2Matrix[ij] = tempMatrix[ik] * currentRotationMatrix[kj]
for(int k=0;k<3;k++) {
value += getMatrixComponent(tempMatrix, i, k) * getMatrixComponent(currentRotationMatrix, k, j);
}
setMatrixComponent(temp2Matrix, i, j, value);
}
}
System.arraycopy(temp2Matrix, 0, currentRotationMatrix, 0, 9);
/*if( MyDebug.LOG ) {
// test:
//transformVector(tempVector, temp2Matrix, initAccelVector);
//transformTransposeVector(tempVector, currentRotationMatrix, initAccelVector);
transformVector(tempVector, temp2Matrix, accelVector);
Log.d(TAG, "### new currentRotationMatrix row 0: " + temp2Matrix[0] + " , " + temp2Matrix[1] + " , " + temp2Matrix[2]);
Log.d(TAG, "### new currentRotationMatrix row 1: " + temp2Matrix[3] + " , " + temp2Matrix[4] + " , " + temp2Matrix[5]);
Log.d(TAG, "### new currentRotationMatrix row 2: " + temp2Matrix[6] + " , " + temp2Matrix[7] + " , " + temp2Matrix[8]);
Log.d(TAG, "### new tempVector: " + tempVector[0] + " , " + tempVector[1] + " , " + tempVector[2]);
}*/
}
@Override
public void onSensorChanged(SensorEvent event) {
/*if( MyDebug.LOG )
Log.d(TAG, "onSensorChanged: " + event);*/
if( event.sensor.getType() == Sensor.TYPE_ACCELEROMETER ) {
final float sensor_alpha = 0.8f; // for filter
for(int i=0;i<3;i++) {
//this.accelVector[i] = event.values[i];
this.accelVector[i] = sensor_alpha * this.accelVector[i] + (1.0f-sensor_alpha) * event.values[i];
}
double mag = Math.sqrt(accelVector[0]*accelVector[0] + accelVector[1]*accelVector[1] + accelVector[2]*accelVector[2]);
if( mag > 1.0e-8 ) {
//noinspection lossy-conversions
accelVector[0] /= mag;
//noinspection lossy-conversions
accelVector[1] /= mag;
//noinspection lossy-conversions
accelVector[2] /= mag;
}
if( !has_init_accel ) {
System.arraycopy(accelVector, 0, initAccelVector, 0, 3);
has_init_accel = true;
}
adjustGyroForAccel();
}
else if( event.sensor.getType() == Sensor.TYPE_GYROSCOPE ) {
if( has_gyroVector ) {
final float sensor_alpha = 0.5f; // for filter
for(int i=0;i<3;i++) {
//this.gyroVector[i] = event.values[i];
this.gyroVector[i] = sensor_alpha * this.gyroVector[i] + (1.0f-sensor_alpha) * event.values[i];
}
}
else {
System.arraycopy(event.values, 0, this.gyroVector, 0, 3);
has_gyroVector = true;
}
// This timestep's delta rotation to be multiplied by the current rotation
// after computing it from the gyro sample data.
if( timestamp != 0 ) {
final float dT = (event.timestamp - timestamp) * NS2S;
// Axis of the rotation sample, not normalized yet.
float axisX = gyroVector[0];
float axisY = gyroVector[1];
float axisZ = gyroVector[2];
// Calculate the angular speed of the sample
double omegaMagnitude = Math.sqrt(axisX*axisX + axisY*axisY + axisZ*axisZ);
// Normalize the rotation vector if it's big enough to get the axis
// (that is, EPSILON should represent your maximum allowable margin of error)
if( omegaMagnitude > 1.0e-5 ) {
//noinspection lossy-conversions
axisX /= omegaMagnitude;
//noinspection lossy-conversions
axisY /= omegaMagnitude;
//noinspection lossy-conversions
axisZ /= omegaMagnitude;
}
// Integrate around this axis with the angular speed by the timestep
// in order to get a delta rotation from this sample over the timestep
// We will convert this axis-angle representation of the delta rotation
// into a quaternion before turning it into the rotation matrix.
double thetaOverTwo = omegaMagnitude * dT / 2.0f;
float sinThetaOverTwo = (float)Math.sin(thetaOverTwo);
float cosThetaOverTwo = (float)Math.cos(thetaOverTwo);
deltaRotationVector[0] = sinThetaOverTwo * axisX;
deltaRotationVector[1] = sinThetaOverTwo * axisY;
deltaRotationVector[2] = sinThetaOverTwo * axisZ;
deltaRotationVector[3] = cosThetaOverTwo;
/*if( MyDebug.LOG ) {
Log.d(TAG, "### values: " + event.values[0] + " , " + event.values[1] + " , " + event.values[2]);
Log.d(TAG, "smoothed values: " + gyroVector[0] + " , " + gyroVector[1] + " , " + gyroVector[2]);
}*/
SensorManager.getRotationMatrixFromVector(deltaRotationMatrix, deltaRotationVector);
// User code should concatenate the delta rotation we computed with the current rotation
// in order to get the updated rotation.
// currentRotationMatrix = currentRotationMatrix * deltaRotationMatrix;
for(int i=0;i<3;i++) {
for(int j=0;j<3;j++) {
float value = 0.0f;
// tempMatrix[ij] = currentRotationMatrix[ik] * deltaRotationMatrix[kj]
for(int k=0;k<3;k++) {
value += getMatrixComponent(currentRotationMatrix, i, k) * getMatrixComponent(deltaRotationMatrix, k, j);
}
setMatrixComponent(tempMatrix, i, j, value);
}
}
System.arraycopy(tempMatrix, 0, currentRotationMatrix, 0, 9);
for(int i=0;i<3;i++) {
for(int j=0;j<3;j++) {
float value = 0.0f;
// tempMatrix[ij] = currentRotationMatrixGyroOnly[ik] * deltaRotationMatrix[kj]
for(int k=0;k<3;k++) {
value += getMatrixComponent(currentRotationMatrixGyroOnly, i, k) * getMatrixComponent(deltaRotationMatrix, k, j);
}
setMatrixComponent(tempMatrix, i, j, value);
}
}
System.arraycopy(tempMatrix, 0, currentRotationMatrixGyroOnly, 0, 9);
/*if( MyDebug.LOG ) {
setVector(inVector, 0.0f, 0.0f, -1.0f); // vector pointing behind the device's screen
transformVector(tempVector, currentRotationMatrix, inVector);
//transformTransposeVector(tempVector, currentRotationMatrix, inVector);
Log.d(TAG, "### gyro vector: " + tempVector[0] + " , " + tempVector[1] + " , " + tempVector[2]);
}*/
adjustGyroForAccel();
}
timestamp = event.timestamp;
}
else if( event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR || event.sensor.getType() == Sensor.TYPE_GAME_ROTATION_VECTOR ) {
if( has_rotationVector ) {
//final float sensor_alpha = 0.7f; // for filter
final float sensor_alpha = 0.8f; // for filter
for(int i=0;i<3;i++) {
//this.rotationVector[i] = event.values[i];
this.rotationVector[i] = sensor_alpha * this.rotationVector[i] + (1.0f-sensor_alpha) * event.values[i];
}
}
else {
System.arraycopy(event.values, 0, this.rotationVector, 0, 3);
has_rotationVector = true;
}
SensorManager.getRotationMatrixFromVector(tempMatrix, rotationVector);
if( !has_original_rotation_matrix ) {
System.arraycopy(tempMatrix, 0, originalRotationMatrix, 0, 9);
has_original_rotation_matrix = event.values[3] != 1.0;
}
// current = originalT.new
for(int i=0;i<3;i++) {
for(int j=0;j<3;j++) {
float value = 0.0f;
// currentRotationMatrix[ij] = originalRotationMatrix[ki] * tempMatrix[kj]
for(int k=0;k<3;k++) {
value += getMatrixComponent(originalRotationMatrix, k, i) * getMatrixComponent(tempMatrix, k, j);
}
setMatrixComponent(currentRotationMatrix, i, j, value);
}
}
if( MyDebug.LOG ) {
Log.d(TAG, "### values: " + event.values[0] + " , " + event.values[1] + " , " + event.values[2] + " , " + event.values[3]);
Log.d(TAG, " " + currentRotationMatrix[0] + " , " + currentRotationMatrix[1] + " , " + currentRotationMatrix[2]);
Log.d(TAG, " " + currentRotationMatrix[3] + " , " + currentRotationMatrix[4] + " , " + currentRotationMatrix[5]);
Log.d(TAG, " " + currentRotationMatrix[6] + " , " + currentRotationMatrix[7] + " , " + currentRotationMatrix[8]);
}
}
if( hasTarget ) {
int n_too_far = 0;
targetAchieved = false;
for(int indx=0;indx<targetVectors.size();indx++) {
float [] targetVector = targetVectors.get(indx);
// first check if we are still "upright"
setVector(inVector, 0.0f, 1.0f, 0.0f); // vector pointing in "up" direction
transformVector(tempVector, currentRotationMatrix, inVector);
/*if( MyDebug.LOG ) {
Log.d(TAG, "### transformed vector up: " + tempVector[0] + " , " + tempVector[1] + " , " + tempVector[2]);
}*/
/*float sin_angle_up = tempVector[0];
if( Math.abs(sin_angle_up) <= 0.017452406437f ) { // 1 degree
is_upright = 0;
}
else
is_upright = (sin_angle_up > 0) ? 1 : -1;*/
// store up vector
is_upright = 0;
float ux = tempVector[0];
float uy = tempVector[1];
float uz = tempVector[2];
// project up vector into plane perpendicular to targetVector
// v' = v - (v.n)n
float u_dot_n = ux * targetVector[0] + uy * targetVector[1] + uz * targetVector[2];
float p_ux = ux - u_dot_n * targetVector[0];
float p_uy = uy - u_dot_n * targetVector[1];
float p_uz = uz - u_dot_n * targetVector[2];
/*if( MyDebug.LOG ) {
Log.d(TAG, " u: " + ux + " , " + uy + " , " + uz);
Log.d(TAG, " p_u: " + p_ux + " , " + p_uy + " , " + p_uz);
}*/
double p_u_mag = Math.sqrt(p_ux*p_ux + p_uy*p_uy + p_uz*p_uz);
if( p_u_mag > 1.0e-5 ) {
/*if( MyDebug.LOG ) {
Log.d(TAG, " p_u norm: " + p_ux/p_u_mag + " , " + p_uy/p_u_mag + " , " + p_uz/p_u_mag);
}*/
// normalise p_u
//noinspection lossy-conversions
p_ux /= p_u_mag;
//p_uy /= p_u_mag; // commented out as not needed
//noinspection lossy-conversions
p_uz /= p_u_mag;
// compute p_u X (0 1 0)
float cx = - p_uz;
float cy = 0.0f;
float cz = p_ux;
/*if( MyDebug.LOG ) {
Log.d(TAG, " c: " + cx + " , " + cy + " , " + cz);
}*/
float sin_angle_up = (float)Math.sqrt(cx*cx + cy*cy + cz*cz);
float angle_up = (float)Math.asin(sin_angle_up);
setVector(inVector, 0.0f, 0.0f, -1.0f); // vector pointing behind the device's screen
transformVector(tempVector, currentRotationMatrix, inVector);
if( Math.abs(angle_up) > this.uprightAngleTol ) {
float dot = cx*tempVector[0] + cy*tempVector[1] + cz*tempVector[2];
is_upright = (dot < 0) ? 1 : -1;
}
}
float cos_angle = tempVector[0] * targetVector[0] + tempVector[1] * targetVector[1] + tempVector[2] * targetVector[2];
float angle = (float)Math.acos(cos_angle);
if( is_upright == 0 ) {
/*if( MyDebug.LOG )
Log.d(TAG, "gyro vector angle with target: " + Math.toDegrees(angle) + " degrees");*/
if( angle <= targetAngle ) {
if( MyDebug.LOG )
Log.d(TAG, " ### achieved target angle: " + Math.toDegrees(angle) + " degrees");
targetAchieved = true;
if( targetCallback != null ) {
//targetCallback.onAchieved(indx);
if( has_lastTargetAngle ) {
if( MyDebug.LOG )
Log.d(TAG, " last target angle: " + Math.toDegrees(lastTargetAngle) + " degrees");
if( angle > lastTargetAngle ) {
// started to get worse, so call callback
targetCallback.onAchieved(indx);
}
// else, don't call callback yet, as we may get closer to the target
}
}
// only bother setting the lastTargetAngle if within the target angle - otherwise we'll have problems if there is more than one target set
has_lastTargetAngle = true;
lastTargetAngle = angle;
}
}
if( angle > tooFarAngle ) {
n_too_far++;
}
/*if( MyDebug.LOG )
Log.d(TAG, "targetAchieved? " + targetAchieved);*/
}
if( n_too_far > 0 && n_too_far == targetVectors.size() ) {
if( targetCallback != null ) {
targetCallback.onTooFar();
}
}
}
}
/* This returns a 3D vector, that represents the current direction that the device is pointing (looking towards the screen),
* relative to when startRecording() was called.
* That is, the coordinate system is defined by the device's initial orientation when startRecording() was called:
* X: -ve to +ve is left to right
* Y: -ve to +ve is down to up
* Z: -ve to +ve is out of the screen to behind the screen
* So if the device hasn't changed orientation, this will return (0, 0, -1).
* (1, 0, 0) means the device has rotated 90 degrees so it's now pointing to the right.
* @param result An array of length 3 to store the returned vector.
*/
/*void getRelativeVector(float [] result) {
setVector(inVector, 0.0f, 0.0f, -1.0f); // vector pointing behind the device's screen
transformVector(result, currentRotationMatrix, inVector);
}*/
/*void getRelativeInverseVector(float [] result) {
setVector(inVector, 0.0f, 0.0f, -1.0f); // vector pointing behind the device's screen
transformTransposeVector(result, currentRotationMatrix, inVector);
}*/
public void getRelativeInverseVector(float [] out, float [] in) {
transformTransposeVector(out, currentRotationMatrix, in);
}
public void getRelativeInverseVectorGyroOnly(float [] out, float [] in) {
transformTransposeVector(out, currentRotationMatrixGyroOnly, in);
}
public void getRotationMatrix(float [] out) {
System.arraycopy(currentRotationMatrix, 0, out, 0, 9);
}
// for testing
public void testForceTargetAchieved(int indx) {
if( MyDebug.LOG )
Log.d(TAG, "testForceTargetAchieved: " + indx);
if( targetCallback != null ) {
targetCallback.onAchieved(indx);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,19 @@
package net.sourceforge.opencamera;
/** Exception for HDRProcessor class.
*/
@SuppressWarnings("WeakerAccess")
public class HDRProcessorException extends Exception {
final static public int INVALID_N_IMAGES = 0; // the supplied number of images is not supported
final static public int UNEQUAL_SIZES = 1; // images not of the same resolution
final private int code;
HDRProcessorException(int code) {
this.code = code;
}
public int getCode() {
return code;
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,269 @@
package net.sourceforge.opencamera;
import android.graphics.Bitmap;
import android.util.Log;
public class JavaImageProcessing {
private static final String TAG = "JavaImageProcessing";
public interface ApplyFunctionInterface {
void init(int n_threads);
void apply(CachedBitmap output, int thread_index, int off_x, int off_y, int this_width, int this_height); // version with no input
/**
* @param pixels An array of pixels for the subset being operated on. I.e., pixels[0] represents the input pixel at (off_x, off_y), and
* the pixels array is of size this_width*this_height.
*/
void apply(CachedBitmap output, int thread_index, int [] pixels, int off_x, int off_y, int this_width, int this_height);
/**
* @param pixels An array of pixels for the subset being operated on. I.e., pixels[0] represents the input pixel at (off_x, off_y), and
* the pixels array is of size 4*this_width*this_height.
*/
void apply(CachedBitmap output, int thread_index, byte [] pixels, int off_x, int off_y, int this_width, int this_height);
}
/** Encapsulates a Bitmap, but optimised for reading individual pixels.
* This differs to CachedBitmap in that FastAccessBitmap automatically decides which to cache,
* based on the requested pixels.
*/
static class FastAccessBitmap {
private final Bitmap bitmap;
private final int bitmap_width;
private final int cache_height;
private final int [] cache_pixels_i;
private int cache_y = -1;
FastAccessBitmap(Bitmap bitmap) {
this.bitmap = bitmap;
this.bitmap_width = bitmap.getWidth();
this.cache_height = Math.min(128, bitmap.getHeight());
this.cache_pixels_i = new int[bitmap_width*cache_height];
// better for performance to initialise the cache, rather than having to keep checking if it's initialised
cache(0);
}
private void cache(int y) {
/*if( MyDebug.LOG )
Log.d(TAG, ">>> cache: " + y + " [ " + this + " ]");*/
y = Math.max(0, y-4);
this.cache_y = Math.min(y, bitmap.getHeight()-cache_height);
this.bitmap.getPixels(cache_pixels_i, 0, bitmap_width, 0, cache_y, bitmap_width, cache_height);
}
int getPixel(int x, int y) {
if( y < cache_y || y >= cache_y+cache_height ) {
// update cache
cache(y);
}
// read from cache
return cache_pixels_i[(y-cache_y)*bitmap_width+x];
}
void ensureCache(int sy, int ey) {
if( ey - sy > cache_height ) {
throw new RuntimeException("can't cache this many rows: " + sy + " to " + ey + " vs cache_height: " + cache_height);
}
if( sy < cache_y || ey >= cache_y+cache_height ) {
cache(sy);
}
}
int getCacheY() {
return this.cache_y;
}
int [] getCachedPixelsI() {
return this.cache_pixels_i;
}
}
/** Encapsulates a Bitmap, together with caching of pixels.
* This differs to FastAccessBitmap in that CachedBitmap requires the caller to actually do the
* caching.
*/
public static class CachedBitmap {
private final Bitmap bitmap;
private final int [] cache_pixels_i;
private final byte [] cache_pixels_b;
CachedBitmap(Bitmap bitmap, int cache_width, int cache_height) {
this.bitmap = bitmap;
this.cache_pixels_i = new int[cache_width*cache_height];
this.cache_pixels_b = null;
}
int [] getCachedPixelsI() {
return this.cache_pixels_i;
}
byte [] getCachedPixelsB() {
return this.cache_pixels_b;
}
}
/** Generic thread to apply a Java function to a bunch of pixels.
*/
private static class ApplyFunctionThread extends Thread {
private final int thread_index;
private final ApplyFunctionInterface function;
private final CachedBitmap input;
private final int start_x, start_y, stop_x, stop_y;
private int chunk_size; // number of lines to process at a time
private CachedBitmap output; // optional
private int output_start_x, output_start_y;
private static int getChunkSize(int start_y, int stop_y) {
int height = stop_y - start_y;
//return height;
//return (int)Math.ceil(height/4.0);
//return Math.min(512, height);
return Math.min(64, height);
//return Math.min(32, height);
}
ApplyFunctionThread(int thread_index, ApplyFunctionInterface function, Bitmap bitmap, int start_x, int start_y, int stop_x, int stop_y) {
super("ApplyFunctionThread");
/*if( MyDebug.LOG ) {
Log.d(TAG, " thread_index: " + thread_index);
Log.d(TAG, " start_x: " + start_x);
Log.d(TAG, " start_y: " + start_y);
Log.d(TAG, " stop_x: " + stop_x);
Log.d(TAG, " stop_y: " + stop_y);
}*/
this.thread_index = thread_index;
this.function = function;
this.start_x = start_x;
this.start_y = start_y;
this.stop_x = stop_x;
this.stop_y = stop_y;
this.chunk_size = getChunkSize(start_y, stop_y);
/*if( MyDebug.LOG )
Log.d(TAG, " chunk_size: " + chunk_size);*/
if( bitmap != null )
this.input = new CachedBitmap(bitmap, stop_x-start_x, chunk_size);
else
this.input = null;
}
void setOutput(Bitmap bitmap, int output_start_x, int output_start_y) {
/*if( MyDebug.LOG ) {
Log.d(TAG, " output_start_x: " + output_start_x);
Log.d(TAG, " output_start_y: " + output_start_y);
}*/
this.output = new CachedBitmap(bitmap, stop_x-start_x, chunk_size);
this.output_start_x = output_start_x;
this.output_start_y = output_start_y;
}
public void run() {
/*if( MyDebug.LOG )
Log.d(TAG, "ApplyFunctionThread.run");*/
int width = stop_x-start_x;
int this_start_y = start_y;
int output_shift_y = output_start_y - start_y;
/*if( MyDebug.LOG ) {
Log.d(TAG, "start_y: " + start_y);
Log.d(TAG, "output_start_y: " + output_start_y);
Log.d(TAG, "output_shift_y: " + output_shift_y);
}*/
if( input == null && output == null ) {
this.chunk_size = stop_y-start_y;
/*if( MyDebug.LOG )
Log.d(TAG, "reset chunk_size to: " + chunk_size);*/
}
final int chunk_size_f = chunk_size;
while(this_start_y < stop_y) {
int this_stop_y = Math.min(this_start_y+chunk_size_f, stop_y);
int this_height = this_stop_y-this_start_y;
//if( MyDebug.LOG )
// Log.d(TAG, "chunks from " + this_start_y + " to " + this_stop_y);
//long time_s = System.currentTimeMillis();
if( input == null ) {
// nothing to copy to cache
function.apply(output, thread_index, start_x, this_start_y, width, this_height);
}
else if( input.bitmap != null ) {
input.bitmap.getPixels(input.cache_pixels_i, 0, width, start_x, this_start_y, width, this_height);
/*if( MyDebug.LOG )
Log.d(TAG, "### ApplyFunctionThread: time after reading pixels: " + (System.currentTimeMillis() - time_s));*/
function.apply(output, thread_index, input.cache_pixels_i, start_x, this_start_y, width, this_height);
}
/*if( MyDebug.LOG )
Log.d(TAG, "### ApplyFunctionThread: time after apply: " + (System.currentTimeMillis() - time_s));*/
if( output != null ) {
// write cached pixels back to output bitmap
if( output.bitmap != null ) {
/*if( MyDebug.LOG ) {
Log.d(TAG, "this_start_y: " + this_start_y);
Log.d(TAG, "output_shift_y: " + output_shift_y);
Log.d(TAG, "this_height: " + this_height);
Log.d(TAG, "height: " + output.bitmap.getHeight());
}*/
output.bitmap.setPixels(output.cache_pixels_i, 0, width, output_start_x, this_start_y+output_shift_y, width, this_height);
}
}
this_start_y = this_stop_y;
}
}
}
/** Applies a function to the specified pixels of the supplied bitmap.
*/
public static void applyFunction(ApplyFunctionInterface function, Bitmap bitmap, Bitmap output, int start_x, int start_y, int stop_x, int stop_y) {
applyFunction(function, bitmap, output, start_x, start_y, stop_x, stop_y, start_x, start_y);
}
/** Applies a function to the specified pixels of the supplied bitmap.
*/
static void applyFunction(ApplyFunctionInterface function, Bitmap bitmap, Bitmap output, int start_x, int start_y, int stop_x, int stop_y, int output_start_x, int output_start_y) {
if( MyDebug.LOG )
Log.d(TAG, "applyFunction [bitmap]");
long time_s = System.currentTimeMillis();
int height = stop_y-start_y;
if( MyDebug.LOG )
Log.d(TAG, "height: " + height);
//final int n_threads = 1;
final int n_threads = height >= 16 ? 4 : 1;
//final int n_threads = height >= 16 ? 8 : 1;
function.init(n_threads);
ApplyFunctionThread [] threads = new ApplyFunctionThread[n_threads];
int st_indx = 0;
for(int i=0;i<n_threads;i++) {
int nd_indx = (((i+1)*height)/n_threads);
/*if( MyDebug.LOG )
Log.d(TAG, "thread " + i + " from " + st_indx + " to " + nd_indx);*/
threads[i] = new ApplyFunctionThread(i, function, bitmap, start_x, start_y+st_indx, stop_x, start_y+nd_indx);
if( output != null )
threads[i].setOutput(output, output_start_x, output_start_y+st_indx);
st_indx = nd_indx;
}
if( MyDebug.LOG )
Log.d(TAG, "start threads");
for(int i=0;i<n_threads;i++) {
threads[i].start();
}
if( MyDebug.LOG )
Log.d(TAG, "wait for threads to complete");
try {
for(int i=0;i<n_threads;i++) {
threads[i].join();
}
}
catch(InterruptedException e) {
Log.e(TAG, "applyFunction threads interrupted");
throw new RuntimeException(e);
}
//function.init(1);
//ApplyFunctionThread thread = new ApplyFunctionThread(0, function, bitmap, start_x, start_y, stop_x, stop_y);
//thread.run();
if( MyDebug.LOG )
Log.d(TAG, "applyFunction time: " + (System.currentTimeMillis() - time_s));
}
}

View file

@ -0,0 +1,34 @@
package net.sourceforge.opencamera;
import static android.content.Context.KEYGUARD_SERVICE;
import android.app.Activity;
import android.app.KeyguardManager;
import android.os.Build;
import android.util.Log;
public class KeyguardUtils {
private static final String TAG = "KeyguardUtils";
public static void requireKeyguard(Activity activity, Runnable callback) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
KeyguardManager keyguardManager = (KeyguardManager) activity.getSystemService(KEYGUARD_SERVICE);
if (keyguardManager == null || !keyguardManager.isKeyguardLocked()) {
callback.run();
return;
}
keyguardManager.requestDismissKeyguard(activity, new KeyguardManager.KeyguardDismissCallback() {
@Override
public void onDismissSucceeded() {
if( MyDebug.LOG )
Log.d(TAG, "onDismissSucceeded");
callback.run();
if( MyDebug.LOG )
Log.d(TAG, "onDismissSucceeded: after callback run");
}
});
} else {
callback.run();
}
}
}

View file

@ -0,0 +1,357 @@
package net.sourceforge.opencamera;
import android.Manifest;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import android.util.Log;
/** Handles listening for GPS location (both coarse and fine).
*/
public class LocationSupplier {
private static final String TAG = "LocationSupplier";
private final Context context;
private final LocationManager locationManager;
private MyLocationListener [] locationListeners;
private volatile boolean test_force_no_location; // if true, always return null location; must be volatile for test project setting the state
private Location cached_location;
private long cached_location_ms;
LocationSupplier(Context context) {
this.context = context;
locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);
}
private Location getCachedLocation() {
if( cached_location != null ) {
long time_ms = System.currentTimeMillis();
if( time_ms <= cached_location_ms + 20000 ) {
return cached_location;
}
else {
cached_location = null;
}
}
return null;
}
/** Cache the current best location. Note that we intentionally call getLocation() from this
* method rather than passing it a location from onLocationChanged(), as we don't want a
* coarse location overriding a better fine location.
*/
private void cacheLocation() {
if( MyDebug.LOG )
Log.d(TAG, "cacheLocation");
Location location = getLocation();
if( location == null ) {
// this isn't an error as it can happen that we receive a call to onLocationChanged() after
// having freed the location listener (possibly because LocationManager had already queued
// a call to onLocationChanged?
// we should not set cached_location to null in such cases
Log.d(TAG, "### asked to cache location when location not available");
}
else {
cached_location = new Location(location);
cached_location_ms = System.currentTimeMillis();
}
}
public static class LocationInfo {
private boolean location_was_cached;
public boolean LocationWasCached() {
return location_was_cached;
}
}
/** If adding extra calls to this, consider whether explicit user permission is required, and whether
* privacy policy or data privacy section needs updating.
* @return Returns null if location not available.
*/
public Location getLocation() {
return getLocation(null);
}
/** If adding extra calls to this, consider whether explicit user permission is required, and whether
* privacy policy or data privacy section needs updating.
* @param locationInfo Optional class to return additional information about the location.
* @return Returns null if location not available.
*/
public Location getLocation(LocationInfo locationInfo) {
if( locationInfo != null )
locationInfo.location_was_cached = false; // init
if( locationListeners == null ) {
// if we have disabled location listening, then don't return a cached location anyway -
// in theory, callers should have already checked for user permission/setting before calling
// getLocation(), but just in case we didn't, don't want to return a cached location
return null;
}
if( test_force_no_location )
return null;
// location listeners should be stored in order best to worst
for(MyLocationListener locationListener : locationListeners) {
Location location = locationListener.getLocation();
if( location != null )
return location;
}
Location location = getCachedLocation();
if( location != null && locationInfo != null )
locationInfo.location_was_cached = true;
return location;
}
private class MyLocationListener implements LocationListener {
private Location location;
volatile boolean test_has_received_location; // must be volatile for test project reading the state
Location getLocation() {
return location;
}
public void onLocationChanged(@NonNull Location location) {
if( MyDebug.LOG )
Log.d(TAG, "onLocationChanged");
this.test_has_received_location = true;
// Android camera source claims we need to check lat/long != 0.0d
// also check for not being null just in case - had a nullpointerexception on Google Play!
if( location != null && ( location.getLatitude() != 0.0d || location.getLongitude() != 0.0d ) ) {
if( MyDebug.LOG ) {
Log.d(TAG, "received location");
// don't log location, in case of privacy!
}
this.location = location;
cacheLocation();
}
}
public void onStatusChanged(String provider, int status, Bundle extras) {
switch( status ) {
case LocationProvider.OUT_OF_SERVICE:
case LocationProvider.TEMPORARILY_UNAVAILABLE:
{
if( MyDebug.LOG ) {
if( status == LocationProvider.OUT_OF_SERVICE )
Log.d(TAG, "location provider out of service");
else if( status == LocationProvider.TEMPORARILY_UNAVAILABLE )
Log.d(TAG, "location provider temporarily unavailable");
}
this.location = null;
this.test_has_received_location = false;
cached_location = null;
break;
}
default:
break;
}
}
public void onProviderEnabled(@NonNull String provider) {
}
public void onProviderDisabled(@NonNull String provider) {
if( MyDebug.LOG )
Log.d(TAG, "onProviderDisabled");
this.location = null;
this.test_has_received_location = false;
cached_location = null;
}
}
/** Best to only call this from MainActivity.initLocation().
* @return Returns false if location permission not available for either coarse or fine.
* Important to only return false if we actually want/need to ask the user for location
* permission!
*/
boolean setupLocationListener() {
if( MyDebug.LOG )
Log.d(TAG, "setupLocationListener");
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
// Define a listener that responds to location updates
// we only set it up if store_location is true, important for privacy and unnecessary battery use
boolean store_location = sharedPreferences.getBoolean(PreferenceKeys.LocationPreferenceKey, false);
if( store_location && locationListeners == null ) {
// Note, ContextCompat.checkSelfPermission is meant to handle being called on any Android version, i.e., pre
// Android Marshmallow it should return true as permissions are set an installation, and can't be switched off by
// the user. However on Galaxy Nexus Android 4.3 and Nexus 7 (2013) Android 5.1.1, ACCESS_COARSE_LOCATION returns
// PERMISSION_DENIED! So we keep the checks to Android Marshmallow or later (where we need them), and avoid
// checking behaviour for earlier devices.
boolean has_coarse_location_permission;
boolean has_fine_location_permission;
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) {
if( MyDebug.LOG )
Log.d(TAG, "check for location permissions");
has_coarse_location_permission = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
has_fine_location_permission = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
if( MyDebug.LOG ) {
Log.d(TAG, "has_coarse_location_permission? " + has_coarse_location_permission);
Log.d(TAG, "has_fine_location_permission? " + has_fine_location_permission);
}
//has_coarse_location_permission = false; // test
//has_fine_location_permission = false; // test
// require at least one permission to be present
// will be important for Android 12+ where user can grant only coarse permission - we still
// want to support geotagging in such cases
if( !has_coarse_location_permission && !has_fine_location_permission ) {
if( MyDebug.LOG )
Log.d(TAG, "location permission not available");
// return false, which tells caller to request permission - we'll call this function again if permission is granted
return false;
}
}
else {
// permissions always available pre-Android 6
has_coarse_location_permission = true;
has_fine_location_permission = true;
}
locationListeners = new MyLocationListener[2];
locationListeners[0] = new MyLocationListener();
locationListeners[1] = new MyLocationListener();
// location listeners should be stored in order best to worst
// also see https://sourceforge.net/p/opencamera/tickets/1/ - need to check provider is available
// now also need to check for permissions - need to support devices that might have one but not both of fine and coarse permissions supplied
if( has_coarse_location_permission && locationManager.getAllProviders().contains(LocationManager.NETWORK_PROVIDER) ) {
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000, 0, locationListeners[1]);
if( MyDebug.LOG )
Log.d(TAG, "created coarse (network) location listener");
}
else {
if( MyDebug.LOG )
Log.d(TAG, "don't have a NETWORK_PROVIDER");
}
if( has_fine_location_permission && locationManager.getAllProviders().contains(LocationManager.GPS_PROVIDER) ) {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, locationListeners[0]);
if( MyDebug.LOG )
Log.d(TAG, "created fine (gps) location listener");
}
else {
if( MyDebug.LOG )
Log.d(TAG, "don't have a GPS_PROVIDER");
}
}
else if( !store_location ) {
freeLocationListeners();
}
// important to return true even if we didn't set up decide the location listeners - as
// returning false indicates to ask user for location permission (which we don't want to
// do if PreferenceKeys.LocationPreferenceKey preference isn't true)
return true;
}
void freeLocationListeners() {
if( MyDebug.LOG )
Log.d(TAG, "freeLocationListeners");
if( locationListeners != null ) {
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) {
// Android Lint claims we need location permission for LocationManager.removeUpdates().
// also see http://stackoverflow.com/questions/32715189/location-manager-remove-updates-permission
if( MyDebug.LOG )
Log.d(TAG, "check for location permissions");
boolean has_coarse_location_permission = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
boolean has_fine_location_permission = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
if( MyDebug.LOG ) {
Log.d(TAG, "has_coarse_location_permission? " + has_coarse_location_permission);
Log.d(TAG, "has_fine_location_permission? " + has_fine_location_permission);
}
// require at least one permission to be present
if( !has_coarse_location_permission && !has_fine_location_permission ) {
if( MyDebug.LOG )
Log.d(TAG, "location permission not available");
return;
}
}
for(int i=0;i<locationListeners.length;i++) {
locationManager.removeUpdates(locationListeners[i]);
locationListeners[i] = null;
}
locationListeners = null;
if( MyDebug.LOG )
Log.d(TAG, "location listeners now freed");
}
}
// for testing:
public boolean testHasReceivedLocation() {
if( locationListeners == null )
return false;
for(MyLocationListener locationListener : locationListeners) {
if( locationListener.test_has_received_location )
return true;
}
return false;
}
public void setForceNoLocation(boolean test_force_no_location) {
this.test_force_no_location = test_force_no_location;
}
/** Use this when we want to test (assert) that location listeners are turned on.
* If we want to assert that they are turned off, then use noLocationListeners.
*/
public boolean hasLocationListeners() {
if( this.locationListeners == null )
return false;
if( this.locationListeners.length != 2 )
return false;
for(MyLocationListener locationListener : locationListeners) {
if( locationListener == null )
return false;
}
return true;
}
/** Use this when we want to test (assert) that location listeners are turned on. Note that this
* is NOT an inverse of hasLocationListeners. For example this means that if
* locationListeners.length==1, hasLocationListeners would return false (so we'd flag up that
* we've not set them up correctly), but noLocationListeners would also return false (to flag
* up that we did set some location listeners up).
*/
public boolean noLocationListeners() {
if( this.locationListeners == null )
return true;
return false;
}
public static String locationToDMS(double coord) {
String sign = (coord < 0.0) ? "-" : "";
coord = Math.abs(coord);
int intPart = (int)coord;
boolean is_zero = (intPart==0);
String degrees = String.valueOf(intPart);
double mod = coord - intPart;
coord = mod * 60;
intPart = (int)coord;
is_zero = is_zero && (intPart==0);
mod = coord - intPart;
String minutes = String.valueOf(intPart);
coord = mod * 60;
intPart = (int)coord;
is_zero = is_zero && (intPart==0);
String seconds = String.valueOf(intPart);
if( is_zero ) {
// so we don't show -ve for coord that is -ve but smaller than 1"
sign = "";
}
// use unicode rather than degrees symbol, due to Android Studio warning - see https://sourceforge.net/p/opencamera/tickets/107/
return sign + degrees + "\u00b0" + minutes + "'" + seconds + "\"";
}
}

View file

@ -0,0 +1,216 @@
package net.sourceforge.opencamera;
import android.app.AlertDialog;
import android.content.SharedPreferences;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.preference.PreferenceManager;
import android.util.Log;
/** Handles magnetic sensor.
*/
class MagneticSensor {
private static final String TAG = "MagneticSensor";
private final MainActivity main_activity;
private Sensor mSensorMagnetic;
private int magnetic_accuracy = -1;
private AlertDialog magnetic_accuracy_dialog;
private boolean magneticListenerIsRegistered;
MagneticSensor(final MainActivity main_activity) {
this.main_activity = main_activity;
}
void initSensor(final SensorManager mSensorManager) {
if( MyDebug.LOG )
Log.d(TAG, "initSensor");
if( mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD) != null ) {
if( MyDebug.LOG )
Log.d(TAG, "found magnetic sensor");
mSensorMagnetic = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
}
else {
if( MyDebug.LOG )
Log.d(TAG, "no support for magnetic sensor");
}
}
/** Registers the magnetic sensor, only if it's required (by user preferences), and hasn't already
* been registered.
* If the magnetic sensor was previously registered, but is no longer required by user preferences,
* then it is unregistered.
*/
void registerMagneticListener(final SensorManager mSensorManager) {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(main_activity);
if( !magneticListenerIsRegistered ) {
if( needsMagneticSensor(sharedPreferences) ) {
if( MyDebug.LOG )
Log.d(TAG, "register magneticListener");
mSensorManager.registerListener(magneticListener, mSensorMagnetic, SensorManager.SENSOR_DELAY_NORMAL);
magneticListenerIsRegistered = true;
}
else {
if( MyDebug.LOG )
Log.d(TAG, "don't register magneticListener as not needed");
}
}
else {
if( needsMagneticSensor(sharedPreferences) ) {
if( MyDebug.LOG )
Log.d(TAG, "magneticListener already registered");
}
else {
if( MyDebug.LOG )
Log.d(TAG, "magneticListener already registered but no longer needed");
mSensorManager.unregisterListener(magneticListener);
magneticListenerIsRegistered = false;
}
}
}
/** Unregisters the magnetic sensor, if it was registered.
*/
void unregisterMagneticListener(final SensorManager mSensorManager) {
if( magneticListenerIsRegistered ) {
if( MyDebug.LOG )
Log.d(TAG, "unregister magneticListener");
mSensorManager.unregisterListener(magneticListener);
magneticListenerIsRegistered = false;
}
else {
if( MyDebug.LOG )
Log.d(TAG, "magneticListener wasn't registered");
}
}
private final SensorEventListener magneticListener = new SensorEventListener() {
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
if( MyDebug.LOG )
Log.d(TAG, "magneticListener.onAccuracyChanged: " + accuracy);
//accuracy = SensorManager.SENSOR_STATUS_ACCURACY_LOW; // test
MagneticSensor.this.magnetic_accuracy = accuracy;
setMagneticAccuracyDialogText(); // update if a dialog is already open for this
checkMagneticAccuracy();
// test accuracy changing after dialog opened:
/*Handler handler = new Handler();
handler.postDelayed(new Runnable() {
public void run() {
MainActivity.this.magnetic_accuracy = SensorManager.SENSOR_STATUS_ACCURACY_HIGH;
setMagneticAccuracyDialogText();
checkMagneticAccuracy();
}
}, 5000);*/
}
@Override
public void onSensorChanged(SensorEvent event) {
main_activity.getPreview().onMagneticSensorChanged(event);
}
};
private void setMagneticAccuracyDialogText() {
if( MyDebug.LOG )
Log.d(TAG, "setMagneticAccuracyDialogText()");
if( magnetic_accuracy_dialog != null ) {
String message = main_activity.getResources().getString(R.string.magnetic_accuracy_info) + " ";
switch( magnetic_accuracy ) {
case SensorManager.SENSOR_STATUS_UNRELIABLE:
message += main_activity.getResources().getString(R.string.accuracy_unreliable);
break;
case SensorManager.SENSOR_STATUS_ACCURACY_LOW:
message += main_activity.getResources().getString(R.string.accuracy_low);
break;
case SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM:
message += main_activity.getResources().getString(R.string.accuracy_medium);
break;
case SensorManager.SENSOR_STATUS_ACCURACY_HIGH:
message += main_activity.getResources().getString(R.string.accuracy_high);
break;
default:
message += main_activity.getResources().getString(R.string.accuracy_unknown);
break;
}
if( MyDebug.LOG )
Log.d(TAG, "message: " + message);
magnetic_accuracy_dialog.setMessage(message);
}
}
private boolean shown_magnetic_accuracy_dialog = false; // whether the dialog for poor magnetic accuracy has been shown since application start
/** Checks whether the user should be informed about poor magnetic sensor accuracy, and shows
* the dialog if so.
*/
void checkMagneticAccuracy() {
if( MyDebug.LOG )
Log.d(TAG, "checkMagneticAccuracy(): " + magnetic_accuracy);
if( magnetic_accuracy != SensorManager.SENSOR_STATUS_UNRELIABLE && magnetic_accuracy != SensorManager.SENSOR_STATUS_ACCURACY_LOW ) {
if( MyDebug.LOG )
Log.d(TAG, "accuracy is good enough (or accuracy not yet known)");
}
else if( shown_magnetic_accuracy_dialog ) {
// if we've shown the dialog since application start, then don't show again even if the user didn't click to not show again
if( MyDebug.LOG )
Log.d(TAG, "already shown_magnetic_accuracy_dialog");
}
else if( main_activity.getPreview().isTakingPhotoOrOnTimer() || main_activity.getPreview().isVideoRecording() ) {
if( MyDebug.LOG )
Log.d(TAG, "don't disturb whilst taking photo, on timer, or recording video");
}
else if( main_activity.isCameraInBackground() ) {
if( MyDebug.LOG )
Log.d(TAG, "don't show magnetic accuracy dialog due to camera in background");
// don't want to show dialog if another is open, or in settings, etc
}
else {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(main_activity);
if( !needsMagneticSensor(sharedPreferences) ) {
if( MyDebug.LOG )
Log.d(TAG, "don't need magnetic sensor");
// note, we shouldn't set shown_magnetic_accuracy_dialog to true here, otherwise we won't pick up if the user enables one of these options
}
else if( sharedPreferences.contains(PreferenceKeys.MagneticAccuracyPreferenceKey) ) {
if( MyDebug.LOG )
Log.d(TAG, "user selected to no longer show the dialog");
shown_magnetic_accuracy_dialog = true; // also set this flag, so future calls to checkMagneticAccuracy() will exit without needing to get/read the SharedPreferences
}
else {
if( MyDebug.LOG )
Log.d(TAG, "show dialog for magnetic accuracy");
shown_magnetic_accuracy_dialog = true;
magnetic_accuracy_dialog = main_activity.getMainUI().showInfoDialog(R.string.magnetic_accuracy_title, 0, PreferenceKeys.MagneticAccuracyPreferenceKey);
setMagneticAccuracyDialogText();
}
}
}
/* Whether the user preferences indicate that we need the magnetic sensor to be enabled.
*/
private boolean needsMagneticSensor(SharedPreferences sharedPreferences) {
if( main_activity.getApplicationInterface().getGeodirectionPref() ||
sharedPreferences.getBoolean(PreferenceKeys.AddYPRToComments, false) ||
sharedPreferences.getBoolean(PreferenceKeys.ShowGeoDirectionLinesPreferenceKey, false) ||
sharedPreferences.getBoolean(PreferenceKeys.ShowGeoDirectionPreferenceKey, false) ) {
return true;
}
return false;
}
int getMagneticAccuracy() {
return this.magnetic_accuracy;
}
void clearDialog() {
this.magnetic_accuracy_dialog = null;
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,106 @@
package net.sourceforge.opencamera;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
/** Handles the audio "noise" trigger option.
*/
public class MyAudioTriggerListenerCallback implements AudioListener.AudioListenerCallback {
private static final String TAG = "MyAudioTriggerLstnrCb";
private final MainActivity main_activity;
private int last_level = -1;
private long time_quiet_loud = -1;
private long time_last_audio_trigger_photo = -1;
private int audio_noise_sensitivity = -1;
MyAudioTriggerListenerCallback(MainActivity main_activity) {
this.main_activity = main_activity;
}
void setAudioNoiseSensitivity(int audio_noise_sensitivity) {
this.audio_noise_sensitivity = audio_noise_sensitivity;
}
/** Listens to audio noise and decides when there's been a "loud" noise to trigger taking a photo.
*/
@Override
public void onAudio(int level) {
boolean audio_trigger = false;
/*if( level > 150 ) {
if( MyDebug.LOG )
Log.d(TAG, "loud noise!: " + level);
audio_trigger = true;
}*/
if( last_level == -1 ) {
last_level = level;
return;
}
int diff = level - last_level;
if( MyDebug.LOG ) {
Log.d(TAG, "noise_sensitivity: " + audio_noise_sensitivity);
Log.d(TAG, "diff: " + diff);
}
if( diff > audio_noise_sensitivity ) {
if( MyDebug.LOG )
Log.d(TAG, "got louder!: " + last_level + " to " + level + " , diff: " + diff);
time_quiet_loud = System.currentTimeMillis();
if( MyDebug.LOG )
Log.d(TAG, " time: " + time_quiet_loud);
}
else if( diff < -audio_noise_sensitivity && time_quiet_loud != -1 ) {
if( MyDebug.LOG )
Log.d(TAG, "got quieter!: " + last_level + " to " + level + " , diff: " + diff);
long time_now = System.currentTimeMillis();
long duration = time_now - time_quiet_loud;
if( MyDebug.LOG ) {
Log.d(TAG, "stopped being loud - was loud since: " + time_quiet_loud);
Log.d(TAG, " time_now: " + time_now);
Log.d(TAG, " duration: " + duration);
}
if( duration < 1500 ) {
if( MyDebug.LOG )
Log.d(TAG, "audio_trigger set");
audio_trigger = true;
}
time_quiet_loud = -1;
}
else {
if( MyDebug.LOG )
Log.d(TAG, "audio level: " + last_level + " to " + level + " , diff: " + diff);
}
last_level = level;
if( audio_trigger ) {
if( MyDebug.LOG )
Log.d(TAG, "audio trigger");
// need to run on UI thread so that this function returns quickly (otherwise we'll have lag in processing the audio)
// but also need to check we're not currently taking a photo or on timer, so we don't repeatedly queue up takePicture() calls, or cancel a timer
long time_now = System.currentTimeMillis();
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(main_activity);
boolean want_audio_listener = sharedPreferences.getString(PreferenceKeys.AudioControlPreferenceKey, "none").equals("noise");
if( time_last_audio_trigger_photo != -1 && time_now - time_last_audio_trigger_photo < 5000 ) {
// avoid risk of repeatedly being triggered - as well as problem of being triggered again by the camera's own "beep"!
if( MyDebug.LOG )
Log.d(TAG, "ignore loud noise due to too soon since last audio triggered photo: " + (time_now - time_last_audio_trigger_photo));
}
else if( !want_audio_listener ) {
// just in case this is a callback from an AudioListener before it's been freed (e.g., if there's a loud noise when exiting settings after turning the option off
if( MyDebug.LOG )
Log.d(TAG, "ignore loud noise due to audio listener option turned off");
}
else {
if( MyDebug.LOG )
Log.d(TAG, "audio trigger from loud noise");
time_last_audio_trigger_photo = time_now;
main_activity.audioTrigger();
}
}
}
}

View file

@ -0,0 +1,21 @@
package net.sourceforge.opencamera;
import android.util.Log;
/** Helper class for logging.
*/
public class MyDebug {
/** Global constant to control logging, should always be set to false in
* released versions.
*/
public static final boolean LOG = false;
/** Wrapper to print exceptions, should use instead of e.printStackTrace().
*/
public static void logStackTrace(String tag, String msg, Throwable tr) {
if( LOG ) {
// don't log exceptions in releases
Log.e(tag, msg, tr);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,63 @@
package net.sourceforge.opencamera;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Build;
import android.service.quicksettings.TileService;
import androidx.annotation.RequiresApi;
import android.util.Log;
/** Provides service for quick settings tile.
*/
@RequiresApi(api = Build.VERSION_CODES.N)
public class MyTileService extends TileService {
private static final String TAG = "MyTileService";
public static final String TILE_ID = "net.sourceforge.opencamera.TILE_CAMERA";
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public void onTileAdded() {
super.onTileAdded();
}
@Override
public void onTileRemoved() {
super.onTileRemoved();
}
@Override
public void onStartListening() {
super.onStartListening();
}
@Override
public void onStopListening() {
super.onStopListening();
}
@Override
public void onClick() {
if( MyDebug.LOG )
Log.d(TAG, "onClick");
super.onClick();
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setAction(TILE_ID);
// use startActivityAndCollapse() instead of startActivity() so that the notification panel doesn't remain pulled down
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE ) {
// startActivityAndCollapse(Intent) throws UnsupportedOperationException on Android 14+
// FLAG_IMMUTABLE needed for PendingIntents on Android 12+
PendingIntent pending_intent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
startActivityAndCollapse(pending_intent);
}
else {
// still get warning for startActivityAndCollapse being deprecated, but startActivityAndCollapse(PendingIntent) requires Android 14+
// and only seems possible to disable the warning for the function, not this statement
startActivityAndCollapse(intent);
}
}
}

View file

@ -0,0 +1,63 @@
package net.sourceforge.opencamera;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Build;
import android.service.quicksettings.TileService;
import androidx.annotation.RequiresApi;
import android.util.Log;
/** Provides service for quick settings tile.
*/
@RequiresApi(api = Build.VERSION_CODES.N)
public class MyTileServiceFrontCamera extends TileService {
private static final String TAG = "MyTileServiceFrontCam";
public static final String TILE_ID = "net.sourceforge.opencamera.TILE_FRONT_CAMERA";
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public void onTileAdded() {
super.onTileAdded();
}
@Override
public void onTileRemoved() {
super.onTileRemoved();
}
@Override
public void onStartListening() {
super.onStartListening();
}
@Override
public void onStopListening() {
super.onStopListening();
}
@Override
public void onClick() {
if( MyDebug.LOG )
Log.d(TAG, "onClick");
super.onClick();
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setAction(TILE_ID);
// use startActivityAndCollapse() instead of startActivity() so that the notification panel doesn't remain pulled down
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE ) {
// startActivityAndCollapse(Intent) throws UnsupportedOperationException on Android 14+
// FLAG_IMMUTABLE needed for PendingIntents on Android 12+
PendingIntent pending_intent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
startActivityAndCollapse(pending_intent);
}
else {
// still get warning for startActivityAndCollapse being deprecated, but startActivityAndCollapse(PendingIntent) requires Android 14+
// and only seems possible to disable the warning for the function, not this statement
startActivityAndCollapse(intent);
}
}
}

View file

@ -0,0 +1,63 @@
package net.sourceforge.opencamera;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Build;
import android.service.quicksettings.TileService;
import androidx.annotation.RequiresApi;
import android.util.Log;
/** Provides service for quick settings tile.
*/
@RequiresApi(api = Build.VERSION_CODES.N)
public class MyTileServiceVideo extends TileService {
private static final String TAG = "MyTileServiceVideo";
public static final String TILE_ID = "net.sourceforge.opencamera.TILE_VIDEO";
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public void onTileAdded() {
super.onTileAdded();
}
@Override
public void onTileRemoved() {
super.onTileRemoved();
}
@Override
public void onStartListening() {
super.onStartListening();
}
@Override
public void onStopListening() {
super.onStopListening();
}
@Override
public void onClick() {
if( MyDebug.LOG )
Log.d(TAG, "onClick");
super.onClick();
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setAction(TILE_ID);
// use startActivityAndCollapse() instead of startActivity() so that the notification panel doesn't remain pulled down
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE ) {
// startActivityAndCollapse(Intent) throws UnsupportedOperationException on Android 14+
// FLAG_IMMUTABLE needed for PendingIntents on Android 12+
PendingIntent pending_intent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
startActivityAndCollapse(pending_intent);
}
else {
// still get warning for startActivityAndCollapse being deprecated, but startActivityAndCollapse(PendingIntent) requires Android 14+
// and only seems possible to disable the warning for the function, not this statement
startActivityAndCollapse(intent);
}
}
}

View file

@ -0,0 +1,59 @@
package net.sourceforge.opencamera;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
import android.widget.RemoteViews;
/** Handles the Open Camera "take photo" widget. This widget launches Open
* Camera, and immediately takes a photo.
*/
public class MyWidgetProviderTakePhoto extends AppWidgetProvider {
private static final String TAG = "MyWidgetProviderTakePho";
// see http://developer.android.com/guide/topics/appwidgets/index.html
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int [] appWidgetIds) {
if( MyDebug.LOG )
Log.d(TAG, "onUpdate");
if( MyDebug.LOG )
Log.d(TAG, "length = " + appWidgetIds.length);
for(int appWidgetId : appWidgetIds) {
if( MyDebug.LOG )
Log.d(TAG, "appWidgetId: " + appWidgetId);
Intent intent = new Intent(context, TakePhoto.class);
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.M )
flags = flags | PendingIntent.FLAG_IMMUTABLE; // needed for targetting Android 12+, but fine to set it all versions from Android 6 onwards
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, flags);
RemoteViews remote_views = new RemoteViews(context.getPackageName(), R.layout.widget_layout_take_photo);
remote_views.setOnClickPendingIntent(R.id.widget_take_photo, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, remote_views);
}
}
/*@Override
public void onReceive(Context context, Intent intent) {
if( MyDebug.LOG ) {
Log.d(TAG, "onReceive " + intent);
}
if (intent.getAction().equals("net.sourceforge.opencamera.LAUNCH_OPEN_CAMERA")) {
if( MyDebug.LOG )
Log.d(TAG, "Launching MainActivity");
final Intent activity = new Intent(context, MainActivity.class);
activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(activity);
if( MyDebug.LOG )
Log.d(TAG, "done");
}
super.onReceive(context, intent);
}*/
}

View file

@ -0,0 +1,30 @@
package net.sourceforge.opencamera;
import android.app.Application;
import android.os.Process;
import android.util.Log;
/** We override the Application class to implement the workaround at
* https://issuetracker.google.com/issues/36972466#comment14 for Google bug crash. It seems ugly,
* but Google consider this a low priority despite calling these "bad behaviours" in applications!
*/
public class OpenCameraApplication extends Application {
private static final String TAG = "OpenCameraApplication";
@Override
public void onCreate() {
if( MyDebug.LOG )
Log.d(TAG, "onCreate");
super.onCreate();
checkAppReplacingState();
}
private void checkAppReplacingState() {
if( MyDebug.LOG )
Log.d(TAG, "checkAppReplacingState");
if( getResources() == null ) {
Log.e(TAG, "app is replacing, kill");
Process.killProcess(Process.myPid());
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,20 @@
package net.sourceforge.opencamera;
/** Exception for PanoramaProcessor class.
*/
@SuppressWarnings("WeakerAccess")
public class PanoramaProcessorException extends Exception {
final static public int INVALID_N_IMAGES = 0; // the supplied number of images is not supported
final static public int UNEQUAL_SIZES = 1; // images not of the same resolution
final static public int FAILED_TO_CROP = 1; // failed to crop
final private int code;
PanoramaProcessorException(int code) {
this.code = code;
}
public int getCode() {
return code;
}
}

View file

@ -0,0 +1,354 @@
package net.sourceforge.opencamera;
import android.Manifest;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.preference.PreferenceManager;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import android.util.Log;
/** Android 6+ permission handling:
*/
public class PermissionHandler {
private static final String TAG = "PermissionHandler";
private final MainActivity main_activity;
final private static int MY_PERMISSIONS_REQUEST_CAMERA = 0;
final private static int MY_PERMISSIONS_REQUEST_STORAGE = 1;
final private static int MY_PERMISSIONS_REQUEST_RECORD_AUDIO = 2;
final private static int MY_PERMISSIONS_REQUEST_LOCATION = 3;
private boolean camera_denied; // whether the user requested to deny a camera permission
private long camera_denied_time_ms; // if denied, the time when this occurred
private boolean storage_denied; // whether the user requested to deny a camera permission
private long storage_denied_time_ms; // if denied, the time when this occurred
private boolean audio_denied; // whether the user requested to deny a camera permission
private long audio_denied_time_ms; // if denied, the time when this occurred
private boolean location_denied; // whether the user requested to deny a camera permission
private long location_denied_time_ms; // if denied, the time when this occurred
// In some cases there can be a problem if the user denies a permission, we then get an onResume()
// (since application goes into background when showing system UI to request permission) at which
// point we try to request permission again! This would happen for camera and storage permissions.
// Whilst that isn't necessarily wrong, there would also be a problem if the user says
// "Don't ask again", we get stuck in a loop repeatedly asking the OS for permission (and it
// repeatedly being automatically denied) causing the UI to become sluggish.
// So instead we only try asking again if not within deny_delay_ms of the user denying that
// permission.
// Time shouldn't be too long, as the user might restart and then not be asked again for camera
// or storage permission.
final private static long deny_delay_ms = 1000;
PermissionHandler(MainActivity main_activity) {
this.main_activity = main_activity;
}
/** Show a "rationale" to the user for needing a particular permission, then request that permission again
* once they close the dialog.
*/
private void showRequestPermissionRationale(final int permission_code) {
if( MyDebug.LOG )
Log.d(TAG, "showRequestPermissionRational: " + permission_code);
if( Build.VERSION.SDK_INT < Build.VERSION_CODES.M ) {
if( MyDebug.LOG )
Log.e(TAG, "shouldn't be requesting permissions for pre-Android M!");
return;
}
boolean ok = true;
String [] permissions = null;
int message_id = 0;
switch (permission_code) {
case MY_PERMISSIONS_REQUEST_CAMERA:
if (MyDebug.LOG)
Log.d(TAG, "display rationale for camera permission");
permissions = new String[]{Manifest.permission.CAMERA};
message_id = R.string.permission_rationale_camera;
break;
case MY_PERMISSIONS_REQUEST_STORAGE:
if (MyDebug.LOG)
Log.d(TAG, "display rationale for storage permission");
permissions = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE};
message_id = R.string.permission_rationale_storage;
break;
case MY_PERMISSIONS_REQUEST_RECORD_AUDIO:
if (MyDebug.LOG)
Log.d(TAG, "display rationale for record audio permission");
permissions = new String[]{Manifest.permission.RECORD_AUDIO};
message_id = R.string.permission_rationale_record_audio;
break;
case MY_PERMISSIONS_REQUEST_LOCATION:
if (MyDebug.LOG)
Log.d(TAG, "display rationale for location permission");
permissions = new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
message_id = R.string.permission_rationale_location;
break;
default:
if (MyDebug.LOG)
Log.e(TAG, "showRequestPermissionRational unknown permission_code: " + permission_code);
ok = false;
break;
}
if( ok ) {
final String [] permissions_f = permissions;
new AlertDialog.Builder(main_activity)
.setTitle(R.string.permission_rationale_title)
.setMessage(message_id)
.setIcon(android.R.drawable.ic_dialog_alert)
.setPositiveButton(android.R.string.ok, null)
.setOnDismissListener(new DialogInterface.OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
if( MyDebug.LOG )
Log.d(TAG, "requesting permission...");
ActivityCompat.requestPermissions(main_activity, permissions_f, permission_code);
}
}).show();
}
}
void requestCameraPermission() {
if( MyDebug.LOG )
Log.d(TAG, "requestCameraPermission");
if( Build.VERSION.SDK_INT < Build.VERSION_CODES.M ) {
if( MyDebug.LOG )
Log.e(TAG, "shouldn't be requesting permissions for pre-Android M!");
return;
}
else if( camera_denied && System.currentTimeMillis() < camera_denied_time_ms + deny_delay_ms ) {
if( MyDebug.LOG )
Log.d(TAG, "too soon since user last denied permission");
return;
}
if( ActivityCompat.shouldShowRequestPermissionRationale(main_activity, Manifest.permission.CAMERA) ) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
showRequestPermissionRationale(MY_PERMISSIONS_REQUEST_CAMERA);
}
else {
// Can go ahead and request the permission
if( MyDebug.LOG )
Log.d(TAG, "requesting camera permission...");
ActivityCompat.requestPermissions(main_activity, new String[]{Manifest.permission.CAMERA}, MY_PERMISSIONS_REQUEST_CAMERA);
}
}
void requestStoragePermission() {
if( MyDebug.LOG )
Log.d(TAG, "requestStoragePermission");
if( Build.VERSION.SDK_INT < Build.VERSION_CODES.M ) {
if( MyDebug.LOG )
Log.e(TAG, "shouldn't be requesting permissions for pre-Android M!");
return;
}
else if( MainActivity.useScopedStorage() ) {
if( MyDebug.LOG )
Log.e(TAG, "shouldn't be requesting permissions for scoped storage!");
return;
}
else if( storage_denied && System.currentTimeMillis() < storage_denied_time_ms + deny_delay_ms ) {
if( MyDebug.LOG )
Log.d(TAG, "too soon since user last denied permission");
return;
}
if( ActivityCompat.shouldShowRequestPermissionRationale(main_activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) ) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
showRequestPermissionRationale(MY_PERMISSIONS_REQUEST_STORAGE);
}
else {
// Can go ahead and request the permission
if( MyDebug.LOG )
Log.d(TAG, "requesting storage permission...");
ActivityCompat.requestPermissions(main_activity, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_STORAGE);
}
}
void requestRecordAudioPermission() {
if( MyDebug.LOG )
Log.d(TAG, "requestRecordAudioPermission");
if( Build.VERSION.SDK_INT < Build.VERSION_CODES.M ) {
if( MyDebug.LOG )
Log.e(TAG, "shouldn't be requesting permissions for pre-Android M!");
return;
}
else if( audio_denied && System.currentTimeMillis() < audio_denied_time_ms + deny_delay_ms ) {
if( MyDebug.LOG )
Log.d(TAG, "too soon since user last denied permission");
return;
}
if( ActivityCompat.shouldShowRequestPermissionRationale(main_activity, Manifest.permission.RECORD_AUDIO) ) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
showRequestPermissionRationale(MY_PERMISSIONS_REQUEST_RECORD_AUDIO);
}
else {
// Can go ahead and request the permission
if( MyDebug.LOG )
Log.d(TAG, "requesting record audio permission...");
ActivityCompat.requestPermissions(main_activity, new String[]{Manifest.permission.RECORD_AUDIO}, MY_PERMISSIONS_REQUEST_RECORD_AUDIO);
}
}
void requestLocationPermission() {
if( MyDebug.LOG )
Log.d(TAG, "requestLocationPermission");
if( Build.VERSION.SDK_INT < Build.VERSION_CODES.M ) {
if( MyDebug.LOG )
Log.e(TAG, "shouldn't be requesting permissions for pre-Android M!");
return;
}
else if( location_denied && System.currentTimeMillis() < location_denied_time_ms + deny_delay_ms ) {
if( MyDebug.LOG )
Log.d(TAG, "too soon since user last denied permission");
return;
}
if( ActivityCompat.shouldShowRequestPermissionRationale(main_activity, Manifest.permission.ACCESS_FINE_LOCATION) ||
ActivityCompat.shouldShowRequestPermissionRationale(main_activity, Manifest.permission.ACCESS_COARSE_LOCATION) ) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
showRequestPermissionRationale(MY_PERMISSIONS_REQUEST_LOCATION);
}
else {
// Can go ahead and request the permission
if( MyDebug.LOG )
Log.d(TAG, "requesting location permissions...");
ActivityCompat.requestPermissions(main_activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, MY_PERMISSIONS_REQUEST_LOCATION);
}
}
public void onRequestPermissionsResult(int requestCode, @NonNull int[] grantResults) {
if( MyDebug.LOG )
Log.d(TAG, "onRequestPermissionsResult: requestCode " + requestCode);
if( Build.VERSION.SDK_INT < Build.VERSION_CODES.M ) {
if( MyDebug.LOG )
Log.e(TAG, "shouldn't be requesting permissions for pre-Android M!");
return;
}
switch( requestCode ) {
case MY_PERMISSIONS_REQUEST_CAMERA:
{
// If request is cancelled, the result arrays are empty.
if( grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED ) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
if( MyDebug.LOG )
Log.d(TAG, "camera permission granted");
main_activity.getPreview().retryOpenCamera();
}
else {
if( MyDebug.LOG )
Log.d(TAG, "camera permission denied");
camera_denied = true;
camera_denied_time_ms = System.currentTimeMillis();
// permission denied, boo! Disable the
// functionality that depends on this permission.
// Open Camera doesn't need to do anything: the camera will remain closed
}
return;
}
case MY_PERMISSIONS_REQUEST_STORAGE:
{
// If request is cancelled, the result arrays are empty.
if( grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED ) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
if( MyDebug.LOG )
Log.d(TAG, "storage permission granted");
main_activity.getPreview().retryOpenCamera();
}
else {
if( MyDebug.LOG )
Log.d(TAG, "storage permission denied");
storage_denied = true;
storage_denied_time_ms = System.currentTimeMillis();
// permission denied, boo! Disable the
// functionality that depends on this permission.
// Open Camera doesn't need to do anything: the camera will remain closed
}
return;
}
case MY_PERMISSIONS_REQUEST_RECORD_AUDIO:
{
// If request is cancelled, the result arrays are empty.
if( grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED ) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
if( MyDebug.LOG )
Log.d(TAG, "record audio permission granted");
// no need to do anything
}
else {
if( MyDebug.LOG )
Log.d(TAG, "record audio permission denied");
audio_denied = true;
audio_denied_time_ms = System.currentTimeMillis();
// permission denied, boo! Disable the
// functionality that depends on this permission.
// no need to do anything
// note that we don't turn off record audio option, as user may then record video not realising audio won't be recorded - best to be explicit each time
}
return;
}
case MY_PERMISSIONS_REQUEST_LOCATION:
{
// If request is cancelled, the result arrays are empty.
if( grantResults.length == 2 && (grantResults[0] == PackageManager.PERMISSION_GRANTED || grantResults[1] == PackageManager.PERMISSION_GRANTED) ) {
// On Android 12 users can choose to only grant approximation location. This means
// one of the permissions will be denied, but as long as one location permission
// is granted, we can still go ahead and use location.
// Otherwise we have a problem that if user selects approximate location, we end
// up turning the location option back off.
if( MyDebug.LOG )
Log.d(TAG, "location permission granted [1]");
main_activity.initLocation();
}
else if( grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED ) {
// in theory this code path is now redundant, but keep here just in case
if( MyDebug.LOG )
Log.d(TAG, "location permission granted [2]");
main_activity.initLocation();
}
else {
if( MyDebug.LOG )
Log.d(TAG, "location permission denied");
location_denied = true;
location_denied_time_ms = System.currentTimeMillis();
// permission denied, boo! Disable the
// functionality that depends on this permission.
// for location, seems best to turn the option back off
if( MyDebug.LOG )
Log.d(TAG, "location permission not available, so switch location off");
main_activity.getPreview().showToast(null, R.string.permission_location_not_available);
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(main_activity);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean(PreferenceKeys.LocationPreferenceKey, false);
editor.apply();
}
return;
}
default:
{
if( MyDebug.LOG )
Log.e(TAG, "unknown requestCode " + requestCode);
}
}
}
}

View file

@ -0,0 +1,386 @@
package net.sourceforge.opencamera;
/** Stores all of the string keys used for SharedPreferences.
*/
public class PreferenceKeys {
// must be static, to safely call from other Activities
/** If this preference is set, no longer show the intro dialog.
*/
public static final String FirstTimePreferenceKey = "done_first_time";
/** This preference stores the version number seen by the user - used to show "What's New" dialog.
*/
public static final String LatestVersionPreferenceKey = "latest_version";
/** This preference stores whether to allow showing the "What's New" dialog.
*/
public static final String ShowWhatsNewPreferenceKey = "preference_show_whats_new";
/** If this preference is set, no longer show the auto-stabilise info dialog.
*/
public static final String AutoStabiliseInfoPreferenceKey = "done_auto_stabilise_info";
/** If this preference is set, no longer show the HDR info dialog.
*/
public static final String HDRInfoPreferenceKey = "done_hdr_info";
/** If this preference is set, no longer show the Panorama info dialog.
*/
public static final String PanoramaInfoPreferenceKey = "done_panorama_info";
/** If this preference is set, no longer show the raw info dialog.
*/
public static final String RawInfoPreferenceKey = "done_raw_info";
/** If this preference is set, no longer show the dialog for poor magnetic accuracy
*/
public static final String MagneticAccuracyPreferenceKey = "done_magnetic_accuracy";
public static final String CameraAPIPreferenceDefault = "preference_camera_api_old";
public static final String CameraAPIPreferenceKey = "preference_camera_api";
private static String getCameraIDKey(int cameraId, String cameraIdSPhysical) {
if( cameraIdSPhysical != null )
return cameraId + "_" + cameraIdSPhysical;
else
return String.valueOf(cameraId);
}
// don't set to be specific for physical cameras, as too confusing to have lots of different flash preferences
// also in Preview, we don't save the flash back if not supported
public static String getFlashPreferenceKey(int cameraId) {
return "flash_value_" + cameraId;
}
public static String getFocusPreferenceKey(int cameraId, boolean is_video) {
return "focus_value_" + cameraId + "_" + is_video;
}
public static final String FocusAssistPreferenceKey = "preference_focus_assist";
public static String getResolutionPreferenceKey(int cameraId, String cameraIdSPhysical) {
return "camera_resolution_" + getCameraIDKey(cameraId, cameraIdSPhysical);
}
public static String getVideoQualityPreferenceKey(int cameraId, String cameraIdSPhysical, boolean high_speed) {
return "video_quality_" + getCameraIDKey(cameraId, cameraIdSPhysical) + (high_speed ? "_highspeed" : "");
}
public static final String OptimiseFocusPreferenceKey = "preference_photo_optimise_focus";
public static final String ImageFormatPreferenceKey = "preference_image_format";
public static final String IsVideoPreferenceKey = "is_video";
public static final String ExposurePreferenceKey = "preference_exposure";
public static final String ColorEffectPreferenceKey = "preference_color_effect";
public static final String SceneModePreferenceKey = "preference_scene_mode";
public static final String WhiteBalancePreferenceKey = "preference_white_balance";
public static final String WhiteBalanceTemperaturePreferenceKey = "preference_white_balance_temperature";
public static final String AntiBandingPreferenceKey = "preference_antibanding";
public static final String EdgeModePreferenceKey = "preference_edge_mode";
public static final String CameraNoiseReductionModePreferenceKey = "preference_noise_reduction_mode"; // n.b., this is for the Camera driver noise reduction mode, not Open Camera's NR photo mode
public static final String ISOPreferenceKey = "preference_iso";
public static final String ExposureTimePreferenceKey = "preference_exposure_time";
public static final String RawPreferenceKey = "preference_raw";
public static final String AllowRawForExpoBracketingPreferenceKey = "preference_raw_expo_bracketing";
public static final String AllowRawForFocusBracketingPreferenceKey = "preference_raw_focus_bracketing";
public static final String PanoramaCropPreferenceKey = "preference_panorama_crop";
public static final String PanoramaSaveExpoPreferenceKey = "preference_panorama_save";
public static final String ExpoBracketingNImagesPreferenceKey = "preference_expo_bracketing_n_images";
public static final String ExpoBracketingStopsPreferenceKey = "preference_expo_bracketing_stops";
public static final String FocusDistancePreferenceKey = "preference_focus_distance";
public static final String FocusBracketingTargetDistancePreferenceKey = "preference_focus_bracketing_target_distance";
public static final String FocusBracketingAutoSourceDistancePreferenceKey = "preference_focus_bracketing_auto_source_distance";
public static final String FocusBracketingNImagesPreferenceKey = "preference_focus_bracketing_n_images";
public static final String FocusBracketingAddInfinityPreferenceKey = "preference_focus_bracketing_add_infinity";
public static final String VolumeKeysPreferenceKey = "preference_volume_keys";
public static final String AudioControlPreferenceKey = "preference_audio_control";
public static final String AudioNoiseControlSensitivityPreferenceKey = "preference_audio_noise_control_sensitivity";
public static final String QualityPreferenceKey = "preference_quality";
public static final String AutoStabilisePreferenceKey = "preference_auto_stabilise";
public static final String PhotoModePreferenceKey = "preference_photo_mode";
public static final String HDRSaveExpoPreferenceKey = "preference_hdr_save_expo";
public static final String HDRTonemappingPreferenceKey = "preference_hdr_tonemapping";
public static final String HDRContrastEnhancementPreferenceKey = "preference_hdr_contrast_enhancement";
public static final String NRSaveExpoPreferenceKey = "preference_nr_save";
public static final String FastBurstNImagesPreferenceKey = "preference_fast_burst_n_images";
public static final String LocationPreferenceKey = "preference_location";
public static final String RemoveDeviceExifPreferenceKey = "preference_remove_device_exif";
public static final String GPSDirectionPreferenceKey = "preference_gps_direction";
public static final String RequireLocationPreferenceKey = "preference_require_location";
public static final String ExifArtistPreferenceKey = "preference_exif_artist";
public static final String ExifCopyrightPreferenceKey = "preference_exif_copyright";
public static final String StampPreferenceKey = "preference_stamp";
public static final String StampDateFormatPreferenceKey = "preference_stamp_dateformat";
public static final String StampTimeFormatPreferenceKey = "preference_stamp_timeformat";
public static final String StampGPSFormatPreferenceKey = "preference_stamp_gpsformat";
//public static final String StampGeoAddressPreferenceKey = "preference_stamp_geo_address";
public static final String UnitsDistancePreferenceKey = "preference_units_distance";
public static final String TextStampPreferenceKey = "preference_textstamp";
public static final String StampFontSizePreferenceKey = "preference_stamp_fontsize";
public static final String StampFontColorPreferenceKey = "preference_stamp_font_color";
public static final String StampStyleKey = "preference_stamp_style";
public static final String VideoSubtitlePref = "preference_video_subtitle";
public static final String FrontCameraMirrorKey = "preference_front_camera_mirror";
public static final String EnableRemote = "preference_enable_remote";
public static final String RemoteName = "preference_remote_device_name";
public static final String RemoteType = "preference_remote_type";
public static final String WaterType = "preference_water_type";
//public static final String BackgroundPhotoSavingPreferenceKey = "preference_background_photo_saving";
public static final String Camera2FakeFlashPreferenceKey = "preference_camera2_fake_flash";
public static final String Camera2DummyCaptureHackPreferenceKey = "preference_camera2_dummy_capture_hack";
public static final String Camera2FastBurstPreferenceKey = "preference_camera2_fast_burst";
public static final String Camera2PhotoVideoRecordingPreferenceKey = "preference_camera2_photo_video_recording";
public static final String UIPlacementPreferenceKey = "preference_ui_placement";
public static final String TouchCapturePreferenceKey = "preference_touch_capture";
public static final String PausePreviewPreferenceKey = "preference_pause_preview";
public static final String ShowToastsPreferenceKey = "preference_show_toasts";
public static final String ThumbnailAnimationPreferenceKey = "preference_thumbnail_animation";
public static final String TakePhotoBorderPreferenceKey = "preference_take_photo_border";
public static final String DimWhenDisconnectedPreferenceKey = "preference_remote_disconnect_screen_dim";
public static final String AllowHapticFeedbackPreferenceKey = "preference_allow_haptic_feedback";
public static final String ShowWhenLockedPreferenceKey = "preference_show_when_locked";
public static final String AllowLongPressPreferenceKey = "preference_allow_long_press";
public static final String StartupFocusPreferenceKey = "preference_startup_focus";
public static final String MultiCamButtonPreferenceKey = "preference_multi_cam_button";
public static final String KeepDisplayOnPreferenceKey = "preference_keep_display_on";
public static final String MaxBrightnessPreferenceKey = "preference_max_brightness";
public static final String UsingSAFPreferenceKey = "preference_using_saf";
public static final String SaveLocationPreferenceKey = "preference_save_location";
public static final String SaveLocationSAFPreferenceKey = "preference_save_location_saf";
public static final String SaveLocationHistoryBasePreferenceKey = "save_location_history";
public static final String SaveLocationHistorySAFBasePreferenceKey = "save_location_history_saf";
public static final String SavePhotoPrefixPreferenceKey = "preference_save_photo_prefix";
public static final String SaveVideoPrefixPreferenceKey = "preference_save_video_prefix";
public static final String SaveZuluTimePreferenceKey = "preference_save_zulu_time";
public static final String SaveIncludeMillisecondsPreferenceKey = "preference_save_include_milliseconds";
public static final String ShowZoomSliderControlsPreferenceKey = "preference_show_zoom_slider_controls";
public static final String ShowTakePhotoPreferenceKey = "preference_show_take_photo";
public static final String ShowFaceDetectionPreferenceKey = "preference_show_face_detection";
public static final String ShowCycleFlashPreferenceKey = "preference_show_cycle_flash";
public static final String ShowFocusPeakingPreferenceKey = "preference_show_focus_peaking";
public static final String ShowAutoLevelPreferenceKey = "preference_show_auto_level";
public static final String ShowStampPreferenceKey = "preference_show_stamp";
public static final String ShowTextStampPreferenceKey = "preference_show_textstamp";
public static final String ShowStoreLocationPreferenceKey = "preference_show_store_location";
public static final String ShowCycleRawPreferenceKey = "preference_show_cycle_raw";
public static final String ShowWhiteBalanceLockPreferenceKey = "preference_show_white_balance_lock";
public static final String ShowExposureLockPreferenceKey = "preference_show_exposure_lock";
public static final String ShowZoomPreferenceKey = "preference_show_zoom";
public static final String ShowISOPreferenceKey = "preference_show_iso";
public static final String HistogramPreferenceKey = "preference_histogram";
public static final String ZebraStripesPreferenceKey = "preference_zebra_stripes";
public static final String ZebraStripesForegroundColorPreferenceKey = "preference_zebra_stripes_foreground_color";
public static final String ZebraStripesBackgroundColorPreferenceKey = "preference_zebra_stripes_background_color";
public static final String FocusPeakingPreferenceKey = "preference_focus_peaking";
public static final String FocusPeakingColorPreferenceKey = "preference_focus_peaking_color";
public static final String PreShotsPreferenceKey = "preference_save_preshots";
public static final String ShowVideoMaxAmpPreferenceKey = "preference_show_video_max_amp";
public static final String ShowAnglePreferenceKey = "preference_show_angle";
public static final String ShowAngleLinePreferenceKey = "preference_show_angle_line";
public static final String ShowPitchLinesPreferenceKey = "preference_show_pitch_lines";
public static final String ShowGeoDirectionLinesPreferenceKey = "preference_show_geo_direction_lines";
public static final String ShowAngleHighlightColorPreferenceKey = "preference_angle_highlight_color";
public static final String CalibratedLevelAnglePreferenceKey = "preference_calibrate_level_angle";
public static final String ShowGeoDirectionPreferenceKey = "preference_show_geo_direction";
public static final String ShowFreeMemoryPreferenceKey = "preference_free_memory";
public static final String ShowTimePreferenceKey = "preference_show_time";
public static final String ShowCameraIDPreferenceKey = "preference_show_camera_id";
public static final String ShowBatteryPreferenceKey = "preference_show_battery";
public static final String ShowGridPreferenceKey = "preference_grid";
public static final String ShowCropGuidePreferenceKey = "preference_crop_guide";
public static final String FaceDetectionPreferenceKey = "preference_face_detection";
public static final String GhostImagePreferenceKey = "preference_ghost_image";
public static final String GhostSelectedImageSAFPreferenceKey = "preference_ghost_selected_image_saf";
public static final String GhostImageAlphaPreferenceKey = "ghost_image_alpha";
public static final String VideoStabilizationPreferenceKey = "preference_video_stabilization";
public static final String ForceVideo4KPreferenceKey = "preference_force_video_4k";
public static final String VideoFormatPreferenceKey = "preference_video_output_format";
public static final String VideoBitratePreferenceKey = "preference_video_bitrate";
public static String getVideoFPSPreferenceKey(int cameraId, String cameraIdSPhysical) {
// for cameraId==0 and cameraIdSPhysical==null, we return preference_video_fps instead of preference_video_fps_0, for
// backwards compatibility for people upgrading
return "preference_video_fps" + ((cameraId==0 && cameraIdSPhysical==null) ? "" : ("_"+getCameraIDKey(cameraId, cameraIdSPhysical)));
}
public static String getVideoCaptureRatePreferenceKey(int cameraId, String cameraIdSPhysical) {
return "preference_capture_rate_" + getCameraIDKey(cameraId, cameraIdSPhysical);
}
public static final String VideoLogPreferenceKey = "preference_video_log";
public static final String VideoProfileGammaPreferenceKey = "preference_video_profile_gamma";
public static final String VideoMaxDurationPreferenceKey = "preference_video_max_duration";
public static final String VideoRestartPreferenceKey = "preference_video_restart";
public static final String VideoMaxFileSizePreferenceKey = "preference_video_max_filesize";
public static final String VideoRestartMaxFileSizePreferenceKey = "preference_video_restart_max_filesize";
public static final String VideoFlashPreferenceKey = "preference_video_flash";
public static final String VideoLowPowerCheckPreferenceKey = "preference_video_low_power_check";
public static final String LockVideoPreferenceKey = "preference_lock_video";
public static final String RecordAudioPreferenceKey = "preference_record_audio";
public static final String RecordAudioChannelsPreferenceKey = "preference_record_audio_channels";
public static final String RecordAudioSourcePreferenceKey = "preference_record_audio_src";
public static final String PreviewSizePreferenceKey = "preference_preview_size";
public static final String RotatePreviewPreferenceKey = "preference_rotate_preview";
public static final String LockOrientationPreferenceKey = "preference_lock_orientation";
public static final String TimerPreferenceKey = "preference_timer";
public static final String TimerBeepPreferenceKey = "preference_timer_beep";
public static final String TimerSpeakPreferenceKey = "preference_timer_speak";
// note for historical reasons the preference refers to burst; the feature was renamed to
// "repeat" in v1.43, but we still need to use the old string to avoid changing user settings
// when people upgrade
public static final String RepeatModePreferenceKey = "preference_burst_mode";
// see note about "repeat" vs "burst" under RepeatModePreferenceKey
public static final String RepeatIntervalPreferenceKey = "preference_burst_interval";
public static final String ShutterSoundPreferenceKey = "preference_shutter_sound";
public static final String ImmersiveModePreferenceKey = "preference_immersive_mode";
public static final String AddYPRToComments="preference_comment_ypr";
}

View file

@ -0,0 +1,244 @@
package net.sourceforge.opencamera;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceGroup;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;
import net.sourceforge.opencamera.ui.FolderChooserDialog;
import java.io.File;
public class PreferenceSubCameraControlsMore extends PreferenceSubScreen {
private static final String TAG = "PfSubCameraControlsMore";
@Override
public void onCreate(Bundle savedInstanceState) {
if( MyDebug.LOG )
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences_sub_camera_controls_more);
final Bundle bundle = getArguments();
/*final int cameraId = bundle.getInt("cameraId");
if( MyDebug.LOG )
Log.d(TAG, "cameraId: " + cameraId);
final int nCameras = bundle.getInt("nCameras");
if( MyDebug.LOG )
Log.d(TAG, "nCameras: " + nCameras);*/
final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
final boolean can_disable_shutter_sound = bundle.getBoolean("can_disable_shutter_sound");
if( MyDebug.LOG )
Log.d(TAG, "can_disable_shutter_sound: " + can_disable_shutter_sound);
if( !can_disable_shutter_sound ) {
// Camera.enableShutterSound requires JELLY_BEAN_MR1 or greater
Preference pref = findPreference("preference_shutter_sound");
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_camera_controls_more");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
/*{
EditTextPreference edit = (EditTextPreference)findPreference("preference_save_location");
InputFilter filter = new InputFilter() {
// whilst Android seems to allow any characters on internal memory, SD cards are typically formatted with FAT32
String disallowed = "|\\?*<\":>";
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
for(int i=start;i<end;i++) {
if( disallowed.indexOf( source.charAt(i) ) != -1 ) {
return "";
}
}
return null;
}
};
edit.getEditText().setFilters(new InputFilter[]{filter});
}*/
{
Preference pref = findPreference("preference_save_location");
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference arg0) {
if( MyDebug.LOG )
Log.d(TAG, "clicked save location");
MainActivity main_activity = (MainActivity)PreferenceSubCameraControlsMore.this.getActivity();
if( main_activity.getStorageUtils().isUsingSAF() ) {
main_activity.openFolderChooserDialogSAF(true);
return true;
}
else if( MainActivity.useScopedStorage() ) {
// we can't use an EditTextPreference (or MyEditTextPreference) due to having to support non-scoped-storage, or when SAF is enabled...
// anyhow, this means we can share code when called from gallery long-press anyway
AlertDialog.Builder alertDialog = main_activity.createSaveFolderDialog();
final AlertDialog alert = alertDialog.create();
// AlertDialog.Builder.setOnDismissListener() requires API level 17, so do it this way instead
alert.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface arg0) {
if( MyDebug.LOG )
Log.d(TAG, "save folder dialog dismissed");
dialogs.remove(alert);
}
});
alert.show();
dialogs.add(alert);
return true;
}
else {
File start_folder = main_activity.getStorageUtils().getImageFolder();
FolderChooserDialog fragment = new MyPreferenceFragment.SaveFolderChooserDialog();
fragment.setStartFolder(start_folder);
fragment.show(getFragmentManager(), "FOLDER_FRAGMENT");
return true;
}
}
});
}
{
final Preference pref = findPreference("preference_using_saf");
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference arg0) {
if( pref.getKey().equals("preference_using_saf") ) {
if( MyDebug.LOG )
Log.d(TAG, "user clicked saf");
if( sharedPreferences.getBoolean(PreferenceKeys.UsingSAFPreferenceKey, false) ) {
if( MyDebug.LOG )
Log.d(TAG, "saf is now enabled");
// seems better to alway re-show the dialog when the user selects, to make it clear where files will be saved (as the SAF location in general will be different to the non-SAF one)
//String uri = sharedPreferences.getString(PreferenceKeys.getSaveLocationSAFPreferenceKey(), "");
//if( uri.length() == 0 )
{
MainActivity main_activity = (MainActivity)PreferenceSubCameraControlsMore.this.getActivity();
Toast.makeText(main_activity, R.string.saf_select_save_location, Toast.LENGTH_SHORT).show();
main_activity.openFolderChooserDialogSAF(true);
}
}
else {
if( MyDebug.LOG )
Log.d(TAG, "saf is now disabled");
// need to update the summary, as switching back to non-SAF folder
MyPreferenceFragment.setSummary(findPreference("preference_save_location"));
}
}
return false;
}
});
}
{
final Preference pref = findPreference("preference_calibrate_level");
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference arg0) {
if( pref.getKey().equals("preference_calibrate_level") ) {
if( MyDebug.LOG )
Log.d(TAG, "user clicked calibrate level option");
AlertDialog.Builder alertDialog = new AlertDialog.Builder(PreferenceSubCameraControlsMore.this.getActivity());
alertDialog.setTitle(getActivity().getResources().getString(R.string.preference_calibrate_level));
alertDialog.setMessage(R.string.preference_calibrate_level_dialog);
alertDialog.setPositiveButton(R.string.preference_calibrate_level_calibrate, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
if( MyDebug.LOG )
Log.d(TAG, "user clicked calibrate level");
MainActivity main_activity = (MainActivity)PreferenceSubCameraControlsMore.this.getActivity();
if( main_activity.getPreview().hasLevelAngleStable() ) {
double current_level_angle = main_activity.getPreview().getLevelAngleUncalibrated();
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putFloat(PreferenceKeys.CalibratedLevelAnglePreferenceKey, (float)current_level_angle);
editor.apply();
main_activity.getPreview().updateLevelAngles();
Toast.makeText(main_activity, R.string.preference_calibrate_level_calibrated, Toast.LENGTH_SHORT).show();
}
}
});
alertDialog.setNegativeButton(R.string.preference_calibrate_level_reset, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
if( MyDebug.LOG )
Log.d(TAG, "user clicked reset calibration level");
MainActivity main_activity = (MainActivity)PreferenceSubCameraControlsMore.this.getActivity();
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putFloat(PreferenceKeys.CalibratedLevelAnglePreferenceKey, 0.0f);
editor.apply();
main_activity.getPreview().updateLevelAngles();
Toast.makeText(main_activity, R.string.preference_calibrate_level_calibration_reset, Toast.LENGTH_SHORT).show();
}
});
final AlertDialog alert = alertDialog.create();
// AlertDialog.Builder.setOnDismissListener() requires API level 17, so do it this way instead
alert.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface arg0) {
if( MyDebug.LOG )
Log.d(TAG, "calibration dialog dismissed");
dialogs.remove(alert);
}
});
alert.show();
dialogs.add(alert);
return false;
}
return false;
}
});
}
// preference_save_location done in onResume
MyPreferenceFragment.setSummary(findPreference("preference_save_photo_prefix"));
MyPreferenceFragment.setSummary(findPreference("preference_save_video_prefix"));
setupDependencies();
if( MyDebug.LOG )
Log.d(TAG, "onCreate done");
}
@Override
public void onResume() {
super.onResume();
// we need to call this onResume too, to handle updating the summary when changing location via SAF dialoga
MyPreferenceFragment.setSummary(findPreference("preference_save_location"));
}
/** Programmatically set up dependencies for preference types (e.g., ListPreference) that don't
* support this in xml (such as SwitchPreference and CheckBoxPreference), or where this depends
* on the device (e.g., Android version).
*/
private void setupDependencies() {
// set up dependency for preference_audio_noise_control_sensitivity on preference_audio_control
ListPreference pref = (ListPreference)findPreference("preference_audio_control");
if( pref != null ) { // may be null if preference not supported
pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference arg0, Object newValue) {
String value = newValue.toString();
setAudioNoiseControlSensitivityDependency(value);
return true;
}
});
setAudioNoiseControlSensitivityDependency(pref.getValue()); // ensure dependency is enabled/disabled as required for initial value
}
}
private void setAudioNoiseControlSensitivityDependency(String newValue) {
Preference dependent = findPreference("preference_audio_noise_control_sensitivity");
if( dependent != null ) { // just in case
boolean enable_dependent = "noise".equals(newValue);
if( MyDebug.LOG )
Log.d(TAG, "clicked audio control: " + newValue + " enable_dependent: " + enable_dependent);
dependent.setEnabled(enable_dependent);
}
}
}

View file

@ -0,0 +1,127 @@
package net.sourceforge.opencamera;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceGroup;
import android.preference.PreferenceManager;
import android.util.Log;
public class PreferenceSubGUI extends PreferenceSubScreen {
private static final String TAG = "PreferenceSubGUI";
@Override
public void onCreate(Bundle savedInstanceState) {
if( MyDebug.LOG )
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences_sub_gui);
final Bundle bundle = getArguments();
final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
final boolean camera_open = bundle.getBoolean("camera_open");
if( MyDebug.LOG )
Log.d(TAG, "camera_open: " + camera_open);
final boolean supports_face_detection = bundle.getBoolean("supports_face_detection");
if( MyDebug.LOG )
Log.d(TAG, "supports_face_detection: " + supports_face_detection);
final boolean supports_flash = bundle.getBoolean("supports_flash");
if( MyDebug.LOG )
Log.d(TAG, "supports_flash: " + supports_flash);
final boolean supports_preview_bitmaps = bundle.getBoolean("supports_preview_bitmaps");
if( MyDebug.LOG )
Log.d(TAG, "supports_preview_bitmaps: " + supports_preview_bitmaps);
final boolean supports_auto_stabilise = bundle.getBoolean("supports_auto_stabilise");
if( MyDebug.LOG )
Log.d(TAG, "supports_auto_stabilise: " + supports_auto_stabilise);
final boolean supports_raw = bundle.getBoolean("supports_raw");
if( MyDebug.LOG )
Log.d(TAG, "supports_raw: " + supports_raw);
final boolean supports_white_balance_lock = bundle.getBoolean("supports_white_balance_lock");
if( MyDebug.LOG )
Log.d(TAG, "supports_white_balance_lock: " + supports_white_balance_lock);
final boolean supports_exposure_lock = bundle.getBoolean("supports_exposure_lock");
if( MyDebug.LOG )
Log.d(TAG, "supports_exposure_lock: " + supports_exposure_lock);
final boolean is_multi_cam = bundle.getBoolean("is_multi_cam");
if( MyDebug.LOG )
Log.d(TAG, "is_multi_cam: " + is_multi_cam);
final boolean has_physical_cameras = bundle.getBoolean("has_physical_cameras");
if( MyDebug.LOG )
Log.d(TAG, "has_physical_cameras: " + has_physical_cameras);
if( !supports_face_detection && ( camera_open || sharedPreferences.getBoolean(PreferenceKeys.FaceDetectionPreferenceKey, false) == false ) ) {
// if camera not open, we'll think this setting isn't supported - but should only remove
// this preference if it's set to the default (otherwise if user sets to a non-default
// value that causes camera to not open, user won't be able to put it back to the
// default!)
Preference pref = findPreference("preference_show_face_detection");
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_gui");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
if( !supports_flash ) {
Preference pref = findPreference("preference_show_cycle_flash");
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_gui");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
if( !supports_preview_bitmaps ) {
Preference pref = findPreference("preference_show_focus_peaking");
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_gui");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
if( !supports_auto_stabilise ) {
Preference pref = findPreference("preference_show_auto_level");
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_gui");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
if( !supports_raw ) {
Preference pref = findPreference("preference_show_cycle_raw");
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_gui");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
if( !supports_white_balance_lock ) {
Preference pref = findPreference("preference_show_white_balance_lock");
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_gui");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
if( !supports_exposure_lock ) {
Preference pref = findPreference("preference_show_exposure_lock");
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_gui");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
if( !is_multi_cam && !has_physical_cameras ) {
Preference pref = findPreference("preference_multi_cam_button");
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_gui");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
if( MyDebug.LOG )
Log.d(TAG, "onCreate done");
}
}

View file

@ -0,0 +1,122 @@
package net.sourceforge.opencamera;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.preference.Preference;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
public class PreferenceSubLicences extends PreferenceSubScreen {
private static final String TAG = "PreferenceSubLicences";
@Override
public void onCreate(Bundle savedInstanceState) {
if( MyDebug.LOG )
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences_sub_licences);
{
final Preference pref = findPreference("preference_licence_open_camera");
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference arg0) {
if( pref.getKey().equals("preference_licence_open_camera") ) {
if( MyDebug.LOG )
Log.d(TAG, "user clicked open camera licence");
// display the GPL v3 text
displayTextDialog(R.string.preference_licence_open_camera, "gpl-3.0.txt");
return false;
}
return false;
}
});
}
{
final Preference pref = findPreference("preference_licence_androidx");
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference arg0) {
if( pref.getKey().equals("preference_licence_androidx") ) {
if( MyDebug.LOG )
Log.d(TAG, "user clicked androidx licence");
// display the Apache licence 2.0 text
displayTextDialog(R.string.preference_licence_androidx, "androidx_LICENSE-2.0.txt");
return false;
}
return false;
}
});
}
{
final Preference pref = findPreference("preference_licence_google_icons");
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference arg0) {
if( pref.getKey().equals("preference_licence_google_icons") ) {
if( MyDebug.LOG )
Log.d(TAG, "user clicked google material design icons licence");
// display the Apache licence 2.0 text
displayTextDialog(R.string.preference_licence_google_icons, "google_material_design_icons_LICENSE-2.0.txt");
return false;
}
return false;
}
});
}
{
final Preference pref = findPreference("preference_licence_online");
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference arg0) {
if( pref.getKey().equals("preference_licence_online") ) {
if( MyDebug.LOG )
Log.d(TAG, "user clicked online licences");
MainActivity main_activity = (MainActivity)PreferenceSubLicences.this.getActivity();
main_activity.launchOnlineLicences();
return false;
}
return false;
}
});
}
if( MyDebug.LOG )
Log.d(TAG, "onCreate done");
}
/* Displays a dialog with text loaded from a file in assets.
*/
private void displayTextDialog(int title_id, String file) {
try {
InputStream inputStream = getActivity().getAssets().open(file);
Scanner scanner = new Scanner(inputStream).useDelimiter("\\A");
AlertDialog.Builder alertDialog = new AlertDialog.Builder(PreferenceSubLicences.this.getActivity());
alertDialog.setTitle(getActivity().getResources().getString(title_id));
alertDialog.setMessage(scanner.next());
alertDialog.setPositiveButton(android.R.string.ok, null);
final AlertDialog alert = alertDialog.create();
// AlertDialog.Builder.setOnDismissListener() requires API level 17, so do it this way instead
alert.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface arg0) {
if( MyDebug.LOG )
Log.d(TAG, "text dialog dismissed");
dialogs.remove(alert);
}
});
alert.show();
dialogs.add(alert);
}
catch(IOException e) {
MyDebug.logStackTrace(TAG, "failed to load text for dialog", e);
}
}
}

View file

@ -0,0 +1,19 @@
package net.sourceforge.opencamera;
import android.os.Bundle;
import android.util.Log;
public class PreferenceSubLocation extends PreferenceSubScreen {
private static final String TAG = "PreferenceSubLocation";
@Override
public void onCreate(Bundle savedInstanceState) {
if( MyDebug.LOG )
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences_sub_location);
if( MyDebug.LOG )
Log.d(TAG, "onCreate done");
}
}

View file

@ -0,0 +1,299 @@
package net.sourceforge.opencamera;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceGroup;
import android.preference.PreferenceManager;
import android.util.Log;
import net.sourceforge.opencamera.preview.Preview;
import net.sourceforge.opencamera.ui.ArraySeekBarPreference;
public class PreferenceSubPhoto extends PreferenceSubScreen {
private static final String TAG = "PreferenceSubPhoto";
@Override
public void onCreate(Bundle savedInstanceState) {
if( MyDebug.LOG )
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences_sub_photo);
final Bundle bundle = getArguments();
final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
final int cameraId = bundle.getInt("cameraId");
if( MyDebug.LOG )
Log.d(TAG, "cameraId: " + cameraId);
final String cameraIdSPhysical = bundle.getString("cameraIdSPhysical");
if( MyDebug.LOG )
Log.d(TAG, "cameraIdSPhysical: " + cameraIdSPhysical);
final boolean using_android_l = bundle.getBoolean("using_android_l");
if( MyDebug.LOG )
Log.d(TAG, "using_android_l: " + using_android_l);
final int [] widths = bundle.getIntArray("resolution_widths");
final int [] heights = bundle.getIntArray("resolution_heights");
final boolean [] supports_burst = bundle.getBooleanArray("resolution_supports_burst");
final boolean supports_jpeg_r = bundle.getBoolean("supports_jpeg_r");
if( MyDebug.LOG )
Log.d(TAG, "supports_jpeg_r: " + supports_jpeg_r);
final boolean supports_raw = bundle.getBoolean("supports_raw");
if( MyDebug.LOG )
Log.d(TAG, "supports_raw: " + supports_raw);
final boolean supports_burst_raw = bundle.getBoolean("supports_burst_raw");
if( MyDebug.LOG )
Log.d(TAG, "supports_burst_raw: " + supports_burst_raw);
final boolean supports_optimise_focus_latency = bundle.getBoolean("supports_optimise_focus_latency");
if( MyDebug.LOG )
Log.d(TAG, "supports_optimise_focus_latency: " + supports_optimise_focus_latency);
final boolean supports_preshots = bundle.getBoolean("supports_preshots");
if( MyDebug.LOG )
Log.d(TAG, "supports_preshots: " + supports_preshots);
final boolean supports_nr = bundle.getBoolean("supports_nr");
if( MyDebug.LOG )
Log.d(TAG, "supports_nr: " + supports_nr);
final boolean supports_hdr = bundle.getBoolean("supports_hdr");
if( MyDebug.LOG )
Log.d(TAG, "supports_hdr: " + supports_hdr);
final boolean supports_expo_bracketing = bundle.getBoolean("supports_expo_bracketing");
if( MyDebug.LOG )
Log.d(TAG, "supports_expo_bracketing: " + supports_expo_bracketing);
final int max_expo_bracketing_n_images = bundle.getInt("max_expo_bracketing_n_images");
if( MyDebug.LOG )
Log.d(TAG, "max_expo_bracketing_n_images: " + max_expo_bracketing_n_images);
final boolean supports_panorama = bundle.getBoolean("supports_panorama");
if( MyDebug.LOG )
Log.d(TAG, "supports_panorama: " + supports_panorama);
final boolean supports_photo_video_recording = bundle.getBoolean("supports_photo_video_recording");
if( MyDebug.LOG )
Log.d(TAG, "supports_photo_video_recording: " + supports_photo_video_recording);
if( widths != null && heights != null && supports_burst != null ) {
CharSequence [] entries = new CharSequence[widths.length];
CharSequence [] values = new CharSequence[widths.length];
for(int i=0;i<widths.length;i++) {
entries[i] = widths[i] + " x " + heights[i] + " " + Preview.getAspectRatioMPString(getResources(), widths[i], heights[i], supports_burst[i]);
values[i] = widths[i] + " " + heights[i];
}
ListPreference lp = (ListPreference)findPreference("preference_resolution");
lp.setEntries(entries);
lp.setEntryValues(values);
String resolution_preference_key = PreferenceKeys.getResolutionPreferenceKey(cameraId, cameraIdSPhysical);
String resolution_value = sharedPreferences.getString(resolution_preference_key, "");
if( MyDebug.LOG )
Log.d(TAG, "resolution_value: " + resolution_value);
lp.setValue(resolution_value);
// now set the key, so we save for the correct cameraId
lp.setKey(resolution_preference_key);
}
else {
Preference pref = findPreference("preference_resolution");
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_photo_settings");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
{
final int n_quality = 100;
CharSequence [] entries = new CharSequence[n_quality];
CharSequence [] values = new CharSequence[n_quality];
for(int i=0;i<n_quality;i++) {
entries[i] = (i+1) + "%";
values[i] = String.valueOf(i + 1);
}
ArraySeekBarPreference sp = (ArraySeekBarPreference)findPreference("preference_quality");
sp.setEntries(entries);
sp.setEntryValues(values);
}
if( !supports_jpeg_r ) {
ListPreference pref = (ListPreference)findPreference("preference_image_format");
pref.setEntries(R.array.preference_image_format_entries_nojpegr);
pref.setEntryValues(R.array.preference_image_format_values_nojpegr);
}
if( !supports_raw ) {
Preference pref = findPreference("preference_raw");
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_photo_settings");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
else {
ListPreference pref = (ListPreference)findPreference("preference_raw");
if( Build.VERSION.SDK_INT < Build.VERSION_CODES.N ) {
// RAW only mode requires at least Android 7; earlier versions seem to have poorer support for DNG files
pref.setEntries(R.array.preference_raw_entries_preandroid7);
pref.setEntryValues(R.array.preference_raw_values_preandroid7);
}
pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if( MyDebug.LOG )
Log.d(TAG, "clicked raw: " + newValue);
if( newValue.equals("preference_raw_yes") || newValue.equals("preference_raw_only") ) {
// we check done_raw_info every time, so that this works if the user selects RAW again without leaving and returning to Settings
boolean done_raw_info = sharedPreferences.contains(PreferenceKeys.RawInfoPreferenceKey);
if( !done_raw_info ) {
AlertDialog.Builder alertDialog = new AlertDialog.Builder(PreferenceSubPhoto.this.getActivity());
alertDialog.setTitle(R.string.preference_raw);
alertDialog.setMessage(R.string.raw_info);
alertDialog.setPositiveButton(android.R.string.ok, null);
alertDialog.setNegativeButton(R.string.dont_show_again, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if( MyDebug.LOG )
Log.d(TAG, "user clicked dont_show_again for raw info dialog");
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean(PreferenceKeys.RawInfoPreferenceKey, true);
editor.apply();
}
});
final AlertDialog alert = alertDialog.create();
// AlertDialog.Builder.setOnDismissListener() requires API level 17, so do it this way instead
alert.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface arg0) {
if( MyDebug.LOG )
Log.d(TAG, "raw dialog dismissed");
dialogs.remove(alert);
}
});
alert.show();
dialogs.add(alert);
}
}
return true;
}
});
}
if( !( supports_raw && supports_burst_raw ) ) {
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_photo_settings");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
Preference pref = findPreference("preference_raw_expo_bracketing");
pg.removePreference(pref);
pref = findPreference("preference_raw_focus_bracketing");
pg.removePreference(pref);
}
if( !supports_optimise_focus_latency ) {
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
Preference pref = findPreference("preference_photo_optimise_focus");
pg.removePreference(pref);
}
if( !supports_preshots ) {
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
Preference pref = findPreference("preference_save_preshots");
pg.removePreference(pref);
}
if( !supports_nr ) {
Preference pref = findPreference("preference_nr_save");
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_photo_settings");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
if( !supports_hdr ) {
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_photo_settings");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
Preference pref = findPreference("preference_hdr_save_expo");
pg.removePreference(pref);
pref = findPreference("preference_hdr_tonemapping");
pg.removePreference(pref);
pref = findPreference("preference_hdr_contrast_enhancement");
pg.removePreference(pref);
}
if( !supports_expo_bracketing || max_expo_bracketing_n_images <= 3 ) {
Preference pref = findPreference("preference_expo_bracketing_n_images");
//PreferenceGroup pg = (PreferenceGroup) this.findPreference("preference_screen_photo_settings");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
if( !supports_expo_bracketing ) {
Preference pref = findPreference("preference_expo_bracketing_stops");
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_screen_photo_settings");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
if( !supports_panorama ) {
//PreferenceGroup pg = (PreferenceGroup) this.findPreference("preference_screen_photo_settings");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
Preference pref = findPreference("preference_panorama_crop");
pg.removePreference(pref);
pref = findPreference("preference_panorama_save");
pg.removePreference(pref);
}
if( !using_android_l ) {
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_category_photo_debugging");
Preference pref = findPreference("preference_camera2_fake_flash");
pg.removePreference(pref);
pref = findPreference("preference_camera2_dummy_capture_hack");
pg.removePreference(pref);
pref = findPreference("preference_camera2_fast_burst");
pg.removePreference(pref);
pref = findPreference("preference_camera2_photo_video_recording");
pg.removePreference(pref);
}
else {
if( !supports_photo_video_recording ) {
Preference pref = findPreference("preference_camera2_photo_video_recording");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_category_photo_debugging");
pg.removePreference(pref);
}
}
{
// remove preference_category_photo_debugging category if empty (which will be the case for old api)
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_category_photo_debugging");
if( MyDebug.LOG )
Log.d(TAG, "preference_category_photo_debugging children: " + pg.getPreferenceCount());
if( pg.getPreferenceCount() == 0 ) {
// pg.getParent() requires API level 26
//PreferenceGroup parent = (PreferenceGroup)this.findPreference("preference_screen_photo_settings");
PreferenceGroup parent = (PreferenceGroup)this.findPreference("preferences_root");
parent.removePreference(pg);
}
}
MyPreferenceFragment.setSummary(findPreference("preference_exif_artist"));
MyPreferenceFragment.setSummary(findPreference("preference_exif_copyright"));
MyPreferenceFragment.setSummary(findPreference("preference_textstamp"));
if( MyDebug.LOG )
Log.d(TAG, "onCreate done");
}
}

View file

@ -0,0 +1,115 @@
package net.sourceforge.opencamera;
import android.os.Bundle;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceGroup;
import android.util.Log;
import net.sourceforge.opencamera.ui.ArraySeekBarPreference;
public class PreferenceSubPreview extends PreferenceSubScreen {
private static final String TAG = "PreferenceSubPreview";
@Override
public void onCreate(Bundle savedInstanceState) {
if( MyDebug.LOG )
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences_sub_preview);
final Bundle bundle = getArguments();
final boolean using_android_l = bundle.getBoolean("using_android_l");
if( MyDebug.LOG )
Log.d(TAG, "using_android_l: " + using_android_l);
final boolean is_multi_cam = bundle.getBoolean("is_multi_cam");
if( MyDebug.LOG )
Log.d(TAG, "is_multi_cam: " + is_multi_cam);
final boolean supports_preview_bitmaps = bundle.getBoolean("supports_preview_bitmaps");
if( MyDebug.LOG )
Log.d(TAG, "supports_preview_bitmaps: " + supports_preview_bitmaps);
{
ListPreference pref = (ListPreference)findPreference("preference_ghost_image");
pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference arg0, Object newValue) {
if( MyDebug.LOG )
Log.d(TAG, "clicked ghost image: " + newValue);
if( newValue.equals("preference_ghost_image_selected") ) {
MainActivity main_activity = (MainActivity) PreferenceSubPreview.this.getActivity();
main_activity.openGhostImageChooserDialogSAF(true);
}
return true;
}
});
}
{
final int max_ghost_image_alpha = 80; // limit max to 80% for privacy reasons, so it isn't possible to put in a state where camera is on, but no preview is shown
final int ghost_image_alpha_step = 5; // should be exact divisor of max_ghost_image_alpha
final int n_ghost_image_alpha = max_ghost_image_alpha/ghost_image_alpha_step;
CharSequence [] entries = new CharSequence[n_ghost_image_alpha];
CharSequence [] values = new CharSequence[n_ghost_image_alpha];
for(int i=0;i<n_ghost_image_alpha;i++) {
int alpha = ghost_image_alpha_step*(i+1);
entries[i] = alpha + "%";
values[i] = String.valueOf(alpha);
}
ArraySeekBarPreference sp = (ArraySeekBarPreference)findPreference("ghost_image_alpha");
sp.setEntries(entries);
sp.setEntryValues(values);
}
if( !using_android_l ) {
Preference pref = findPreference("preference_focus_assist");
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_preview");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
if( !is_multi_cam ) {
Preference pref = findPreference("preference_show_camera_id");
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_preview");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
if( !using_android_l ) {
Preference pref = findPreference("preference_show_iso");
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_preview");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
if( !supports_preview_bitmaps ) {
//PreferenceGroup pg = (PreferenceGroup)this.findPreference("preference_preview");
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
Preference pref = findPreference("preference_histogram");
pg.removePreference(pref);
pref = findPreference("preference_zebra_stripes");
pg.removePreference(pref);
pref = findPreference("preference_zebra_stripes_foreground_color");
pg.removePreference(pref);
pref = findPreference("preference_zebra_stripes_background_color");
pg.removePreference(pref);
pref = findPreference("preference_focus_peaking");
pg.removePreference(pref);
pref = findPreference("preference_focus_peaking_color");
pg.removePreference(pref);
}
if( MyDebug.LOG )
Log.d(TAG, "onCreate done");
}
}

View file

@ -0,0 +1,96 @@
package net.sourceforge.opencamera;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceGroup;
import android.preference.PreferenceManager;
import android.util.Log;
import net.sourceforge.opencamera.cameracontroller.CameraController;
public class PreferenceSubProcessing extends PreferenceSubScreen {
private static final String TAG = "PreferenceSubProcessing";
@Override
public void onCreate(Bundle savedInstanceState) {
if( MyDebug.LOG )
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences_sub_processing);
final Bundle bundle = getArguments();
final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
final boolean camera_open = bundle.getBoolean("camera_open");
if( MyDebug.LOG )
Log.d(TAG, "camera_open: " + camera_open);
boolean has_antibanding = false;
String [] antibanding_values = bundle.getStringArray("antibanding");
if( antibanding_values != null && antibanding_values.length > 0 ) {
String [] antibanding_entries = bundle.getStringArray("antibanding_entries");
if( antibanding_entries != null && antibanding_entries.length == antibanding_values.length ) { // should always be true here, but just in case
MyPreferenceFragment.readFromBundle(this, antibanding_values, antibanding_entries, PreferenceKeys.AntiBandingPreferenceKey, CameraController.ANTIBANDING_DEFAULT, "preferences_root");
has_antibanding = true;
}
}
if( MyDebug.LOG )
Log.d(TAG, "has_antibanding?: " + has_antibanding);
if( !has_antibanding && ( camera_open || sharedPreferences.getString(PreferenceKeys.AntiBandingPreferenceKey, CameraController.ANTIBANDING_DEFAULT).equals(CameraController.ANTIBANDING_DEFAULT) ) ) {
// if camera not open, we'll think this setting isn't supported - but should only remove
// this preference if it's set to the default (otherwise if user sets to a non-default
// value that causes camera to not open, user won't be able to put it back to the
// default!)
Preference pref = findPreference(PreferenceKeys.AntiBandingPreferenceKey);
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
boolean has_edge_mode = false;
String [] edge_mode_values = bundle.getStringArray("edge_modes");
if( edge_mode_values != null && edge_mode_values.length > 0 ) {
String [] edge_mode_entries = bundle.getStringArray("edge_modes_entries");
if( edge_mode_entries != null && edge_mode_entries.length == edge_mode_values.length ) { // should always be true here, but just in case
MyPreferenceFragment.readFromBundle(this, edge_mode_values, edge_mode_entries, PreferenceKeys.EdgeModePreferenceKey, CameraController.EDGE_MODE_DEFAULT, "preferences_root");
has_edge_mode = true;
}
}
if( MyDebug.LOG )
Log.d(TAG, "has_edge_mode?: " + has_edge_mode);
if( !has_edge_mode && ( camera_open || sharedPreferences.getString(PreferenceKeys.EdgeModePreferenceKey, CameraController.EDGE_MODE_DEFAULT).equals(CameraController.EDGE_MODE_DEFAULT) ) ) {
// if camera not open, we'll think this setting isn't supported - but should only remove
// this preference if it's set to the default (otherwise if user sets to a non-default
// value that causes camera to not open, user won't be able to put it back to the
// default!)
Preference pref = findPreference(PreferenceKeys.EdgeModePreferenceKey);
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
boolean has_noise_reduction_mode = false;
String [] noise_reduction_mode_values = bundle.getStringArray("noise_reduction_modes");
if( noise_reduction_mode_values != null && noise_reduction_mode_values.length > 0 ) {
String [] noise_reduction_mode_entries = bundle.getStringArray("noise_reduction_modes_entries");
if( noise_reduction_mode_entries != null && noise_reduction_mode_entries.length == noise_reduction_mode_values.length ) { // should always be true here, but just in case
MyPreferenceFragment.readFromBundle(this, noise_reduction_mode_values, noise_reduction_mode_entries, PreferenceKeys.CameraNoiseReductionModePreferenceKey, CameraController.NOISE_REDUCTION_MODE_DEFAULT, "preferences_root");
has_noise_reduction_mode = true;
}
}
if( MyDebug.LOG )
Log.d(TAG, "has_noise_reduction_mode?: " + has_noise_reduction_mode);
if( !has_noise_reduction_mode && ( camera_open || sharedPreferences.getString(PreferenceKeys.CameraNoiseReductionModePreferenceKey, CameraController.NOISE_REDUCTION_MODE_DEFAULT).equals(CameraController.NOISE_REDUCTION_MODE_DEFAULT) ) ) {
// if camera not open, we'll think this setting isn't supported - but should only remove
// this preference if it's set to the default (otherwise if user sets to a non-default
// value that causes camera to not open, user won't be able to put it back to the
// default!)
Preference pref = findPreference(PreferenceKeys.CameraNoiseReductionModePreferenceKey);
PreferenceGroup pg = (PreferenceGroup)this.findPreference("preferences_root");
pg.removePreference(pref);
}
if( MyDebug.LOG )
Log.d(TAG, "onCreate done");
}
}

View file

@ -0,0 +1,18 @@
package net.sourceforge.opencamera;
import android.os.Bundle;
import android.util.Log;
public class PreferenceSubRemoteCtrl extends PreferenceSubScreen {
private static final String TAG = "PreferenceSubRemoteCtrl";
@Override
public void onCreate(Bundle savedInstanceState) {
if( MyDebug.LOG )
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences_sub_remote_ctrl);
if( MyDebug.LOG )
Log.d(TAG, "onCreate done");
}
}

Some files were not shown because too many files have changed in this diff Show more