Repo created
26
.gitignore
vendored
Normal 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
|
||||||
41
.idea/inspectionProfiles/Project_Default.xml
generated
Normal 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
|
|
@ -0,0 +1,3 @@
|
||||||
|
RewriteEngine On
|
||||||
|
RewriteCond %{HTTP:X-Forwarded-Proto} !https
|
||||||
|
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
|
||||||
BIN
_docs/baseline_add_a_photo_white_48.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
_docs/baseline_delete_white_48.png
Normal file
|
After Width: | Height: | Size: 670 B |
BIN
_docs/baseline_filter_vintage_white_48.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
97
_docs/credits.html
Normal 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>< <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á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
|
|
@ -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>< <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™ 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
|
After Width: | Height: | Size: 2 KiB |
BIN
_docs/exposure_unlocked.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
_docs/focus_mode_auto.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
_docs/focus_mode_continuous_picture.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
_docs/focus_mode_edof.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
_docs/focus_mode_fixed.png
Normal file
|
After Width: | Height: | Size: 933 B |
BIN
_docs/focus_mode_infinity.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
_docs/focus_mode_locked.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
_docs/focus_mode_manual.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
202
_docs/google_material_design_icons_LICENSE-2.0.txt
Normal 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
1946
_docs/history.html
Normal file
BIN
_docs/ic_exposure_white_48dp.png
Normal file
|
After Width: | Height: | Size: 969 B |
BIN
_docs/ic_gps_fixed_white_48dp.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
_docs/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
_docs/ic_mic_white_48dp.png
Normal file
|
After Width: | Height: | Size: 678 B |
BIN
_docs/ic_pause_circle_outline_white_48dp.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
326
_docs/index.html
Normal 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™ 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 ​ 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/​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 ​ 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/​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 ​ 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/​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/​2014/09/20/​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/​wiki/​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/​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/​icons/128411/​antivirus_close_forbid_hide_​lock_locked_password_privacy_​private_protection_restriction​_safe_secure_security_icon#​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/​icons/128416/​free_freedom_hack_lock_open_​padlock_password_secure_​security_unlock_unlocked_icon#​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/​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/​icons/103177/​eye_see_view_watch_icon#​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/​icons/51924/​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/​icons/81105/​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/​icons/6915/​book_gallery_images_photos_​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/​icons/115801/​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/​icons/111030/​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/​icons/103031/​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/​icons/92787/​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/​icons/81087/​mb_photo_icon#size=128 and https://www.iconfinder.com/​icons/81197/​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/​icons/81197/​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/​icons/81087/​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/​icons/115789/​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
|
|
@ -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>< <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 ​ 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/​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
|
After Width: | Height: | Size: 706 B |
117
_docs/privacy_oc.html
Normal 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>< <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
|
After Width: | Height: | Size: 1.3 KiB |
BIN
_docs/share.png
Normal file
|
After Width: | Height: | Size: 936 B |
5
_docs/stylesheet.css
Normal 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
|
After Width: | Height: | Size: 2.5 KiB |
BIN
_docs/take_photo.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
_docs/take_video.png
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
202
androidx_LICENSE-2.0.txt
Normal 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
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 {}
|
||||||
|
|
@ -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 {}
|
||||||
|
|
@ -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 {}
|
||||||
|
|
@ -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 {}
|
||||||
|
|
@ -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 {}
|
||||||
|
|
@ -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 {}
|
||||||
1681
app/src/androidTest/java/net/sourceforge/opencamera/TestUtils.java
Normal 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 {}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
app/src/androidTest/res/drawable-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
BIN
app/src/androidTest/res/drawable-ldpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/androidTest/res/drawable-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
app/src/androidTest/res/drawable-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
6
app/src/androidTest/res/values/strings.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<string name="app_name">OpenCamera.testTest</string>
|
||||||
|
|
||||||
|
</resources>
|
||||||
159
app/src/main/AndroidManifest.xml
Normal 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>
|
||||||
202
app/src/main/assets/androidx_LICENSE-2.0.txt
Normal 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.
|
||||||
202
app/src/main/assets/google_material_design_icons_LICENSE-2.0.txt
Normal 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.
|
||||||
674
app/src/main/assets/gpl-3.0.txt
Normal 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>.
|
||||||
185
app/src/main/java/net/sourceforge/opencamera/AudioListener.java
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
665
app/src/main/java/net/sourceforge/opencamera/GyroSensor.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2362
app/src/main/java/net/sourceforge/opencamera/HDRProcessor.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
4607
app/src/main/java/net/sourceforge/opencamera/ImageSaver.java
Normal file
4934
app/src/main/java/net/sourceforge/opencamera/JavaImageFunctions.java
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 + "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
216
app/src/main/java/net/sourceforge/opencamera/MagneticSensor.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
6963
app/src/main/java/net/sourceforge/opencamera/MainActivity.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
app/src/main/java/net/sourceforge/opencamera/MyDebug.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3028
app/src/main/java/net/sourceforge/opencamera/PanoramaProcessor.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
386
app/src/main/java/net/sourceforge/opencamera/PreferenceKeys.java
Normal 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";
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||