Initial commit
10
.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
57
.idea/assetWizardSettings.xml
generated
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="WizardSettings">
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="imageWizard">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="imageAssetPanel">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="launcher">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="foregroundImage">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="scalingPercent" value="46" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="backgroundAssetType" value="COLOR" />
|
||||
<entry key="backgroundColor" value="1fb3e2" />
|
||||
<entry key="foregroundImage" value="C:\code_icon(1).png" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
BIN
.idea/caches/build_file_checksums.ser
generated
Normal file
29
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,29 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<Objective-C-extensions>
|
||||
<file>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
|
||||
</file>
|
||||
<class>
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
|
||||
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
|
||||
</class>
|
||||
<extensions>
|
||||
<pair source="cpp" header="h" fileNamingConvention="NONE" />
|
||||
<pair source="c" header="h" fileNamingConvention="NONE" />
|
||||
</extensions>
|
||||
</Objective-C-extensions>
|
||||
</code_scheme>
|
||||
</component>
|
||||
6
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="PROJECT" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
19
.idea/gradle.xml
generated
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
<option value="$PROJECT_DIR$/lib" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveModulePerSourceSet" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
78
.idea/markdown-navigator.xml
generated
Normal file
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MarkdownProjectSettings" wasCopied="true">
|
||||
<PreviewSettings splitEditorLayout="SPLIT" splitEditorPreview="PREVIEW" useGrayscaleRendering="false" zoomFactor="1.0" maxImageWidth="0" showGitHubPageIfSynced="false" allowBrowsingInPreview="false" synchronizePreviewPosition="true" highlightPreviewType="NONE" highlightFadeOut="5" highlightOnTyping="true" synchronizeSourcePosition="true" verticallyAlignSourceAndPreviewSyncPosition="true" showSearchHighlightsInPreview="true" showSelectionInPreview="true" openRemoteLinks="true" replaceUnicodeEmoji="false" lastLayoutSetsDefault="false">
|
||||
<PanelProvider>
|
||||
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.panel" providerName="Default - Swing" />
|
||||
</PanelProvider>
|
||||
</PreviewSettings>
|
||||
<ParserSettings gitHubSyntaxChange="false" emojiShortcuts="0" emojiImages="0">
|
||||
<PegdownExtensions>
|
||||
<option name="ABBREVIATIONS" value="false" />
|
||||
<option name="ANCHORLINKS" value="true" />
|
||||
<option name="ASIDE" value="false" />
|
||||
<option name="ATXHEADERSPACE" value="true" />
|
||||
<option name="AUTOLINKS" value="true" />
|
||||
<option name="DEFINITIONS" value="false" />
|
||||
<option name="DEFINITION_BREAK_DOUBLE_BLANK_LINE" value="false" />
|
||||
<option name="FENCED_CODE_BLOCKS" value="true" />
|
||||
<option name="FOOTNOTES" value="false" />
|
||||
<option name="HARDWRAPS" value="false" />
|
||||
<option name="HTML_DEEP_PARSER" value="false" />
|
||||
<option name="INSERTED" value="false" />
|
||||
<option name="QUOTES" value="false" />
|
||||
<option name="RELAXEDHRULES" value="true" />
|
||||
<option name="SMARTS" value="false" />
|
||||
<option name="STRIKETHROUGH" value="true" />
|
||||
<option name="SUBSCRIPT" value="false" />
|
||||
<option name="SUPERSCRIPT" value="false" />
|
||||
<option name="SUPPRESS_HTML_BLOCKS" value="false" />
|
||||
<option name="SUPPRESS_INLINE_HTML" value="false" />
|
||||
<option name="TABLES" value="true" />
|
||||
<option name="TASKLISTITEMS" value="true" />
|
||||
<option name="TOC" value="false" />
|
||||
<option name="WIKILINKS" value="true" />
|
||||
</PegdownExtensions>
|
||||
<ParserOptions>
|
||||
<option name="ADMONITION_EXT" value="false" />
|
||||
<option name="ATTRIBUTES_EXT" value="false" />
|
||||
<option name="COMMONMARK_LISTS" value="false" />
|
||||
<option name="DUMMY" value="false" />
|
||||
<option name="EMOJI_SHORTCUTS" value="true" />
|
||||
<option name="ENUMERATED_REFERENCES_EXT" value="false" />
|
||||
<option name="FLEXMARK_FRONT_MATTER" value="false" />
|
||||
<option name="GFM_LOOSE_BLANK_LINE_AFTER_ITEM_PARA" value="true" />
|
||||
<option name="GFM_TABLE_RENDERING" value="true" />
|
||||
<option name="GITBOOK_URL_ENCODING" value="false" />
|
||||
<option name="GITHUB_LISTS" value="true" />
|
||||
<option name="GITHUB_WIKI_LINKS" value="true" />
|
||||
<option name="HEADER_ID_NO_DUPED_DASHES" value="false" />
|
||||
<option name="JEKYLL_FRONT_MATTER" value="false" />
|
||||
<option name="NO_TEXT_ATTRIBUTES" value="false" />
|
||||
<option name="PARSE_HTML_ANCHOR_ID" value="false" />
|
||||
<option name="SIM_TOC_BLANK_LINE_SPACER" value="true" />
|
||||
</ParserOptions>
|
||||
</ParserSettings>
|
||||
<HtmlSettings headerTopEnabled="false" headerBottomEnabled="false" bodyTopEnabled="false" bodyBottomEnabled="false" embedUrlContent="false" addPageHeader="true" embedImages="false" embedHttpImages="false" imageUriSerials="false">
|
||||
<GeneratorProvider>
|
||||
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.generator" providerName="Default Swing HTML Generator" />
|
||||
</GeneratorProvider>
|
||||
<headerTop />
|
||||
<headerBottom />
|
||||
<bodyTop />
|
||||
<bodyBottom />
|
||||
</HtmlSettings>
|
||||
<CssSettings previewScheme="UI_SCHEME" cssUri="" isCssUriEnabled="false" isCssUriSerial="false" isCssTextEnabled="false" isDynamicPageWidth="true">
|
||||
<StylesheetProvider>
|
||||
<provider providerId="com.vladsch.idea.multimarkdown.editor.swing.html.css" providerName="Default Swing Stylesheet" />
|
||||
</StylesheetProvider>
|
||||
<ScriptProviders />
|
||||
<cssText />
|
||||
<cssUriHistory />
|
||||
</CssSettings>
|
||||
<HtmlExportSettings updateOnSave="false" parentDir="$ProjectFileDir$" targetDir="$ProjectFileDir$" cssDir="" scriptDir="" plainHtml="false" imageDir="" copyLinkedImages="false" imageUniquifyType="0" targetExt="" useTargetExt="false" noCssNoScripts="false" linkToExportedHtml="true" exportOnSettingsChange="true" regenerateOnProjectOpen="false" linkFormatType="HTTP_ABSOLUTE" />
|
||||
<LinkMapSettings>
|
||||
<textMaps />
|
||||
</LinkMapSettings>
|
||||
</component>
|
||||
</project>
|
||||
3
.idea/markdown-navigator/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,3 @@
|
||||
<component name="MarkdownNavigator.ProfileManager">
|
||||
<settings default="" pdf-export="" />
|
||||
</component>
|
||||
34
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="NullableNotNullManager">
|
||||
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
|
||||
<option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
|
||||
<option name="myNullables">
|
||||
<value>
|
||||
<list size="5">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
|
||||
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
|
||||
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
|
||||
<item index="4" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myNotNulls">
|
||||
<value>
|
||||
<list size="4">
|
||||
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
|
||||
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
|
||||
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
|
||||
<item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
<option name="id" value="Android" />
|
||||
</component>
|
||||
</project>
|
||||
12
.idea/runConfigurations.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
||||
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
28
.travis.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
language: android
|
||||
jdk: oraclejdk8
|
||||
before_install:
|
||||
- yes | sdkmanager "platforms;android-27"
|
||||
|
||||
env:
|
||||
global:
|
||||
- ANDROID_API_LEVEL=27
|
||||
- ANDROID_BUILD_TOOLS_VERSION=27.0.3
|
||||
- TRAVIS_SECURE_ENV_VARS=true
|
||||
|
||||
android:
|
||||
components:
|
||||
# The BuildTools version used by your project
|
||||
- tools
|
||||
- platform-tools
|
||||
- build-tools-$ANDROID_BUILD_TOOLS_VERSION
|
||||
- extra-android-m2repository
|
||||
- extra-google-android-support
|
||||
|
||||
# The SDK version used to compile your project
|
||||
- android-$ANDROID_API_LEVEL
|
||||
licenses:
|
||||
- '.+'
|
||||
|
||||
script:
|
||||
- ./gradlew clean
|
||||
- ./gradlew assembleDebug
|
||||
201
LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
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.
|
||||
112
README.md
Normal file
@@ -0,0 +1,112 @@
|
||||
# ZXingLite
|
||||
|
||||

|
||||
|
||||
[](https://raw.githubusercontent.com/jenly1314/ZXingLite/master/app/release/app-release.apk)
|
||||
[](https://jitpack.io/#jenly1314/ZXingLite)
|
||||
[](https://travis-ci.org/jenly1314/ZXingLite)
|
||||
[](https://android-arsenal.com/api?level=15)
|
||||
[](http://www.apache.org/licenses/LICENSE-2.0)
|
||||
[](http://blog.csdn.net/jenly121)
|
||||
|
||||
ZXingLite for Android 是ZXing的精简版,优化扫码和生成二维码功能,扫码界面支持完全自定义,让集成更简单。
|
||||
|
||||
## Gif 展示
|
||||

|
||||
|
||||
|
||||
## ViewfinderView属性说明
|
||||
| 属性 | 值类型 | 默认值 | 说明 |
|
||||
| :------| :------ | :------ | :------ |
|
||||
| maskColor | color |<font color=#000000>#60000000</font>| 扫描区外遮罩的颜色 |
|
||||
| frameColor | color |<font color=#1FB3E2>#7F1FB3E2</font>| 扫描区边框的颜色 |
|
||||
| cornerColor | color |<font color=#1FB3E2>#FF1FB3E2</font>| 扫描区边角的颜色 |
|
||||
| laserColor | color |<font color=#1FB3E2>#FF1FB3E2</font>| 扫描区激光线的颜色 |
|
||||
| resultPointColor | color |<font color=#EFBD21>#C0EFBD21</font>| 扫描区结果点的颜色 |
|
||||
| text | string || 扫描提示文本信息 |
|
||||
| textColor | color |<font color=#C0C0C0>#FFC0C0C0</font>| 提示文本字体颜色 |
|
||||
| textSize | dimension |14sp| 提示文本字体大小 |
|
||||
| textPadding | dimension |24dp| 提示文本距离扫描区的间距 |
|
||||
| textLocation | enum |top| 提示文本信息显示的位置 |
|
||||
|
||||
## 引入
|
||||
|
||||
### Maven:
|
||||
```maven
|
||||
<dependency>
|
||||
<groupId>com.king.zxing</groupId>
|
||||
<artifactId>zxing-lite</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<type>pom</type>
|
||||
</dependency>
|
||||
```
|
||||
### Gradle:
|
||||
```gradle
|
||||
implementation 'com.king.zxing:zxing-lite:1.0.0'
|
||||
```
|
||||
### Lvy:
|
||||
```lvy
|
||||
<dependency org='com.king.zxing' name='zxing-lite' rev='1.0.0'>
|
||||
<artifact name='$AID' ext='pom'></artifact>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
###### 如果Gradle出现compile失败的情况,可以在Project的build.gradle里面添加如下:(也可以使用上面的GitPack来complie)
|
||||
```gradle
|
||||
allprojects {
|
||||
repositories {
|
||||
maven { url 'https://dl.bintray.com/jenly/maven' }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 引入的库:
|
||||
```gradle
|
||||
compileOnly 'com.android.support:appcompat-v7:27.1.1'
|
||||
api 'com.com.google.zxing:core:3.3.3'
|
||||
```
|
||||
|
||||
## 示例
|
||||
|
||||
布局示例 (可自定义布局,布局内至少要保证有SurfaceView和ViewfinderView,控件id可根据重写CaptureActivity 的 getPreviewViewId 和 getViewFinderViewId方法自定义)
|
||||
```Xml
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<SurfaceView
|
||||
android:id="@+id/preview_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
<com.king.zxing.ViewfinderView
|
||||
android:id="@+id/viewfinder_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
</merge>
|
||||
```
|
||||
|
||||
代码示例 (二维码/条形码)
|
||||
```Java
|
||||
//生成二维码
|
||||
CodeUtils.createQRCode(content,600,logo);
|
||||
//生成条形码
|
||||
CodeUtils.createBarCode(content, BarcodeFormat.CODE_128,800,200);
|
||||
```
|
||||
|
||||
更多使用详情,请查看[app](app)中的源码使用示例
|
||||
|
||||
## 关于我
|
||||
Name: <a title="关于作者" href="https://about.me/jenly1314" target="_blank">Jenly</a>
|
||||
|
||||
Email: <a title="欢迎邮件与我交流" href="mailto:jenly1314@gmail.com" target="_blank">jenly1314#gmail.com</a> / <a title="给我发邮件" href="mailto:jenly1314@vip.qq.com" target="_blank">jenly1314#vip.qq.com</a>
|
||||
|
||||
CSDN: <a title="CSDN博客" href="http://blog.csdn.net/jenly121" target="_blank">jenly121</a>
|
||||
|
||||
Github: <a title="Github开源项目" href="https://github.com/jenly1314" target="_blank">jenly1314</a>
|
||||
|
||||
微信公众号:
|
||||
|
||||

|
||||
|
||||
加入QQ群: <a title="点击加入QQ群" href="http://shang.qq.com/wpa/qunwpa?idkey=8fcc6a2f88552ea44b1411582c94fd124f7bb3ec227e2a400dbbfaad3dc2f5ad" target="_blank">20867961</a>
|
||||
|
||||
|
||||
|
||||
|
||||
1
app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
36
app/build.gradle
Normal file
@@ -0,0 +1,36 @@
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
compileSdkVersion build_versions.compileSdk
|
||||
buildToolsVersion build_versions.buildTools
|
||||
defaultConfig {
|
||||
applicationId "com.king.zxing.app"
|
||||
minSdkVersion build_versions.minSdk
|
||||
targetSdkVersion build_versions.targetSdk
|
||||
versionCode app_version.versionCode
|
||||
versionName app_version.versionName
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
testImplementation deps.test.junit
|
||||
androidTestImplementation deps.test.runner
|
||||
androidTestImplementation deps.test.espresso
|
||||
//support
|
||||
implementation deps.support.design
|
||||
implementation deps.support.appcompat
|
||||
implementation deps.support.constraintlayout
|
||||
|
||||
implementation deps.easypermissions
|
||||
|
||||
implementation project(':lib')
|
||||
}
|
||||
21
app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
BIN
app/release/app-release.apk
Normal file
1
app/release/output.json
Normal file
@@ -0,0 +1 @@
|
||||
[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":1,"versionName":"1.0.0","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release"},"path":"app-release.apk","properties":{}}]
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.king.zxing.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
assertEquals("com.king.zxing.app", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
31
app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.king.zxing.app">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".MainActivity"
|
||||
android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".EasyCaptureActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name=".CustomCaptureActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
<activity
|
||||
android:name=".CodeActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
BIN
app/src/main/ic_launcher-web.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
85
app/src/main/java/com/king/zxing/app/CodeActivity.java
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Jenly Yu
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.king.zxing.app;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.king.zxing.util.CodeUtils;
|
||||
|
||||
/**
|
||||
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
|
||||
*/
|
||||
public class CodeActivity extends AppCompatActivity {
|
||||
|
||||
private TextView tvTitle;
|
||||
private ImageView ivCode;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.code_activity);
|
||||
ivCode = findViewById(R.id.ivCode);
|
||||
tvTitle = findViewById(R.id.tvTitle);
|
||||
tvTitle.setText(getIntent().getStringExtra(MainActivity.KEY_TITLE));
|
||||
boolean isQRCode = getIntent().getBooleanExtra(MainActivity.KEY_IS_QR_CODE,false);
|
||||
|
||||
if(isQRCode){
|
||||
createQRCode(getString(R.string.app_name));
|
||||
}else{
|
||||
createBarCode(getString(R.string.app_name));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成二维码
|
||||
* @param content
|
||||
*/
|
||||
private void createQRCode(String content){
|
||||
//生成二维码最好放子线程生成防止阻塞UI,这里只是演示
|
||||
Bitmap logo = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher);
|
||||
Bitmap bitmap = CodeUtils.createQRCode(content,600,logo);
|
||||
//显示二维码
|
||||
ivCode.setImageBitmap(bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成条形码
|
||||
* @param content
|
||||
*/
|
||||
private void createBarCode(String content){
|
||||
//生成条形码最好放子线程生成防止阻塞UI,这里只是演示
|
||||
Bitmap bitmap = CodeUtils.createBarCode(content, BarcodeFormat.CODE_128,800,200);
|
||||
//显示条形码
|
||||
ivCode.setImageBitmap(bitmap);
|
||||
}
|
||||
|
||||
|
||||
public void OnClick(View v){
|
||||
switch (v.getId()){
|
||||
case R.id.ivLeft:
|
||||
onBackPressed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Jenly Yu
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.king.zxing.app;
|
||||
|
||||
import android.hardware.Camera;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.king.zxing.CaptureActivity;
|
||||
import com.king.zxing.app.util.StatusBarUtils;
|
||||
|
||||
/**
|
||||
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
|
||||
*/
|
||||
public class CustomCaptureActivity extends CaptureActivity {
|
||||
|
||||
@Override
|
||||
public int getLayoutId() {
|
||||
return R.layout.custom_capture_activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
StatusBarUtils.immersiveStatusBar(this,toolbar,0.2f);
|
||||
TextView tvTitle = findViewById(R.id.tvTitle);
|
||||
tvTitle.setText(getIntent().getStringExtra(MainActivity.KEY_TITLE));
|
||||
|
||||
getBeepManager().setPlayBeep(true);
|
||||
getBeepManager().setVibrate(true);
|
||||
}
|
||||
|
||||
private void offFlash(){
|
||||
Camera camera = getCameraManager().getOpenCamera().getCamera();
|
||||
Camera.Parameters parameters = camera.getParameters();
|
||||
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
|
||||
camera.setParameters(parameters);
|
||||
}
|
||||
|
||||
public void openFlash(){
|
||||
Camera camera = getCameraManager().getOpenCamera().getCamera();
|
||||
Camera.Parameters parameters = camera.getParameters();
|
||||
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
|
||||
camera.setParameters(parameters);
|
||||
}
|
||||
|
||||
|
||||
private void clickFlash(View v){
|
||||
if(v.isSelected()){
|
||||
offFlash();
|
||||
v.setSelected(false);
|
||||
}else{
|
||||
openFlash();
|
||||
v.setSelected(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void OnClick(View v){
|
||||
switch (v.getId()){
|
||||
case R.id.ivLeft:
|
||||
onBackPressed();
|
||||
break;
|
||||
case R.id.ivFlash:
|
||||
clickFlash(v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Jenly Yu
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.king.zxing.app;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.king.zxing.CaptureActivity;
|
||||
import com.king.zxing.app.util.StatusBarUtils;
|
||||
|
||||
/**
|
||||
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
|
||||
*/
|
||||
public class EasyCaptureActivity extends CaptureActivity {
|
||||
|
||||
|
||||
@Override
|
||||
public int getLayoutId() {
|
||||
return R.layout.easy_capture_activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
StatusBarUtils.immersiveStatusBar(this,toolbar,0.2f);
|
||||
TextView tvTitle = findViewById(R.id.tvTitle);
|
||||
tvTitle.setText(getIntent().getStringExtra(MainActivity.KEY_TITLE));
|
||||
|
||||
}
|
||||
|
||||
public void OnClick(View v){
|
||||
switch (v.getId()){
|
||||
case R.id.ivLeft:
|
||||
onBackPressed();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
147
app/src/main/java/com/king/zxing/app/MainActivity.java
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Jenly Yu
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.king.zxing.app;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Intent;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.ActivityOptionsCompat;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.Toast;
|
||||
import com.king.zxing.CaptureActivity;
|
||||
import com.king.zxing.Intents;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import pub.devrel.easypermissions.AfterPermissionGranted;
|
||||
import pub.devrel.easypermissions.EasyPermissions;
|
||||
|
||||
public class MainActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks{
|
||||
|
||||
public static final String KEY_TITLE = "key_title";
|
||||
public static final String KEY_IS_QR_CODE = "key_code";
|
||||
|
||||
public static final int REQUEST_CODE = 0X01;
|
||||
|
||||
public static final int RC_CAMERA = 0X01;
|
||||
|
||||
private Class<?> cls;
|
||||
private String title;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if(resultCode == RESULT_OK){
|
||||
if(data!=null){
|
||||
//识别结果
|
||||
String result = data.getStringExtra(Intents.Scan.RESULT);
|
||||
Toast.makeText(this,result,Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
||||
// Forward results to EasyPermissions
|
||||
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionsGranted(int requestCode, List<String> list) {
|
||||
// Some permissions have been granted
|
||||
startScan(cls,title);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPermissionsDenied(int requestCode, List<String> list) {
|
||||
// Some permissions have been denied
|
||||
// ...
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测拍摄权限
|
||||
* @param cls
|
||||
* @param title
|
||||
*/
|
||||
@AfterPermissionGranted(RC_CAMERA)
|
||||
private void checkCameraPermissions(Class<?> cls,String title){
|
||||
this.cls = cls;
|
||||
this.title = title;
|
||||
String[] perms = {Manifest.permission.CAMERA};
|
||||
if (EasyPermissions.hasPermissions(this, perms)) {//有权限
|
||||
startScan(cls,title);
|
||||
} else {
|
||||
// Do not have permissions, request them now
|
||||
EasyPermissions.requestPermissions(this, getString(R.string.permission_camera),
|
||||
RC_CAMERA, perms);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫码
|
||||
* @param cls
|
||||
* @param title
|
||||
*/
|
||||
private void startScan(Class<?> cls,String title){
|
||||
ActivityOptionsCompat optionsCompat = ActivityOptionsCompat.makeCustomAnimation(this,R.anim.in,R.anim.out);
|
||||
Intent intent = new Intent(this, cls);
|
||||
intent.putExtra(KEY_TITLE,title);
|
||||
ActivityCompat.startActivityForResult(this,intent,REQUEST_CODE,optionsCompat.toBundle());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成二维码/条形码
|
||||
* @param isQRCode
|
||||
*/
|
||||
private void startCode(boolean isQRCode){
|
||||
Intent intent = new Intent(this,CodeActivity.class);
|
||||
intent.putExtra(KEY_IS_QR_CODE,isQRCode);
|
||||
intent.putExtra(KEY_TITLE,isQRCode ? getString(R.string.qr_code) : getString(R.string.bar_code));
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
public void OnClick(View v){
|
||||
switch (v.getId()){
|
||||
case R.id.btn1:
|
||||
checkCameraPermissions(CaptureActivity.class, ((Button)v).getText().toString());
|
||||
break;
|
||||
case R.id.btn2:
|
||||
checkCameraPermissions(EasyCaptureActivity.class,((Button)v).getText().toString());
|
||||
break;
|
||||
case R.id.btn3:
|
||||
checkCameraPermissions(CustomCaptureActivity.class,((Button)v).getText().toString());
|
||||
break;
|
||||
case R.id.btn4:
|
||||
startCode(false);
|
||||
break;
|
||||
case R.id.btn5:
|
||||
startCode(true);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Jenly Yu
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.king.zxing.app.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.FloatRange;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.king.zxing.app.R;
|
||||
|
||||
/**
|
||||
* @author Jenly <a href="mailto:jenly1314@gmail.com">Jenly</a>
|
||||
*/
|
||||
public enum StatusBarUtils {
|
||||
INSTANCE;
|
||||
|
||||
public static void immersiveStatusBar(Activity activity,Toolbar toolbar) {
|
||||
immersiveStatusBar(activity,toolbar,0.0f);
|
||||
}
|
||||
|
||||
public static void immersiveStatusBar(Activity activity,Toolbar toolbar,@FloatRange(from = 0.0, to = 1.0) float alpha) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
return;
|
||||
}
|
||||
|
||||
Window window = activity.getWindow();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
||||
window.setStatusBarColor(Color.TRANSPARENT);
|
||||
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
} else {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
}
|
||||
|
||||
ViewGroup decorView = (ViewGroup) window.getDecorView();
|
||||
ViewGroup contentView = window.getDecorView().findViewById(Window.ID_ANDROID_CONTENT);
|
||||
View rootView = contentView.getChildAt(0);
|
||||
if (rootView != null) {
|
||||
rootView.setFitsSystemWindows(false);
|
||||
}
|
||||
if(toolbar!=null){
|
||||
toolbar.setPadding(0,getStatusBarHeight(activity),0,0);
|
||||
}
|
||||
|
||||
decorView.addView(createStatusBarView(activity,alpha));
|
||||
}
|
||||
|
||||
private static View createStatusBarView(Activity activity,@FloatRange(from = 0.0, to = 1.0) float alpha) {
|
||||
// 绘制一个和状态栏一样高的矩形
|
||||
View statusBarView = new View(activity);
|
||||
LinearLayout.LayoutParams params =
|
||||
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));
|
||||
statusBarView.setLayoutParams(params);
|
||||
statusBarView.setBackgroundColor(Color.argb((int) (alpha * 255), 0, 0, 0));
|
||||
statusBarView.setId(R.id.translucent_view);
|
||||
return statusBarView;
|
||||
}
|
||||
|
||||
/** 获取状态栏高度 */
|
||||
public static int getStatusBarHeight(Context context) {
|
||||
return context.getResources().getDimensionPixelSize(R.dimen.status_bar_height);
|
||||
}
|
||||
}
|
||||
5
app/src/main/res/anim/in.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:fromAlpha="0.2"
|
||||
android:toAlpha="1.0"
|
||||
android:duration="200" />
|
||||
5
app/src/main/res/anim/out.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.0"
|
||||
android:duration="200" />
|
||||
34
app/src/main/res/drawable-v24/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:endX="78.5885"
|
||||
android:endY="90.9159"
|
||||
android:startX="48.7653"
|
||||
android:startY="61.0927"
|
||||
android:type="linear">
|
||||
<item
|
||||
android:color="#44000000"
|
||||
android:offset="0.0"/>
|
||||
<item
|
||||
android:color="#00000000"
|
||||
android:offset="1.0"/>
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:fillType="nonZero"
|
||||
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||
android:strokeColor="#00000000"
|
||||
android:strokeWidth="1"/>
|
||||
</vector>
|
||||
BIN
app/src/main/res/drawable-xxhdpi/btn_back_normal.png
Normal file
|
After Width: | Height: | Size: 726 B |
BIN
app/src/main/res/drawable-xxhdpi/btn_back_pressed.png
Normal file
|
After Width: | Height: | Size: 804 B |
BIN
app/src/main/res/drawable-xxhdpi/btn_none.png
Normal file
|
After Width: | Height: | Size: 1008 B |
BIN
app/src/main/res/drawable-xxhdpi/flash_off.png
Normal file
|
After Width: | Height: | Size: 809 B |
BIN
app/src/main/res/drawable-xxhdpi/flash_on.png
Normal file
|
After Width: | Height: | Size: 712 B |
5
app/src/main/res/drawable/btn_back_selector.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true" android:drawable="@drawable/btn_back_pressed"/>
|
||||
<item android:state_pressed="false" android:drawable="@drawable/btn_back_normal"/>
|
||||
</selector>
|
||||
5
app/src/main/res/drawable/flash_selected_selector.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_selected="true" android:drawable="@drawable/flash_on"/>
|
||||
<item android:state_selected="false" android:drawable="@drawable/flash_off"/>
|
||||
</selector>
|
||||
171
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@@ -0,0 +1,171 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportHeight="108"
|
||||
android:viewportWidth="108">
|
||||
<path
|
||||
android:fillColor="#26A69A"
|
||||
android:pathData="M0,0h108v108h-108z"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M9,0L9,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,0L19,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,0L29,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,0L39,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,0L49,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,0L59,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,0L69,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,0L79,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M89,0L89,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M99,0L99,108"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,9L108,9"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,19L108,19"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,29L108,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,39L108,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,49L108,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,59L108,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,69L108,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,79L108,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,89L108,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M0,99L108,99"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,29L89,29"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,39L89,39"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,49L89,49"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,59L89,59"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,69L89,69"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M19,79L89,79"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M29,19L29,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M39,19L39,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M49,19L49,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M59,19L59,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M69,19L69,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
<path
|
||||
android:fillColor="#00000000"
|
||||
android:pathData="M79,19L79,89"
|
||||
android:strokeColor="#33FFFFFF"
|
||||
android:strokeWidth="0.8"/>
|
||||
</vector>
|
||||
101
app/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:background="@color/colorPrimary"
|
||||
android:layout_height="wrap_content"
|
||||
app:contentInsetLeft="0dp"
|
||||
app:contentInsetStart="0dp">
|
||||
<TextView
|
||||
android:id="@+id/tvTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/title_height"
|
||||
android:gravity="center"
|
||||
android:singleLine="true"
|
||||
android:textSize="@dimen/size_18sp"
|
||||
android:textColor="@color/white"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:text="@string/app_name"/>
|
||||
</android.support.v7.widget.Toolbar>
|
||||
<android.support.constraint.Guideline
|
||||
android:id="@+id/line"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toBottomOf="@+id/toolbar"
|
||||
app:layout_constraintGuide_percent="0.2"/>
|
||||
<Button
|
||||
android:id="@+id/btn1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:text="默认扫码"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/line"
|
||||
style="@style/OnClick"/>
|
||||
<Button
|
||||
android:id="@+id/btn2"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:text="简单自定义布局扫码"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/btn1"
|
||||
style="@style/OnClick"/>
|
||||
<Button
|
||||
android:id="@+id/btn3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:text="自定义布局扫码"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/btn2"
|
||||
style="@style/OnClick"/>
|
||||
<Button
|
||||
android:id="@+id/btn4"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:text="生成条形码"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/btn3"
|
||||
style="@style/OnClick"/>
|
||||
<Button
|
||||
android:id="@+id/btn5"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:text="生成二维码"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/btn4"
|
||||
style="@style/OnClick"/>
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
18
app/src/main/res/layout/code_activity.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<include android:id="@+id/toolbar"
|
||||
layout="@layout/toolbar"/>
|
||||
<ImageView
|
||||
android:id="@+id/ivCode"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/toolbar"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
35
app/src/main/res/layout/custom_capture_activity.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<SurfaceView
|
||||
android:id="@+id/preview_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"/>
|
||||
<com.king.zxing.ViewfinderView
|
||||
android:id="@+id/viewfinder_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:text="@string/tips_scan_code"
|
||||
app:textSize="@dimen/size_14sp"
|
||||
app:laserColor="@color/colorAccent"
|
||||
app:frameColor="@color/colorPrimary"
|
||||
app:cornerColor="@color/colorPrimary"
|
||||
app:resultPointColor="@color/colorAccent"
|
||||
app:textLocation="bottom"/>
|
||||
<ImageView
|
||||
android:id="@+id/ivFlash"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/flash_selected_selector"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginTop="160dp"
|
||||
style="@style/OnClick"/>
|
||||
<include layout="@layout/toolbar_capture"/>
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
8
app/src/main/res/layout/easy_capture_activity.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<include layout="@layout/capture"/>
|
||||
<include layout="@layout/toolbar_capture"/>
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
12
app/src/main/res/layout/toolbar.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:background="@color/colorPrimary"
|
||||
android:layout_height="wrap_content"
|
||||
app:contentInsetLeft="0dp"
|
||||
app:contentInsetStart="0dp">
|
||||
<include layout="@layout/top_title_back_bar"/>
|
||||
</android.support.v7.widget.Toolbar>
|
||||
11
app/src/main/res/layout/toolbar_capture.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:contentInsetLeft="0dp"
|
||||
app:contentInsetStart="0dp">
|
||||
<include layout="@layout/top_title_back_bar"/>
|
||||
</android.support.v7.widget.Toolbar>
|
||||
35
app/src/main/res/layout/top_title_back_bar.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/ll"
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/title_height"
|
||||
android:gravity="center">
|
||||
<ImageView
|
||||
android:id="@+id/ivLeft"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:src="@drawable/btn_back_selector"
|
||||
android:paddingLeft="@dimen/size_14dp"
|
||||
android:paddingRight="@dimen/size_14dp"
|
||||
style="@style/OnClick"/>
|
||||
<TextView
|
||||
android:id="@+id/tvTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:singleLine="true"
|
||||
android:textSize="@dimen/size_18sp"
|
||||
android:textColor="@color/white"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:text="@string/app_name"/>
|
||||
<ImageView
|
||||
android:id="@+id/ivRight"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingLeft="@dimen/size_14dp"
|
||||
android:paddingRight="@dimen/size_14dp"
|
||||
android:src="@drawable/btn_none"
|
||||
style="@style/OnClick"/>
|
||||
</LinearLayout>
|
||||
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1022 B |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
4
app/src/main/res/values-v19/dimens.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="status_bar_height">25dp</dimen>
|
||||
</resources>
|
||||
8
app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
|
||||
<color name="white">#ffffff</color>
|
||||
</resources>
|
||||
12
app/src/main/res/values/dimens.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="status_bar_height">0dp</dimen>
|
||||
|
||||
<dimen name="title_height">50dp</dimen>
|
||||
|
||||
<dimen name="size_14sp">14sp</dimen>
|
||||
<dimen name="size_16sp">16sp</dimen>
|
||||
<dimen name="size_18sp">18sp</dimen>
|
||||
|
||||
<dimen name="size_14dp">14dp</dimen>
|
||||
</resources>
|
||||
4
app/src/main/res/values/ic_launcher_background.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#1FB3E2</color>
|
||||
</resources>
|
||||
5
app/src/main/res/values/ids.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<item name="statusbar_view" type="id"/>
|
||||
<item name="translucent_view" type="id"/>
|
||||
</resources>
|
||||
7
app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<resources>
|
||||
<string name="app_name">ZXingLite</string>
|
||||
<string name="permission_camera">App扫码需要用到拍摄权限</string>
|
||||
<string name="tips_scan_code">将二维码/条形码放入框内,即可自动扫码</string>
|
||||
<string name="qr_code">二维码</string>
|
||||
<string name="bar_code">条形码</string>
|
||||
</resources>
|
||||
17
app/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="windowActionBar">false</item>
|
||||
</style>
|
||||
|
||||
<style name="OnClick">
|
||||
<item name="android:clickable">true</item>
|
||||
<item name="android:onClick">OnClick</item>
|
||||
</style>
|
||||
</resources>
|
||||
17
app/src/test/java/com/king/zxing/app/ExampleUnitTest.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.king.zxing.app;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
37
build.gradle
Normal file
@@ -0,0 +1,37 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
apply from: 'versions.gradle'
|
||||
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.1.3'
|
||||
classpath 'com.novoda:bintray-release:0.8.1'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
}
|
||||
}
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
addRepos(repositories)
|
||||
}
|
||||
}
|
||||
|
||||
task clean(type: Delete) {
|
||||
delete rootProject.buildDir
|
||||
}
|
||||
|
||||
allprojects {
|
||||
tasks.withType(Javadoc) {
|
||||
options{
|
||||
encoding "UTF-8"
|
||||
charSet 'UTF-8'
|
||||
links "http://docs.oracle.com/javase/8/docs/api"
|
||||
}
|
||||
}
|
||||
}
|
||||
13
gradle.properties
Normal file
@@ -0,0 +1,13 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs = -Xmx1536m
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#Mon Aug 06 16:59:20 CST 2018
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
||||
172
gradlew
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
84
gradlew.bat
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
1
lib/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
11
lib/bintray.gradle
Normal file
@@ -0,0 +1,11 @@
|
||||
apply plugin: 'com.novoda.bintray-release'
|
||||
|
||||
//添加
|
||||
publish {
|
||||
userOrg = 'jenly'//bintray.com用户名
|
||||
groupId = 'com.king.zxing'//jcenter上的路径
|
||||
artifactId = 'zxing-lite'//项目名称
|
||||
publishVersion = app_version.versionName//版本号
|
||||
desc = 'ZXingLite for Android'//描述
|
||||
website = 'https://github.com/jenly1314/ZXingLite'//网站
|
||||
}
|
||||
33
lib/build.gradle
Normal file
@@ -0,0 +1,33 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply from: 'bintray.gradle'
|
||||
|
||||
android {
|
||||
compileSdkVersion build_versions.compileSdk
|
||||
buildToolsVersion build_versions.buildTools
|
||||
defaultConfig {
|
||||
minSdkVersion build_versions.minSdk
|
||||
targetSdkVersion build_versions.targetSdk
|
||||
versionCode app_version.versionCode
|
||||
versionName app_version.versionName
|
||||
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
testImplementation deps.test.junit
|
||||
androidTestImplementation deps.test.runner
|
||||
androidTestImplementation deps.test.espresso
|
||||
|
||||
compileOnly deps.support.appcompat
|
||||
api deps.zxing
|
||||
}
|
||||
21
lib/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.king.zxing;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
assertEquals("com.king.zxing.test", appContext.getPackageName());
|
||||
}
|
||||
}
|
||||
20
lib/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.king.zxing">
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.FLASHLIGHT"/>
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS"/>
|
||||
<!-- unavailable in API 23 -->
|
||||
<uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name="com.king.zxing.CaptureActivity"
|
||||
android:screenOrientation="portrait"/>
|
||||
</application>
|
||||
</manifest>
|
||||
88
lib/src/main/java/com/king/zxing/AmbientLightManager.java
Normal file
@@ -0,0 +1,88 @@
|
||||
package com.king.zxing;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
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 com.king.zxing.camera.CameraManager;
|
||||
import com.king.zxing.camera.FrontLightMode;
|
||||
|
||||
/**
|
||||
* Detects ambient light and switches on the front light when very dark, and off again when sufficiently light.
|
||||
*
|
||||
* @author Sean Owen
|
||||
* @author Nikolaus Huber
|
||||
*/
|
||||
final class AmbientLightManager implements SensorEventListener {
|
||||
|
||||
private static final float TOO_DARK_LUX = 45.0f;
|
||||
private static final float BRIGHT_ENOUGH_LUX = 450.0f;
|
||||
|
||||
private final Context context;
|
||||
private CameraManager cameraManager;
|
||||
private Sensor lightSensor;
|
||||
|
||||
AmbientLightManager(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
void start(CameraManager cameraManager) {
|
||||
this.cameraManager = cameraManager;
|
||||
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (FrontLightMode.readPref(sharedPrefs) == FrontLightMode.AUTO) {
|
||||
SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
|
||||
lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
|
||||
if (lightSensor != null) {
|
||||
sensorManager.registerListener(this, lightSensor, SensorManager.SENSOR_DELAY_NORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if (lightSensor != null) {
|
||||
SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
|
||||
sensorManager.unregisterListener(this);
|
||||
cameraManager = null;
|
||||
lightSensor = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent sensorEvent) {
|
||||
float ambientLightLux = sensorEvent.values[0];
|
||||
if (cameraManager != null) {
|
||||
if (ambientLightLux <= TOO_DARK_LUX) {
|
||||
cameraManager.setTorch(true);
|
||||
} else if (ambientLightLux >= BRIGHT_ENOUGH_LUX) {
|
||||
cameraManager.setTorch(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
}
|
||||
136
lib/src/main/java/com/king/zxing/BeepManager.java
Normal file
@@ -0,0 +1,136 @@
|
||||
package com.king.zxing;
|
||||
/*
|
||||
* Copyright (C) 2010 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.os.Build;
|
||||
import android.os.Vibrator;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Manages beeps and vibrations for {@link CaptureActivity}.
|
||||
*/
|
||||
public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable {
|
||||
|
||||
private static final String TAG = BeepManager.class.getSimpleName();
|
||||
|
||||
private static final float BEEP_VOLUME = 0.10f;
|
||||
private static final long VIBRATE_DURATION = 200L;
|
||||
|
||||
private final Activity activity;
|
||||
private MediaPlayer mediaPlayer;
|
||||
private boolean playBeep;
|
||||
private boolean vibrate;
|
||||
|
||||
BeepManager(Activity activity) {
|
||||
this.activity = activity;
|
||||
this.mediaPlayer = null;
|
||||
updatePrefs();
|
||||
}
|
||||
|
||||
public void setVibrate(boolean vibrate){
|
||||
this.vibrate = vibrate;
|
||||
}
|
||||
|
||||
public void setPlayBeep(boolean playBeep){
|
||||
this.playBeep = playBeep;
|
||||
}
|
||||
|
||||
synchronized void updatePrefs() {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||
playBeep = shouldBeep(prefs, activity);
|
||||
// vibrate = prefs.getBoolean(Preferences.KEY_VIBRATE, false);
|
||||
if (playBeep && mediaPlayer == null) {
|
||||
// The volume on STREAM_SYSTEM is not adjustable, and users found it too loud,
|
||||
// so we now play on the music stream.
|
||||
activity.setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||
mediaPlayer = buildMediaPlayer(activity);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void playBeepSoundAndVibrate() {
|
||||
if (playBeep && mediaPlayer != null) {
|
||||
mediaPlayer.start();
|
||||
}
|
||||
if (vibrate) {
|
||||
Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
vibrator.vibrate(VIBRATE_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldBeep(SharedPreferences prefs, Context activity) {
|
||||
boolean shouldPlayBeep = prefs.getBoolean(Preferences.KEY_PLAY_BEEP, true);
|
||||
if (shouldPlayBeep) {
|
||||
// See if sound settings overrides this
|
||||
AudioManager audioService = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE);
|
||||
if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
|
||||
shouldPlayBeep = false;
|
||||
}
|
||||
}
|
||||
return shouldPlayBeep;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
private MediaPlayer buildMediaPlayer(Context activity) {
|
||||
MediaPlayer mediaPlayer = new MediaPlayer();
|
||||
try (AssetFileDescriptor file = activity.getResources().openRawResourceFd(R.raw.beep)) {
|
||||
mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength());
|
||||
mediaPlayer.setOnErrorListener(this);
|
||||
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||
mediaPlayer.setLooping(false);
|
||||
mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
|
||||
mediaPlayer.prepare();
|
||||
return mediaPlayer;
|
||||
} catch (IOException ioe) {
|
||||
Log.w(TAG, ioe);
|
||||
mediaPlayer.release();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean onError(MediaPlayer mp, int what, int extra) {
|
||||
if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) {
|
||||
// we are finished, so put up an appropriate error toast if required and finish
|
||||
activity.finish();
|
||||
} else {
|
||||
// possibly media player error, so release and recreate
|
||||
close();
|
||||
updatePrefs();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() {
|
||||
if (mediaPlayer != null) {
|
||||
mediaPlayer.release();
|
||||
mediaPlayer = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
781
lib/src/main/java/com/king/zxing/CaptureActivity.java
Normal file
@@ -0,0 +1,781 @@
|
||||
package com.king.zxing;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.ResultMetadataType;
|
||||
import com.king.zxing.camera.CameraManager;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This activity opens the camera and does the actual scanning on a background thread. It draws a
|
||||
* viewfinder to help the user place the barcode correctly, shows feedback as the image processing
|
||||
* is happening, and then overlays the results when a scan is successful.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
* @author Sean Owen
|
||||
*/
|
||||
public class CaptureActivity extends Activity implements SurfaceHolder.Callback {
|
||||
|
||||
public static final String KEY_RESULT = Intents.Scan.RESULT;
|
||||
|
||||
private static final String TAG = CaptureActivity.class.getSimpleName();
|
||||
|
||||
private static final long DEFAULT_INTENT_RESULT_DURATION_MS = 1500L;
|
||||
private static final long BULK_MODE_SCAN_DELAY_MS = 1000L;
|
||||
|
||||
private static final String[] ZXING_URLS = { "http://zxing.appspot.com/scan", "zxing://scan/" };
|
||||
|
||||
private static final Collection<ResultMetadataType> DISPLAYABLE_METADATA_TYPES =
|
||||
EnumSet.of(ResultMetadataType.ISSUE_NUMBER,
|
||||
ResultMetadataType.SUGGESTED_PRICE,
|
||||
ResultMetadataType.ERROR_CORRECTION_LEVEL,
|
||||
ResultMetadataType.POSSIBLE_COUNTRY);
|
||||
|
||||
private CameraManager cameraManager;
|
||||
private CaptureActivityHandler handler;
|
||||
private Result savedResultToShow;
|
||||
private ViewfinderView viewfinderView;
|
||||
// private TextView statusView;
|
||||
// private View resultView;
|
||||
private Result lastResult;
|
||||
private boolean hasSurface;
|
||||
// private boolean copyToClipboard;
|
||||
// private IntentSource source;
|
||||
// private String sourceUrl;
|
||||
// private ScanFromWebPageManager scanFromWebPageManager;
|
||||
private Collection<BarcodeFormat> decodeFormats;
|
||||
private Map<DecodeHintType,?> decodeHints;
|
||||
private String characterSet;
|
||||
// private HistoryManager historyManager;
|
||||
private InactivityTimer inactivityTimer;
|
||||
private BeepManager beepManager;
|
||||
private AmbientLightManager ambientLightManager;
|
||||
|
||||
public ViewfinderView getViewfinderView() {
|
||||
return viewfinderView;
|
||||
}
|
||||
|
||||
public Handler getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
public CameraManager getCameraManager() {
|
||||
return cameraManager;
|
||||
}
|
||||
|
||||
public BeepManager getBeepManager() {
|
||||
return beepManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
|
||||
Window window = getWindow();
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
setContentView(getLayoutId());
|
||||
|
||||
hasSurface = false;
|
||||
inactivityTimer = new InactivityTimer(this);
|
||||
beepManager = new BeepManager(this);
|
||||
ambientLightManager = new AmbientLightManager(this);
|
||||
|
||||
// PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
|
||||
}
|
||||
|
||||
public int getLayoutId(){
|
||||
return R.layout.capture;
|
||||
}
|
||||
|
||||
public int getViewFinderViewId(){
|
||||
return R.id.viewfinder_view;
|
||||
}
|
||||
|
||||
public int getPreviewViewId(){
|
||||
return R.id.preview_view;
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫码后声音与震动管理里入口
|
||||
* @return true表示播放和震动,是否播放与震动还得看{@link BeepManager}
|
||||
*/
|
||||
public boolean isBeepSoundAndVibrate(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// historyManager must be initialized here to update the history preference
|
||||
// historyManager = new HistoryManager(this);
|
||||
// historyManager.trimHistory();
|
||||
|
||||
// CameraManager must be initialized here, not in onCreate(). This is necessary because we don't
|
||||
// want to open the camera driver and measure the screen size if we're going to show the help on
|
||||
// first launch. That led to bugs where the scanning rectangle was the wrong size and partially
|
||||
// off screen.
|
||||
cameraManager = new CameraManager(getApplication());
|
||||
viewfinderView = findViewById(getViewFinderViewId());
|
||||
viewfinderView.setCameraManager(cameraManager);
|
||||
|
||||
// resultView = findViewById(R.id.result_view);
|
||||
// statusView = findViewById(R.id.status_view);
|
||||
|
||||
handler = null;
|
||||
lastResult = null;
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
if (prefs.getBoolean(Preferences.KEY_DISABLE_AUTO_ORIENTATION, true)) {
|
||||
setRequestedOrientation(getCurrentOrientation());
|
||||
} else {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
|
||||
}
|
||||
|
||||
resetStatusView();
|
||||
|
||||
|
||||
beepManager.updatePrefs();
|
||||
ambientLightManager.start(cameraManager);
|
||||
|
||||
inactivityTimer.onResume();
|
||||
|
||||
Intent intent = getIntent();
|
||||
|
||||
// copyToClipboard = prefs.getBoolean(Preferences.KEY_COPY_TO_CLIPBOARD, true)
|
||||
// && (intent == null || intent.getBooleanExtra(Intents.Scan.SAVE_HISTORY, true));
|
||||
//
|
||||
// source = IntentSource.NONE;
|
||||
// sourceUrl = null;
|
||||
// scanFromWebPageManager = null;
|
||||
decodeFormats = null;
|
||||
characterSet = null;
|
||||
|
||||
if (intent != null) {
|
||||
|
||||
String action = intent.getAction();
|
||||
String dataString = intent.getDataString();
|
||||
|
||||
if (Intents.Scan.ACTION.equals(action)) {
|
||||
|
||||
// Scan the formats the intent requested, and return the result to the calling activity.
|
||||
// source = IntentSource.NATIVE_APP_INTENT;
|
||||
decodeFormats = DecodeFormatManager.parseDecodeFormats(intent);
|
||||
decodeHints = DecodeHintManager.parseDecodeHints(intent);
|
||||
|
||||
if (intent.hasExtra(Intents.Scan.WIDTH) && intent.hasExtra(Intents.Scan.HEIGHT)) {
|
||||
int width = intent.getIntExtra(Intents.Scan.WIDTH, 0);
|
||||
int height = intent.getIntExtra(Intents.Scan.HEIGHT, 0);
|
||||
if (width > 0 && height > 0) {
|
||||
cameraManager.setManualFramingRect(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
if (intent.hasExtra(Intents.Scan.CAMERA_ID)) {
|
||||
int cameraId = intent.getIntExtra(Intents.Scan.CAMERA_ID, -1);
|
||||
if (cameraId >= 0) {
|
||||
cameraManager.setManualCameraId(cameraId);
|
||||
}
|
||||
}
|
||||
|
||||
// String customPromptMessage = intent.getStringExtra(Intents.Scan.PROMPT_MESSAGE);
|
||||
// if (customPromptMessage != null) {
|
||||
// statusView.setText(customPromptMessage);
|
||||
// }
|
||||
|
||||
} else if (dataString != null &&
|
||||
dataString.contains("http://www.google") &&
|
||||
dataString.contains("/m/products/scan")) {
|
||||
|
||||
// Scan only products and send the result to mobile Product Search.
|
||||
// source = IntentSource.PRODUCT_SEARCH_LINK;
|
||||
// sourceUrl = dataString;
|
||||
decodeFormats = DecodeFormatManager.PRODUCT_FORMATS;
|
||||
|
||||
} else if (isZXingURL(dataString)) {
|
||||
|
||||
// Scan formats requested in query string (all formats if none specified).
|
||||
// If a return URL is specified, send the results there. Otherwise, handle it ourselves.
|
||||
// source = IntentSource.ZXING_LINK;
|
||||
// sourceUrl = dataString;
|
||||
Uri inputUri = Uri.parse(dataString);
|
||||
// scanFromWebPageManager = new ScanFromWebPageManager(inputUri);
|
||||
decodeFormats = DecodeFormatManager.parseDecodeFormats(inputUri);
|
||||
// Allow a sub-set of the hints to be specified by the caller.
|
||||
decodeHints = DecodeHintManager.parseDecodeHints(inputUri);
|
||||
|
||||
}
|
||||
|
||||
characterSet = intent.getStringExtra(Intents.Scan.CHARACTER_SET);
|
||||
|
||||
}
|
||||
|
||||
SurfaceView surfaceView = findViewById(getPreviewViewId());
|
||||
SurfaceHolder surfaceHolder = surfaceView.getHolder();
|
||||
if (hasSurface) {
|
||||
// The activity was paused but not stopped, so the surface still exists. Therefore
|
||||
// surfaceCreated() won't be called, so init the camera here.
|
||||
initCamera(surfaceHolder);
|
||||
} else {
|
||||
// Install the callback and wait for surfaceCreated() to init the camera.
|
||||
surfaceHolder.addCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
private int getCurrentOrientation() {
|
||||
int rotation = getWindowManager().getDefaultDisplay().getRotation();
|
||||
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
switch (rotation) {
|
||||
case Surface.ROTATION_0:
|
||||
case Surface.ROTATION_90:
|
||||
return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
|
||||
default:
|
||||
return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
|
||||
}
|
||||
} else {
|
||||
switch (rotation) {
|
||||
case Surface.ROTATION_0:
|
||||
case Surface.ROTATION_270:
|
||||
return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
|
||||
default:
|
||||
return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isZXingURL(String dataString) {
|
||||
if (dataString == null) {
|
||||
return false;
|
||||
}
|
||||
for (String url : ZXING_URLS) {
|
||||
if (dataString.startsWith(url)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
if (handler != null) {
|
||||
handler.quitSynchronously();
|
||||
handler = null;
|
||||
}
|
||||
inactivityTimer.onPause();
|
||||
ambientLightManager.stop();
|
||||
beepManager.close();
|
||||
cameraManager.closeDriver();
|
||||
//historyManager = null; // Keep for onActivityResult
|
||||
if (!hasSurface) {
|
||||
SurfaceView surfaceView = findViewById(R.id.preview_view);
|
||||
SurfaceHolder surfaceHolder = surfaceView.getHolder();
|
||||
surfaceHolder.removeCallback(this);
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
inactivityTimer.shutdown();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
// if (source == IntentSource.NATIVE_APP_INTENT) {
|
||||
// setResult(RESULT_CANCELED);
|
||||
// finish();
|
||||
// return true;
|
||||
// }
|
||||
// if ((source == IntentSource.NONE || source == IntentSource.ZXING_LINK) && lastResult != null) {
|
||||
// restartPreviewAfterDelay(0L);
|
||||
// return true;
|
||||
// }
|
||||
break;
|
||||
case KeyEvent.KEYCODE_FOCUS:
|
||||
case KeyEvent.KEYCODE_CAMERA:
|
||||
// Handle these events so they don't launch the Camera app
|
||||
return true;
|
||||
// Use volume up/down to turn on light
|
||||
case KeyEvent.KEYCODE_VOLUME_DOWN:
|
||||
cameraManager.setTorch(false);
|
||||
return true;
|
||||
case KeyEvent.KEYCODE_VOLUME_UP:
|
||||
cameraManager.setTorch(true);
|
||||
return true;
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// MenuInflater menuInflater = getMenuInflater();
|
||||
// menuInflater.inflate(R.menu.capture, menu);
|
||||
// return super.onCreateOptionsMenu(menu);
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean onOptionsItemSelected(MenuItem item) {
|
||||
// Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
// intent.addFlags(Intents.FLAG_NEW_DOC);
|
||||
// switch (item.getItemId()) {
|
||||
// case R.id.menu_share:
|
||||
// intent.setClassName(this, ShareActivity.class.getName());
|
||||
// startActivity(intent);
|
||||
// break;
|
||||
// case R.id.menu_history:
|
||||
// intent.setClassName(this, HistoryActivity.class.getName());
|
||||
// startActivityForResult(intent, HISTORY_REQUEST_CODE);
|
||||
// break;
|
||||
// case R.id.menu_settings:
|
||||
// intent.setClassName(this, PreferencesActivity.class.getName());
|
||||
// startActivity(intent);
|
||||
// break;
|
||||
// case R.id.menu_help:
|
||||
// intent.setClassName(this, HelpActivity.class.getName());
|
||||
// startActivity(intent);
|
||||
// break;
|
||||
// default:
|
||||
// return super.onOptionsItemSelected(item);
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// @Override
|
||||
// public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
// if (resultCode == RESULT_OK && requestCode == HISTORY_REQUEST_CODE && historyManager != null) {
|
||||
// int itemNumber = intent.getIntExtra(Intents.History.ITEM_NUMBER, -1);
|
||||
// if (itemNumber >= 0) {
|
||||
// HistoryItem historyItem = historyManager.buildHistoryItem(itemNumber);
|
||||
// decodeOrStoreSavedBitmap(null, historyItem.getResult());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
private void decodeOrStoreSavedBitmap(Bitmap bitmap, Result result) {
|
||||
// Bitmap isn't used yet -- will be used soon
|
||||
if (handler == null) {
|
||||
savedResultToShow = result;
|
||||
} else {
|
||||
if (result != null) {
|
||||
savedResultToShow = result;
|
||||
}
|
||||
if (savedResultToShow != null) {
|
||||
Message message = Message.obtain(handler, R.id.decode_succeeded, savedResultToShow);
|
||||
handler.sendMessage(message);
|
||||
}
|
||||
savedResultToShow = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
if (holder == null) {
|
||||
Log.e(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!");
|
||||
}
|
||||
if (!hasSurface) {
|
||||
hasSurface = true;
|
||||
initCamera(holder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
hasSurface = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* A valid barcode has been found, so give an indication of success and show the results.
|
||||
*
|
||||
* @param rawResult The contents of the barcode.
|
||||
* @param scaleFactor amount by which thumbnail was scaled
|
||||
* @param barcode A greyscale bitmap of the camera data which was decoded.
|
||||
*/
|
||||
public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor) {
|
||||
inactivityTimer.onActivity();
|
||||
lastResult = rawResult;
|
||||
if(isBeepSoundAndVibrate()){
|
||||
beepManager.playBeepSoundAndVibrate();
|
||||
}
|
||||
String resultString = rawResult.getText();
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(KEY_RESULT,resultString);
|
||||
setResult(RESULT_OK,intent);
|
||||
finish();
|
||||
|
||||
|
||||
// ResultHandler resultHandler = ResultHandlerFactory.makeResultHandler(this, rawResult);
|
||||
|
||||
// boolean fromLiveScan = barcode != null;
|
||||
// if (fromLiveScan) {
|
||||
//// historyManager.addHistoryItem(rawResult, resultHandler);
|
||||
// // Then not from history, so beep/vibrate and we have an image to draw on
|
||||
// beepManager.playBeepSoundAndVibrate();
|
||||
// drawResultPoints(barcode, scaleFactor, rawResult);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// switch (source) {
|
||||
// case NATIVE_APP_INTENT:
|
||||
// case PRODUCT_SEARCH_LINK:
|
||||
// handleDecodeExternally(rawResult, resultHandler, barcode);
|
||||
// break;
|
||||
// case ZXING_LINK:
|
||||
// if (scanFromWebPageManager == null || !scanFromWebPageManager.isScanFromWebPage()) {
|
||||
// handleDecodeInternally(rawResult, resultHandler, barcode);
|
||||
// } else {
|
||||
// handleDecodeExternally(rawResult, resultHandler, barcode);
|
||||
// }
|
||||
// break;
|
||||
// case NONE:
|
||||
// SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
// if (fromLiveScan && prefs.getBoolean(PreferencesActivity.KEY_BULK_MODE, false)) {
|
||||
// Toast.makeText(getApplicationContext(),
|
||||
// getResources().getString(R.string.msg_bulk_mode_scanned) + " (" + rawResult.getText() + ')',
|
||||
// Toast.LENGTH_SHORT).show();
|
||||
// maybeSetClipboard(resultHandler);
|
||||
// // Wait a moment or else it will scan the same barcode continuously about 3 times
|
||||
// restartPreviewAfterDelay(BULK_MODE_SCAN_DELAY_MS);
|
||||
// } else {
|
||||
// handleDecodeInternally(rawResult, resultHandler, barcode);
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Superimpose a line for 1D or dots for 2D to highlight the key features of the barcode.
|
||||
// *
|
||||
// * @param barcode A bitmap of the captured image.
|
||||
// * @param scaleFactor amount by which thumbnail was scaled
|
||||
// * @param rawResult The decoded results which contains the points to draw.
|
||||
// */
|
||||
// private void drawResultPoints(Bitmap barcode, float scaleFactor, Result rawResult) {
|
||||
// ResultPoint[] points = rawResult.getResultPoints();
|
||||
// if (points != null && points.length > 0) {
|
||||
// Canvas canvas = new Canvas(barcode);
|
||||
// Paint paint = new Paint();
|
||||
// paint.setColor(getResources().getColor(R.color.result_points));
|
||||
// if (points.length == 2) {
|
||||
// paint.setStrokeWidth(4.0f);
|
||||
// drawLine(canvas, paint, points[0], points[1], scaleFactor);
|
||||
// } else if (points.length == 4 &&
|
||||
// (rawResult.getBarcodeFormat() == BarcodeFormat.UPC_A ||
|
||||
// rawResult.getBarcodeFormat() == BarcodeFormat.EAN_13)) {
|
||||
// // Hacky special case -- draw two lines, for the barcode and metadata
|
||||
// drawLine(canvas, paint, points[0], points[1], scaleFactor);
|
||||
// drawLine(canvas, paint, points[2], points[3], scaleFactor);
|
||||
// } else {
|
||||
// paint.setStrokeWidth(10.0f);
|
||||
// for (ResultPoint point : points) {
|
||||
// if (point != null) {
|
||||
// canvas.drawPoint(scaleFactor * point.getX(), scaleFactor * point.getY(), paint);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private static void drawLine(Canvas canvas, Paint paint, ResultPoint a, ResultPoint b, float scaleFactor) {
|
||||
// if (a != null && b != null) {
|
||||
// canvas.drawLine(scaleFactor * a.getX(),
|
||||
// scaleFactor * a.getY(),
|
||||
// scaleFactor * b.getX(),
|
||||
// scaleFactor * b.getY(),
|
||||
// paint);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Put up our own UI for how to handle the decoded contents.
|
||||
// private void handleDecodeInternally(Result rawResult, ResultHandler resultHandler, Bitmap barcode) {
|
||||
//
|
||||
// maybeSetClipboard(resultHandler);
|
||||
//
|
||||
// SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
//
|
||||
// if (resultHandler.getDefaultButtonID() != null && prefs.getBoolean(PreferencesActivity.KEY_AUTO_OPEN_WEB, false)) {
|
||||
// resultHandler.handleButtonPress(resultHandler.getDefaultButtonID());
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// statusView.setVisibility(View.GONE);
|
||||
// viewfinderView.setVisibility(View.GONE);
|
||||
// resultView.setVisibility(View.VISIBLE);
|
||||
//
|
||||
// ImageView barcodeImageView = (ImageView) findViewById(R.id.barcode_image_view);
|
||||
// if (barcode == null) {
|
||||
// barcodeImageView.setImageBitmap(BitmapFactory.decodeResource(getResources(),
|
||||
// R.drawable.launcher_icon));
|
||||
// } else {
|
||||
// barcodeImageView.setImageBitmap(barcode);
|
||||
// }
|
||||
//
|
||||
// TextView formatTextView = (TextView) findViewById(R.id.format_text_view);
|
||||
// formatTextView.setText(rawResult.getBarcodeFormat().toString());
|
||||
//
|
||||
// TextView typeTextView = (TextView) findViewById(R.id.type_text_view);
|
||||
// typeTextView.setText(resultHandler.getType().toString());
|
||||
//
|
||||
// DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
|
||||
// TextView timeTextView = (TextView) findViewById(R.id.time_text_view);
|
||||
// timeTextView.setText(formatter.format(rawResult.getTimestamp()));
|
||||
//
|
||||
//
|
||||
// TextView metaTextView = (TextView) findViewById(R.id.meta_text_view);
|
||||
// View metaTextViewLabel = findViewById(R.id.meta_text_view_label);
|
||||
// metaTextView.setVisibility(View.GONE);
|
||||
// metaTextViewLabel.setVisibility(View.GONE);
|
||||
// Map<ResultMetadataType,Object> metadata = rawResult.getResultMetadata();
|
||||
// if (metadata != null) {
|
||||
// StringBuilder metadataText = new StringBuilder(20);
|
||||
// for (Map.Entry<ResultMetadataType,Object> entry : metadata.entrySet()) {
|
||||
// if (DISPLAYABLE_METADATA_TYPES.contains(entry.getKey())) {
|
||||
// metadataText.append(entry.getValue()).append('\n');
|
||||
// }
|
||||
// }
|
||||
// if (metadataText.length() > 0) {
|
||||
// metadataText.setLength(metadataText.length() - 1);
|
||||
// metaTextView.setText(metadataText);
|
||||
// metaTextView.setVisibility(View.VISIBLE);
|
||||
// metaTextViewLabel.setVisibility(View.VISIBLE);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// CharSequence displayContents = resultHandler.getDisplayContents();
|
||||
// TextView contentsTextView = (TextView) findViewById(R.id.contents_text_view);
|
||||
// contentsTextView.setText(displayContents);
|
||||
// int scaledSize = Math.max(22, 32 - displayContents.length() / 4);
|
||||
// contentsTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, scaledSize);
|
||||
//
|
||||
// TextView supplementTextView = (TextView) findViewById(R.id.contents_supplement_text_view);
|
||||
// supplementTextView.setText("");
|
||||
// supplementTextView.setOnClickListener(null);
|
||||
// if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(
|
||||
// PreferencesActivity.KEY_SUPPLEMENTAL, true)) {
|
||||
// SupplementalInfoRetriever.maybeInvokeRetrieval(supplementTextView,
|
||||
// resultHandler.getResult(),
|
||||
// historyManager,
|
||||
// this);
|
||||
// }
|
||||
//
|
||||
// int buttonCount = resultHandler.getButtonCount();
|
||||
// ViewGroup buttonView = (ViewGroup) findViewById(R.id.result_button_view);
|
||||
// buttonView.requestFocus();
|
||||
// for (int x = 0; x < ResultHandler.MAX_BUTTON_COUNT; x++) {
|
||||
// TextView button = (TextView) buttonView.getChildAt(x);
|
||||
// if (x < buttonCount) {
|
||||
// button.setVisibility(View.VISIBLE);
|
||||
// button.setText(resultHandler.getButtonText(x));
|
||||
// button.setOnClickListener(new ResultButtonListener(resultHandler, x));
|
||||
// } else {
|
||||
// button.setVisibility(View.GONE);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
//
|
||||
// // Briefly show the contents of the barcode, then handle the result outside Barcode Scanner.
|
||||
// private void handleDecodeExternally(Result rawResult, ResultHandler resultHandler, Bitmap barcode) {
|
||||
//
|
||||
// if (barcode != null) {
|
||||
// viewfinderView.drawResultBitmap(barcode);
|
||||
// }
|
||||
//
|
||||
// long resultDurationMS;
|
||||
// if (getIntent() == null) {
|
||||
// resultDurationMS = DEFAULT_INTENT_RESULT_DURATION_MS;
|
||||
// } else {
|
||||
// resultDurationMS = getIntent().getLongExtra(Intents.Scan.RESULT_DISPLAY_DURATION_MS,
|
||||
// DEFAULT_INTENT_RESULT_DURATION_MS);
|
||||
// }
|
||||
//
|
||||
// if (resultDurationMS > 0) {
|
||||
// String rawResultString = String.valueOf(rawResult);
|
||||
// if (rawResultString.length() > 32) {
|
||||
// rawResultString = rawResultString.substring(0, 32) + " ...";
|
||||
// }
|
||||
// statusView.setText(getString(resultHandler.getDisplayTitle()) + " : " + rawResultString);
|
||||
// }
|
||||
//
|
||||
// maybeSetClipboard(resultHandler);
|
||||
//
|
||||
// switch (source) {
|
||||
// case NATIVE_APP_INTENT:
|
||||
// // Hand back whatever action they requested - this can be changed to Intents.Scan.ACTION when
|
||||
// // the deprecated intent is retired.
|
||||
// Intent intent = new Intent(getIntent().getAction());
|
||||
// intent.addFlags(Intents.FLAG_NEW_DOC);
|
||||
// intent.putExtra(Intents.Scan.RESULT, rawResult.toString());
|
||||
// intent.putExtra(Intents.Scan.RESULT_FORMAT, rawResult.getBarcodeFormat().toString());
|
||||
// byte[] rawBytes = rawResult.getRawBytes();
|
||||
// if (rawBytes != null && rawBytes.length > 0) {
|
||||
// intent.putExtra(Intents.Scan.RESULT_BYTES, rawBytes);
|
||||
// }
|
||||
// Map<ResultMetadataType, ?> metadata = rawResult.getResultMetadata();
|
||||
// if (metadata != null) {
|
||||
// if (metadata.containsKey(ResultMetadataType.UPC_EAN_EXTENSION)) {
|
||||
// intent.putExtra(Intents.Scan.RESULT_UPC_EAN_EXTENSION,
|
||||
// metadata.get(ResultMetadataType.UPC_EAN_EXTENSION).toString());
|
||||
// }
|
||||
// Number orientation = (Number) metadata.get(ResultMetadataType.ORIENTATION);
|
||||
// if (orientation != null) {
|
||||
// intent.putExtra(Intents.Scan.RESULT_ORIENTATION, orientation.intValue());
|
||||
// }
|
||||
// String ecLevel = (String) metadata.get(ResultMetadataType.ERROR_CORRECTION_LEVEL);
|
||||
// if (ecLevel != null) {
|
||||
// intent.putExtra(Intents.Scan.RESULT_ERROR_CORRECTION_LEVEL, ecLevel);
|
||||
// }
|
||||
// @SuppressWarnings("unchecked")
|
||||
// Iterable<byte[]> byteSegments = (Iterable<byte[]>) metadata.get(ResultMetadataType.BYTE_SEGMENTS);
|
||||
// if (byteSegments != null) {
|
||||
// int i = 0;
|
||||
// for (byte[] byteSegment : byteSegments) {
|
||||
// intent.putExtra(Intents.Scan.RESULT_BYTE_SEGMENTS_PREFIX + i, byteSegment);
|
||||
// i++;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// sendReplyMessage(R.id.return_scan_result, intent, resultDurationMS);
|
||||
// break;
|
||||
//
|
||||
// case PRODUCT_SEARCH_LINK:
|
||||
// // Reformulate the URL which triggered us into a query, so that the request goes to the same
|
||||
// // TLD as the scan URL.
|
||||
// int end = sourceUrl.lastIndexOf("/scan");
|
||||
// String productReplyURL = sourceUrl.substring(0, end) + "?q=" +
|
||||
// resultHandler.getDisplayContents() + "&source=zxing";
|
||||
// sendReplyMessage(R.id.launch_product_query, productReplyURL, resultDurationMS);
|
||||
// break;
|
||||
//
|
||||
// case ZXING_LINK:
|
||||
// if (scanFromWebPageManager != null && scanFromWebPageManager.isScanFromWebPage()) {
|
||||
// String linkReplyURL = scanFromWebPageManager.buildReplyURL(rawResult, resultHandler);
|
||||
// scanFromWebPageManager = null;
|
||||
// sendReplyMessage(R.id.launch_product_query, linkReplyURL, resultDurationMS);
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private void maybeSetClipboard(ResultHandler resultHandler) {
|
||||
// if (copyToClipboard && !resultHandler.areContentsSecure()) {
|
||||
// ClipboardInterface.setText(resultHandler.getDisplayContents(), this);
|
||||
// }
|
||||
// }
|
||||
|
||||
private void sendReplyMessage(int id, Object arg, long delayMS) {
|
||||
if (handler != null) {
|
||||
Message message = Message.obtain(handler, id, arg);
|
||||
if (delayMS > 0L) {
|
||||
handler.sendMessageDelayed(message, delayMS);
|
||||
} else {
|
||||
handler.sendMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void initCamera(SurfaceHolder surfaceHolder) {
|
||||
if (surfaceHolder == null) {
|
||||
throw new IllegalStateException("No SurfaceHolder provided");
|
||||
}
|
||||
if (cameraManager.isOpen()) {
|
||||
Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
cameraManager.openDriver(surfaceHolder);
|
||||
// Creating the handler starts the preview, which can also throw a RuntimeException.
|
||||
if (handler == null) {
|
||||
handler = new CaptureActivityHandler(this, decodeFormats, decodeHints, characterSet, cameraManager);
|
||||
}
|
||||
decodeOrStoreSavedBitmap(null, null);
|
||||
} catch (IOException ioe) {
|
||||
Log.w(TAG, ioe);
|
||||
// displayFrameworkBugMessageAndExit();
|
||||
} catch (RuntimeException e) {
|
||||
// Barcode Scanner has seen crashes in the wild of this variety:
|
||||
// java.?lang.?RuntimeException: Fail to connect to camera service
|
||||
Log.w(TAG, "Unexpected error initializing camera", e);
|
||||
// displayFrameworkBugMessageAndExit();
|
||||
}
|
||||
}
|
||||
|
||||
// private void displayFrameworkBugMessageAndExit() {
|
||||
// AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
// builder.setTitle(getString(R.string.app_name));
|
||||
// builder.setMessage(getString(R.string.msg_camera_framework_bug));
|
||||
// builder.setPositiveButton(R.string.button_ok, new FinishListener(this));
|
||||
// builder.setOnCancelListener(new FinishListener(this));
|
||||
// builder.show();
|
||||
// }
|
||||
|
||||
public void restartPreviewAfterDelay(long delayMS) {
|
||||
if (handler != null) {
|
||||
handler.sendEmptyMessageDelayed(R.id.restart_preview, delayMS);
|
||||
}
|
||||
resetStatusView();
|
||||
}
|
||||
|
||||
private void resetStatusView() {
|
||||
// resultView.setVisibility(View.GONE);
|
||||
// statusView.setText(R.string.msg_default_status);
|
||||
// statusView.setVisibility(View.VISIBLE);
|
||||
viewfinderView.setVisibility(View.VISIBLE);
|
||||
lastResult = null;
|
||||
}
|
||||
|
||||
public void drawViewfinder() {
|
||||
viewfinderView.drawViewfinder();
|
||||
}
|
||||
}
|
||||
169
lib/src/main/java/com/king/zxing/CaptureActivityHandler.java
Normal file
@@ -0,0 +1,169 @@
|
||||
package com.king.zxing;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.provider.Browser;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.Result;
|
||||
import com.king.zxing.camera.CameraManager;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class handles all the messaging which comprises the state machine for capture.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class CaptureActivityHandler extends Handler {
|
||||
|
||||
private static final String TAG = CaptureActivityHandler.class.getSimpleName();
|
||||
|
||||
private final CaptureActivity activity;
|
||||
private final DecodeThread decodeThread;
|
||||
private State state;
|
||||
private final CameraManager cameraManager;
|
||||
|
||||
private enum State {
|
||||
PREVIEW,
|
||||
SUCCESS,
|
||||
DONE
|
||||
}
|
||||
|
||||
CaptureActivityHandler(CaptureActivity activity,
|
||||
Collection<BarcodeFormat> decodeFormats,
|
||||
Map<DecodeHintType,?> baseHints,
|
||||
String characterSet,
|
||||
CameraManager cameraManager) {
|
||||
this.activity = activity;
|
||||
decodeThread = new DecodeThread(activity, decodeFormats, baseHints, characterSet,
|
||||
new ViewfinderResultPointCallback(activity.getViewfinderView()));
|
||||
decodeThread.start();
|
||||
state = State.SUCCESS;
|
||||
|
||||
// Start ourselves capturing previews and decoding.
|
||||
this.cameraManager = cameraManager;
|
||||
cameraManager.startPreview();
|
||||
restartPreviewAndDecode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
if (message.what == R.id.restart_preview) {
|
||||
restartPreviewAndDecode();
|
||||
|
||||
} else if (message.what == R.id.decode_succeeded) {
|
||||
state = State.SUCCESS;
|
||||
Bundle bundle = message.getData();
|
||||
Bitmap barcode = null;
|
||||
float scaleFactor = 1.0f;
|
||||
if (bundle != null) {
|
||||
byte[] compressedBitmap = bundle.getByteArray(DecodeThread.BARCODE_BITMAP);
|
||||
if (compressedBitmap != null) {
|
||||
barcode = BitmapFactory.decodeByteArray(compressedBitmap, 0, compressedBitmap.length, null);
|
||||
// Mutable copy:
|
||||
barcode = barcode.copy(Bitmap.Config.ARGB_8888, true);
|
||||
}
|
||||
scaleFactor = bundle.getFloat(DecodeThread.BARCODE_SCALED_FACTOR);
|
||||
}
|
||||
activity.handleDecode((Result) message.obj, barcode, scaleFactor);
|
||||
|
||||
} else if (message.what == R.id.decode_failed) {// We're decoding as fast as possible, so when one decode fails, start another.
|
||||
state = State.PREVIEW;
|
||||
cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
|
||||
|
||||
} else if (message.what == R.id.return_scan_result) {
|
||||
activity.setResult(Activity.RESULT_OK, (Intent) message.obj);
|
||||
activity.finish();
|
||||
|
||||
} else if (message.what == R.id.launch_product_query) {
|
||||
String url = (String) message.obj;
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.addFlags(Intents.FLAG_NEW_DOC);
|
||||
intent.setData(Uri.parse(url));
|
||||
|
||||
ResolveInfo resolveInfo =
|
||||
activity.getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||
String browserPackageName = null;
|
||||
if (resolveInfo != null && resolveInfo.activityInfo != null) {
|
||||
browserPackageName = resolveInfo.activityInfo.packageName;
|
||||
Log.d(TAG, "Using browser in package " + browserPackageName);
|
||||
}
|
||||
|
||||
// Needed for default Android browser / Chrome only apparently
|
||||
if (browserPackageName != null) {
|
||||
switch (browserPackageName) {
|
||||
case "com.android.browser":
|
||||
case "com.android.chrome":
|
||||
intent.setPackage(browserPackageName);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(Browser.EXTRA_APPLICATION_ID, browserPackageName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
activity.startActivity(intent);
|
||||
} catch (ActivityNotFoundException ignored) {
|
||||
Log.w(TAG, "Can't find anything to handle VIEW of URI " + url);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void quitSynchronously() {
|
||||
state = State.DONE;
|
||||
cameraManager.stopPreview();
|
||||
Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);
|
||||
quit.sendToTarget();
|
||||
try {
|
||||
// Wait at most half a second; should be enough time, and onPause() will timeout quickly
|
||||
decodeThread.join(500L);
|
||||
} catch (InterruptedException e) {
|
||||
// continue
|
||||
}
|
||||
|
||||
// Be absolutely sure we don't send any queued up messages
|
||||
removeMessages(R.id.decode_succeeded);
|
||||
removeMessages(R.id.decode_failed);
|
||||
}
|
||||
|
||||
private void restartPreviewAndDecode() {
|
||||
if (state == State.SUCCESS) {
|
||||
state = State.PREVIEW;
|
||||
cameraManager.requestPreviewFrame(decodeThread.getHandler(), R.id.decode);
|
||||
activity.drawViewfinder();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
105
lib/src/main/java/com/king/zxing/DecodeFormatManager.java
Normal file
@@ -0,0 +1,105 @@
|
||||
package com.king.zxing;
|
||||
/*
|
||||
* Copyright (C) 2010 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
final class DecodeFormatManager {
|
||||
|
||||
private static final Pattern COMMA_PATTERN = Pattern.compile(",");
|
||||
|
||||
static final Set<BarcodeFormat> PRODUCT_FORMATS;
|
||||
static final Set<BarcodeFormat> INDUSTRIAL_FORMATS;
|
||||
private static final Set<BarcodeFormat> ONE_D_FORMATS;
|
||||
static final Set<BarcodeFormat> QR_CODE_FORMATS = EnumSet.of(BarcodeFormat.QR_CODE);
|
||||
static final Set<BarcodeFormat> DATA_MATRIX_FORMATS = EnumSet.of(BarcodeFormat.DATA_MATRIX);
|
||||
static final Set<BarcodeFormat> AZTEC_FORMATS = EnumSet.of(BarcodeFormat.AZTEC);
|
||||
static final Set<BarcodeFormat> PDF417_FORMATS = EnumSet.of(BarcodeFormat.PDF_417);
|
||||
static {
|
||||
PRODUCT_FORMATS = EnumSet.of(BarcodeFormat.UPC_A,
|
||||
BarcodeFormat.UPC_E,
|
||||
BarcodeFormat.EAN_13,
|
||||
BarcodeFormat.EAN_8,
|
||||
BarcodeFormat.RSS_14,
|
||||
BarcodeFormat.RSS_EXPANDED);
|
||||
INDUSTRIAL_FORMATS = EnumSet.of(BarcodeFormat.CODE_39,
|
||||
BarcodeFormat.CODE_93,
|
||||
BarcodeFormat.CODE_128,
|
||||
BarcodeFormat.ITF,
|
||||
BarcodeFormat.CODABAR);
|
||||
ONE_D_FORMATS = EnumSet.copyOf(PRODUCT_FORMATS);
|
||||
ONE_D_FORMATS.addAll(INDUSTRIAL_FORMATS);
|
||||
}
|
||||
private static final Map<String,Set<BarcodeFormat>> FORMATS_FOR_MODE;
|
||||
static {
|
||||
FORMATS_FOR_MODE = new HashMap<>();
|
||||
FORMATS_FOR_MODE.put(Intents.Scan.ONE_D_MODE, ONE_D_FORMATS);
|
||||
FORMATS_FOR_MODE.put(Intents.Scan.PRODUCT_MODE, PRODUCT_FORMATS);
|
||||
FORMATS_FOR_MODE.put(Intents.Scan.QR_CODE_MODE, QR_CODE_FORMATS);
|
||||
FORMATS_FOR_MODE.put(Intents.Scan.DATA_MATRIX_MODE, DATA_MATRIX_FORMATS);
|
||||
FORMATS_FOR_MODE.put(Intents.Scan.AZTEC_MODE, AZTEC_FORMATS);
|
||||
FORMATS_FOR_MODE.put(Intents.Scan.PDF417_MODE, PDF417_FORMATS);
|
||||
}
|
||||
|
||||
private DecodeFormatManager() {}
|
||||
|
||||
static Set<BarcodeFormat> parseDecodeFormats(Intent intent) {
|
||||
Iterable<String> scanFormats = null;
|
||||
CharSequence scanFormatsString = intent.getStringExtra(Intents.Scan.FORMATS);
|
||||
if (scanFormatsString != null) {
|
||||
scanFormats = Arrays.asList(COMMA_PATTERN.split(scanFormatsString));
|
||||
}
|
||||
return parseDecodeFormats(scanFormats, intent.getStringExtra(Intents.Scan.MODE));
|
||||
}
|
||||
|
||||
static Set<BarcodeFormat> parseDecodeFormats(Uri inputUri) {
|
||||
List<String> formats = inputUri.getQueryParameters(Intents.Scan.FORMATS);
|
||||
if (formats != null && formats.size() == 1 && formats.get(0) != null) {
|
||||
formats = Arrays.asList(COMMA_PATTERN.split(formats.get(0)));
|
||||
}
|
||||
return parseDecodeFormats(formats, inputUri.getQueryParameter(Intents.Scan.MODE));
|
||||
}
|
||||
|
||||
private static Set<BarcodeFormat> parseDecodeFormats(Iterable<String> scanFormats, String decodeMode) {
|
||||
if (scanFormats != null) {
|
||||
Set<BarcodeFormat> formats = EnumSet.noneOf(BarcodeFormat.class);
|
||||
try {
|
||||
for (String format : scanFormats) {
|
||||
formats.add(BarcodeFormat.valueOf(format));
|
||||
}
|
||||
return formats;
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// ignore it then
|
||||
}
|
||||
}
|
||||
if (decodeMode != null) {
|
||||
return FORMATS_FOR_MODE.get(decodeMode);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
120
lib/src/main/java/com/king/zxing/DecodeHandler.java
Normal file
@@ -0,0 +1,120 @@
|
||||
package com.king.zxing;
|
||||
/*
|
||||
* Copyright (C) 2010 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import com.google.zxing.BinaryBitmap;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.MultiFormatReader;
|
||||
import com.google.zxing.PlanarYUVLuminanceSource;
|
||||
import com.google.zxing.ReaderException;
|
||||
import com.google.zxing.Result;
|
||||
import com.google.zxing.common.HybridBinarizer;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Map;
|
||||
|
||||
final class DecodeHandler extends Handler {
|
||||
|
||||
private static final String TAG = DecodeHandler.class.getSimpleName();
|
||||
|
||||
private final CaptureActivity activity;
|
||||
private final MultiFormatReader multiFormatReader;
|
||||
private boolean running = true;
|
||||
|
||||
DecodeHandler(CaptureActivity activity, Map<DecodeHintType,Object> hints) {
|
||||
multiFormatReader = new MultiFormatReader();
|
||||
multiFormatReader.setHints(hints);
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
if (message == null || !running) {
|
||||
return;
|
||||
}
|
||||
if (message.what == R.id.decode) {
|
||||
decode((byte[]) message.obj, message.arg1, message.arg2);
|
||||
|
||||
} else if (message.what == R.id.quit) {
|
||||
running = false;
|
||||
Looper.myLooper().quit();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the data within the viewfinder rectangle, and time how long it took. For efficiency,
|
||||
* reuse the same reader objects from one decode to the next.
|
||||
*
|
||||
* @param data The YUV preview frame.
|
||||
* @param width The width of the preview frame.
|
||||
* @param height The height of the preview frame.
|
||||
*/
|
||||
private void decode(byte[] data, int width, int height) {
|
||||
long start = System.currentTimeMillis();
|
||||
Result rawResult = null;
|
||||
PlanarYUVLuminanceSource source = activity.getCameraManager().buildLuminanceSource(data, width, height);
|
||||
if (source != null) {
|
||||
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
|
||||
try {
|
||||
rawResult = multiFormatReader.decodeWithState(bitmap);
|
||||
} catch (ReaderException re) {
|
||||
// continue
|
||||
} finally {
|
||||
multiFormatReader.reset();
|
||||
}
|
||||
}
|
||||
|
||||
Handler handler = activity.getHandler();
|
||||
if (rawResult != null) {
|
||||
// Don't log the barcode contents for security.
|
||||
long end = System.currentTimeMillis();
|
||||
Log.d(TAG, "Found barcode in " + (end - start) + " ms");
|
||||
if (handler != null) {
|
||||
Message message = Message.obtain(handler, R.id.decode_succeeded, rawResult);
|
||||
Bundle bundle = new Bundle();
|
||||
bundleThumbnail(source, bundle);
|
||||
message.setData(bundle);
|
||||
message.sendToTarget();
|
||||
}
|
||||
} else {
|
||||
if (handler != null) {
|
||||
Message message = Message.obtain(handler, R.id.decode_failed);
|
||||
message.sendToTarget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void bundleThumbnail(PlanarYUVLuminanceSource source, Bundle bundle) {
|
||||
int[] pixels = source.renderThumbnail();
|
||||
int width = source.getThumbnailWidth();
|
||||
int height = source.getThumbnailHeight();
|
||||
Bitmap bitmap = Bitmap.createBitmap(pixels, 0, width, width, height, Bitmap.Config.ARGB_8888);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, out);
|
||||
bundle.putByteArray(DecodeThread.BARCODE_BITMAP, out.toByteArray());
|
||||
bundle.putFloat(DecodeThread.BARCODE_SCALED_FACTOR, (float) width / source.getWidth());
|
||||
}
|
||||
|
||||
}
|
||||
237
lib/src/main/java/com/king/zxing/DecodeHintManager.java
Normal file
@@ -0,0 +1,237 @@
|
||||
package com.king.zxing;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2013 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.zxing.DecodeHintType;
|
||||
|
||||
/**
|
||||
* @author Lachezar Dobrev
|
||||
*/
|
||||
final class DecodeHintManager {
|
||||
|
||||
private static final String TAG = DecodeHintManager.class.getSimpleName();
|
||||
|
||||
// This pattern is used in decoding integer arrays.
|
||||
private static final Pattern COMMA = Pattern.compile(",");
|
||||
|
||||
private DecodeHintManager() {}
|
||||
|
||||
/**
|
||||
* <p>Split a query string into a list of name-value pairs.</p>
|
||||
*
|
||||
* <p>This is an alternative to the {@link Uri#getQueryParameterNames()} and
|
||||
* {@link Uri#getQueryParameters(String)}, which are quirky and not suitable
|
||||
* for exist-only Uri parameters.</p>
|
||||
*
|
||||
* <p>This method ignores multiple parameters with the same name and returns the
|
||||
* first one only. This is technically incorrect, but should be acceptable due
|
||||
* to the method of processing Hints: no multiple values for a hint.</p>
|
||||
*
|
||||
* @param query query to split
|
||||
* @return name-value pairs
|
||||
*/
|
||||
private static Map<String,String> splitQuery(String query) {
|
||||
Map<String,String> map = new HashMap<>();
|
||||
int pos = 0;
|
||||
while (pos < query.length()) {
|
||||
if (query.charAt(pos) == '&') {
|
||||
// Skip consecutive ampersand separators.
|
||||
pos ++;
|
||||
continue;
|
||||
}
|
||||
int amp = query.indexOf('&', pos);
|
||||
int equ = query.indexOf('=', pos);
|
||||
if (amp < 0) {
|
||||
// This is the last element in the query, no more ampersand elements.
|
||||
String name;
|
||||
String text;
|
||||
if (equ < 0) {
|
||||
// No equal sign
|
||||
name = query.substring(pos);
|
||||
name = name.replace('+', ' '); // Preemptively decode +
|
||||
name = Uri.decode(name);
|
||||
text = "";
|
||||
} else {
|
||||
// Split name and text.
|
||||
name = query.substring(pos, equ);
|
||||
name = name.replace('+', ' '); // Preemptively decode +
|
||||
name = Uri.decode(name);
|
||||
text = query.substring(equ + 1);
|
||||
text = text.replace('+', ' '); // Preemptively decode +
|
||||
text = Uri.decode(text);
|
||||
}
|
||||
if (!map.containsKey(name)) {
|
||||
map.put(name, text);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (equ < 0 || equ > amp) {
|
||||
// No equal sign until the &: this is a simple parameter with no value.
|
||||
String name = query.substring(pos, amp);
|
||||
name = name.replace('+', ' '); // Preemptively decode +
|
||||
name = Uri.decode(name);
|
||||
if (!map.containsKey(name)) {
|
||||
map.put(name, "");
|
||||
}
|
||||
pos = amp + 1;
|
||||
continue;
|
||||
}
|
||||
String name = query.substring(pos, equ);
|
||||
name = name.replace('+', ' '); // Preemptively decode +
|
||||
name = Uri.decode(name);
|
||||
String text = query.substring(equ + 1, amp);
|
||||
text = text.replace('+', ' '); // Preemptively decode +
|
||||
text = Uri.decode(text);
|
||||
if (!map.containsKey(name)) {
|
||||
map.put(name, text);
|
||||
}
|
||||
pos = amp + 1;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
static Map<DecodeHintType,?> parseDecodeHints(Uri inputUri) {
|
||||
String query = inputUri.getEncodedQuery();
|
||||
if (query == null || query.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Extract parameters
|
||||
Map<String, String> parameters = splitQuery(query);
|
||||
|
||||
Map<DecodeHintType, Object> hints = new EnumMap<>(DecodeHintType.class);
|
||||
|
||||
for (DecodeHintType hintType: DecodeHintType.values()) {
|
||||
|
||||
if (hintType == DecodeHintType.CHARACTER_SET ||
|
||||
hintType == DecodeHintType.NEED_RESULT_POINT_CALLBACK ||
|
||||
hintType == DecodeHintType.POSSIBLE_FORMATS) {
|
||||
continue; // This hint is specified in another way
|
||||
}
|
||||
|
||||
String parameterName = hintType.name();
|
||||
String parameterText = parameters.get(parameterName);
|
||||
if (parameterText == null) {
|
||||
continue;
|
||||
}
|
||||
if (hintType.getValueType().equals(Object.class)) {
|
||||
// This is an unspecified type of hint content. Use the value as is.
|
||||
// TODO: Can we make a different assumption on this?
|
||||
hints.put(hintType, parameterText);
|
||||
continue;
|
||||
}
|
||||
if (hintType.getValueType().equals(Void.class)) {
|
||||
// Void hints are just flags: use the constant specified by DecodeHintType
|
||||
hints.put(hintType, Boolean.TRUE);
|
||||
continue;
|
||||
}
|
||||
if (hintType.getValueType().equals(String.class)) {
|
||||
// A string hint: use the decoded value.
|
||||
hints.put(hintType, parameterText);
|
||||
continue;
|
||||
}
|
||||
if (hintType.getValueType().equals(Boolean.class)) {
|
||||
// A boolean hint: a few values for false, everything else is true.
|
||||
// An empty parameter is simply a flag-style parameter, assuming true
|
||||
if (parameterText.isEmpty()) {
|
||||
hints.put(hintType, Boolean.TRUE);
|
||||
} else if ("0".equals(parameterText) ||
|
||||
"false".equalsIgnoreCase(parameterText) ||
|
||||
"no".equalsIgnoreCase(parameterText)) {
|
||||
hints.put(hintType, Boolean.FALSE);
|
||||
} else {
|
||||
hints.put(hintType, Boolean.TRUE);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
if (hintType.getValueType().equals(int[].class)) {
|
||||
// An integer array. Used to specify valid lengths.
|
||||
// Strip a trailing comma as in Java style array initialisers.
|
||||
if (!parameterText.isEmpty() && parameterText.charAt(parameterText.length() - 1) == ',') {
|
||||
parameterText = parameterText.substring(0, parameterText.length() - 1);
|
||||
}
|
||||
String[] values = COMMA.split(parameterText);
|
||||
int[] array = new int[values.length];
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
try {
|
||||
array[i] = Integer.parseInt(values[i]);
|
||||
} catch (NumberFormatException ignored) {
|
||||
Log.w(TAG, "Skipping array of integers hint " + hintType + " due to invalid numeric value: '" + values[i] + '\'');
|
||||
array = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (array != null) {
|
||||
hints.put(hintType, array);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
Log.w(TAG, "Unsupported hint type '" + hintType + "' of type " + hintType.getValueType());
|
||||
}
|
||||
|
||||
Log.i(TAG, "Hints from the URI: " + hints);
|
||||
return hints;
|
||||
}
|
||||
|
||||
static Map<DecodeHintType, Object> parseDecodeHints(Intent intent) {
|
||||
Bundle extras = intent.getExtras();
|
||||
if (extras == null || extras.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
Map<DecodeHintType,Object> hints = new EnumMap<>(DecodeHintType.class);
|
||||
|
||||
for (DecodeHintType hintType: DecodeHintType.values()) {
|
||||
|
||||
if (hintType == DecodeHintType.CHARACTER_SET ||
|
||||
hintType == DecodeHintType.NEED_RESULT_POINT_CALLBACK ||
|
||||
hintType == DecodeHintType.POSSIBLE_FORMATS) {
|
||||
continue; // This hint is specified in another way
|
||||
}
|
||||
|
||||
String hintName = hintType.name();
|
||||
if (extras.containsKey(hintName)) {
|
||||
if (hintType.getValueType().equals(Void.class)) {
|
||||
// Void hints are just flags: use the constant specified by the DecodeHintType
|
||||
hints.put(hintType, Boolean.TRUE);
|
||||
} else {
|
||||
Object hintData = extras.get(hintName);
|
||||
if (hintType.getValueType().isInstance(hintData)) {
|
||||
hints.put(hintType, hintData);
|
||||
} else {
|
||||
Log.w(TAG, "Ignoring hint " + hintType + " because it is not assignable from " + hintData);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.i(TAG, "Hints from the Intent: " + hints);
|
||||
return hints;
|
||||
}
|
||||
|
||||
}
|
||||
114
lib/src/main/java/com/king/zxing/DecodeThread.java
Normal file
@@ -0,0 +1,114 @@
|
||||
package com.king.zxing;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.DecodeHintType;
|
||||
import com.google.zxing.ResultPointCallback;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
/**
|
||||
* This thread does all the heavy lifting of decoding the images.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
final class DecodeThread extends Thread {
|
||||
|
||||
public static final String BARCODE_BITMAP = "barcode_bitmap";
|
||||
public static final String BARCODE_SCALED_FACTOR = "barcode_scaled_factor";
|
||||
|
||||
private final CaptureActivity activity;
|
||||
private final Map<DecodeHintType,Object> hints;
|
||||
private Handler handler;
|
||||
private final CountDownLatch handlerInitLatch;
|
||||
|
||||
DecodeThread(CaptureActivity activity,
|
||||
Collection<BarcodeFormat> decodeFormats,
|
||||
Map<DecodeHintType,?> baseHints,
|
||||
String characterSet,
|
||||
ResultPointCallback resultPointCallback) {
|
||||
|
||||
this.activity = activity;
|
||||
handlerInitLatch = new CountDownLatch(1);
|
||||
|
||||
hints = new EnumMap<>(DecodeHintType.class);
|
||||
if (baseHints != null) {
|
||||
hints.putAll(baseHints);
|
||||
}
|
||||
|
||||
// The prefs can't change while the thread is running, so pick them up once here.
|
||||
if (decodeFormats == null || decodeFormats.isEmpty()) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||
decodeFormats = EnumSet.noneOf(BarcodeFormat.class);
|
||||
if (prefs.getBoolean(Preferences.KEY_DECODE_1D_PRODUCT, true)) {
|
||||
decodeFormats.addAll(DecodeFormatManager.PRODUCT_FORMATS);
|
||||
}
|
||||
if (prefs.getBoolean(Preferences.KEY_DECODE_1D_INDUSTRIAL, true)) {
|
||||
decodeFormats.addAll(DecodeFormatManager.INDUSTRIAL_FORMATS);
|
||||
}
|
||||
if (prefs.getBoolean(Preferences.KEY_DECODE_QR, true)) {
|
||||
decodeFormats.addAll(DecodeFormatManager.QR_CODE_FORMATS);
|
||||
}
|
||||
if (prefs.getBoolean(Preferences.KEY_DECODE_DATA_MATRIX, true)) {
|
||||
decodeFormats.addAll(DecodeFormatManager.DATA_MATRIX_FORMATS);
|
||||
}
|
||||
if (prefs.getBoolean(Preferences.KEY_DECODE_AZTEC, false)) {
|
||||
decodeFormats.addAll(DecodeFormatManager.AZTEC_FORMATS);
|
||||
}
|
||||
if (prefs.getBoolean(Preferences.KEY_DECODE_PDF417, false)) {
|
||||
decodeFormats.addAll(DecodeFormatManager.PDF417_FORMATS);
|
||||
}
|
||||
}
|
||||
hints.put(DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
|
||||
|
||||
if (characterSet != null) {
|
||||
hints.put(DecodeHintType.CHARACTER_SET, characterSet);
|
||||
}
|
||||
hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, resultPointCallback);
|
||||
Log.i("DecodeThread", "Hints: " + hints);
|
||||
}
|
||||
|
||||
Handler getHandler() {
|
||||
try {
|
||||
handlerInitLatch.await();
|
||||
} catch (InterruptedException ie) {
|
||||
// continue?
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Looper.prepare();
|
||||
handler = new DecodeHandler(activity, hints);
|
||||
handlerInitLatch.countDown();
|
||||
Looper.loop();
|
||||
}
|
||||
|
||||
}
|
||||
123
lib/src/main/java/com/king/zxing/InactivityTimer.java
Normal file
@@ -0,0 +1,123 @@
|
||||
package com.king.zxing;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.BatteryManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
/**
|
||||
* Finishes an activity after a period of inactivity if the device is on battery power.
|
||||
*/
|
||||
final class InactivityTimer {
|
||||
|
||||
private static final String TAG = InactivityTimer.class.getSimpleName();
|
||||
|
||||
private static final long INACTIVITY_DELAY_MS = 5 * 60 * 1000L;
|
||||
|
||||
private final Activity activity;
|
||||
private final BroadcastReceiver powerStatusReceiver;
|
||||
private boolean registered;
|
||||
private AsyncTask<Object,Object,Object> inactivityTask;
|
||||
|
||||
InactivityTimer(Activity activity) {
|
||||
this.activity = activity;
|
||||
powerStatusReceiver = new PowerStatusReceiver();
|
||||
registered = false;
|
||||
onActivity();
|
||||
}
|
||||
|
||||
synchronized void onActivity() {
|
||||
cancel();
|
||||
inactivityTask = new InactivityAsyncTask();
|
||||
try {
|
||||
inactivityTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
} catch (RejectedExecutionException ree) {
|
||||
Log.w(TAG, "Couldn't schedule inactivity task; ignoring");
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void onPause() {
|
||||
cancel();
|
||||
if (registered) {
|
||||
activity.unregisterReceiver(powerStatusReceiver);
|
||||
registered = false;
|
||||
} else {
|
||||
Log.w(TAG, "PowerStatusReceiver was never registered?");
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void onResume() {
|
||||
if (registered) {
|
||||
Log.w(TAG, "PowerStatusReceiver was already registered?");
|
||||
} else {
|
||||
activity.registerReceiver(powerStatusReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
|
||||
registered = true;
|
||||
}
|
||||
onActivity();
|
||||
}
|
||||
|
||||
private synchronized void cancel() {
|
||||
AsyncTask<?,?,?> task = inactivityTask;
|
||||
if (task != null) {
|
||||
task.cancel(true);
|
||||
inactivityTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
cancel();
|
||||
}
|
||||
|
||||
private final class PowerStatusReceiver extends BroadcastReceiver {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
|
||||
// 0 indicates that we're on battery
|
||||
boolean onBatteryNow = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) <= 0;
|
||||
if (onBatteryNow) {
|
||||
InactivityTimer.this.onActivity();
|
||||
} else {
|
||||
InactivityTimer.this.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class InactivityAsyncTask extends AsyncTask<Object,Object,Object> {
|
||||
@Override
|
||||
protected Object doInBackground(Object... objects) {
|
||||
try {
|
||||
Thread.sleep(INACTIVITY_DELAY_MS);
|
||||
Log.i(TAG, "Finishing activity due to inactivity");
|
||||
activity.finish();
|
||||
} catch (InterruptedException e) {
|
||||
// continue without killing
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
303
lib/src/main/java/com/king/zxing/Intents.java
Normal file
@@ -0,0 +1,303 @@
|
||||
package com.king.zxing;
|
||||
/*
|
||||
* Copyright (C) 2008 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
/**
|
||||
* This class provides the constants to use when sending an Intent to Barcode Scanner.
|
||||
* These strings are effectively API and cannot be changed.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class Intents {
|
||||
private Intents() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants related to the {@link Scan#ACTION} Intent.
|
||||
*/
|
||||
public static final class Scan {
|
||||
/**
|
||||
* Send this intent to open the Barcodes app in scanning mode, find a barcode, and return
|
||||
* the results.
|
||||
*/
|
||||
public static final String ACTION = "com.google.zxing.client.android.SCAN";
|
||||
|
||||
/**
|
||||
* By default, sending this will decode all barcodes that we understand. However it
|
||||
* may be useful to limit scanning to certain formats. Use
|
||||
* {@link android.content.Intent#putExtra(String, String)} with one of the values below.
|
||||
*
|
||||
* Setting this is effectively shorthand for setting explicit formats with {@link #FORMATS}.
|
||||
* It is overridden by that setting.
|
||||
*/
|
||||
public static final String MODE = "SCAN_MODE";
|
||||
|
||||
/**
|
||||
* Decode only UPC and EAN barcodes. This is the right choice for shopping apps which get
|
||||
* prices, reviews, etc. for products.
|
||||
*/
|
||||
public static final String PRODUCT_MODE = "PRODUCT_MODE";
|
||||
|
||||
/**
|
||||
* Decode only 1D barcodes.
|
||||
*/
|
||||
public static final String ONE_D_MODE = "ONE_D_MODE";
|
||||
|
||||
/**
|
||||
* Decode only QR codes.
|
||||
*/
|
||||
public static final String QR_CODE_MODE = "QR_CODE_MODE";
|
||||
|
||||
/**
|
||||
* Decode only Data Matrix codes.
|
||||
*/
|
||||
public static final String DATA_MATRIX_MODE = "DATA_MATRIX_MODE";
|
||||
|
||||
/**
|
||||
* Decode only Aztec.
|
||||
*/
|
||||
public static final String AZTEC_MODE = "AZTEC_MODE";
|
||||
|
||||
/**
|
||||
* Decode only PDF417.
|
||||
*/
|
||||
public static final String PDF417_MODE = "PDF417_MODE";
|
||||
|
||||
/**
|
||||
* Comma-separated list of formats to scan for. The values must match the names of
|
||||
* {@link com.google.zxing.BarcodeFormat}s, e.g. {@link com.google.zxing.BarcodeFormat#EAN_13}.
|
||||
* Example: "EAN_13,EAN_8,QR_CODE". This overrides {@link #MODE}.
|
||||
*/
|
||||
public static final String FORMATS = "SCAN_FORMATS";
|
||||
|
||||
/**
|
||||
* Optional parameter to specify the id of the camera from which to recognize barcodes.
|
||||
* Overrides the default camera that would otherwise would have been selected.
|
||||
* If provided, should be an int.
|
||||
*/
|
||||
public static final String CAMERA_ID = "SCAN_CAMERA_ID";
|
||||
|
||||
/**
|
||||
* @see com.google.zxing.DecodeHintType#CHARACTER_SET
|
||||
*/
|
||||
public static final String CHARACTER_SET = "CHARACTER_SET";
|
||||
|
||||
/**
|
||||
* Optional parameters to specify the width and height of the scanning rectangle in pixels.
|
||||
* The app will try to honor these, but will clamp them to the size of the preview frame.
|
||||
* You should specify both or neither, and pass the size as an int.
|
||||
*/
|
||||
public static final String WIDTH = "SCAN_WIDTH";
|
||||
public static final String HEIGHT = "SCAN_HEIGHT";
|
||||
|
||||
/**
|
||||
* Desired duration in milliseconds for which to pause after a successful scan before
|
||||
* returning to the calling intent. Specified as a long, not an integer!
|
||||
* For example: 1000L, not 1000.
|
||||
*/
|
||||
public static final String RESULT_DISPLAY_DURATION_MS = "RESULT_DISPLAY_DURATION_MS";
|
||||
|
||||
/**
|
||||
* Prompt to show on-screen when scanning by intent. Specified as a {@link String}.
|
||||
*/
|
||||
public static final String PROMPT_MESSAGE = "PROMPT_MESSAGE";
|
||||
|
||||
/**
|
||||
* If a barcode is found, Barcodes returns {@link android.app.Activity#RESULT_OK} to
|
||||
* {@link android.app.Activity#onActivityResult(int, int, android.content.Intent)}
|
||||
* of the app which requested the scan via
|
||||
* {@link android.app.Activity#startActivityForResult(android.content.Intent, int)}
|
||||
* The barcodes contents can be retrieved with
|
||||
* {@link android.content.Intent#getStringExtra(String)}.
|
||||
* If the user presses Back, the result code will be {@link android.app.Activity#RESULT_CANCELED}.
|
||||
*/
|
||||
public static final String RESULT = "SCAN_RESULT";
|
||||
|
||||
/**
|
||||
* Call {@link android.content.Intent#getStringExtra(String)} with {@code RESULT_FORMAT}
|
||||
* to determine which barcode format was found.
|
||||
* See {@link com.google.zxing.BarcodeFormat} for possible values.
|
||||
*/
|
||||
public static final String RESULT_FORMAT = "SCAN_RESULT_FORMAT";
|
||||
|
||||
/**
|
||||
* Call {@link android.content.Intent#getStringExtra(String)} with {@code RESULT_UPC_EAN_EXTENSION}
|
||||
* to return the content of any UPC extension barcode that was also found. Only applicable
|
||||
* to {@link com.google.zxing.BarcodeFormat#UPC_A} and {@link com.google.zxing.BarcodeFormat#EAN_13}
|
||||
* formats.
|
||||
*/
|
||||
public static final String RESULT_UPC_EAN_EXTENSION = "SCAN_RESULT_UPC_EAN_EXTENSION";
|
||||
|
||||
/**
|
||||
* Call {@link android.content.Intent#getByteArrayExtra(String)} with {@code RESULT_BYTES}
|
||||
* to get a {@code byte[]} of raw bytes in the barcode, if available.
|
||||
*/
|
||||
public static final String RESULT_BYTES = "SCAN_RESULT_BYTES";
|
||||
|
||||
/**
|
||||
* Key for the value of {@link com.google.zxing.ResultMetadataType#ORIENTATION}, if available.
|
||||
* Call {@link android.content.Intent#getIntArrayExtra(String)} with {@code RESULT_ORIENTATION}.
|
||||
*/
|
||||
public static final String RESULT_ORIENTATION = "SCAN_RESULT_ORIENTATION";
|
||||
|
||||
/**
|
||||
* Key for the value of {@link com.google.zxing.ResultMetadataType#ERROR_CORRECTION_LEVEL}, if available.
|
||||
* Call {@link android.content.Intent#getStringExtra(String)} with {@code RESULT_ERROR_CORRECTION_LEVEL}.
|
||||
*/
|
||||
public static final String RESULT_ERROR_CORRECTION_LEVEL = "SCAN_RESULT_ERROR_CORRECTION_LEVEL";
|
||||
|
||||
/**
|
||||
* Prefix for keys that map to the values of {@link com.google.zxing.ResultMetadataType#BYTE_SEGMENTS},
|
||||
* if available. The actual values will be set under a series of keys formed by adding 0, 1, 2, ...
|
||||
* to this prefix. So the first byte segment is under key "SCAN_RESULT_BYTE_SEGMENTS_0" for example.
|
||||
* Call {@link android.content.Intent#getByteArrayExtra(String)} with these keys.
|
||||
*/
|
||||
public static final String RESULT_BYTE_SEGMENTS_PREFIX = "SCAN_RESULT_BYTE_SEGMENTS_";
|
||||
|
||||
/**
|
||||
* Setting this to false will not save scanned codes in the history. Specified as a {@code boolean}.
|
||||
*/
|
||||
public static final String SAVE_HISTORY = "SAVE_HISTORY";
|
||||
|
||||
private Scan() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants related to the scan history and retrieving history items.
|
||||
*/
|
||||
public static final class History {
|
||||
|
||||
public static final String ITEM_NUMBER = "ITEM_NUMBER";
|
||||
|
||||
private History() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants related to the {@link Encode#ACTION} Intent.
|
||||
*/
|
||||
public static final class Encode {
|
||||
/**
|
||||
* Send this intent to encode a piece of data as a QR code and display it full screen, so
|
||||
* that another person can scan the barcode from your screen.
|
||||
*/
|
||||
public static final String ACTION = "com.google.zxing.client.android.ENCODE";
|
||||
|
||||
/**
|
||||
* The data to encode. Use {@link android.content.Intent#putExtra(String, String)} or
|
||||
* {@link android.content.Intent#putExtra(String, android.os.Bundle)},
|
||||
* depending on the type and format specified. Non-QR Code formats should
|
||||
* just use a String here. For QR Code, see Contents for details.
|
||||
*/
|
||||
public static final String DATA = "ENCODE_DATA";
|
||||
|
||||
/**
|
||||
* The type of data being supplied if the format is QR Code. Use
|
||||
* {@link android.content.Intent#putExtra(String, String)} with one of Contents.Type.
|
||||
*/
|
||||
public static final String TYPE = "ENCODE_TYPE";
|
||||
|
||||
/**
|
||||
* The barcode format to be displayed. If this isn't specified or is blank,
|
||||
* it defaults to QR Code. Use {@link android.content.Intent#putExtra(String, String)}, where
|
||||
* format is one of {@link com.google.zxing.BarcodeFormat}.
|
||||
*/
|
||||
public static final String FORMAT = "ENCODE_FORMAT";
|
||||
|
||||
/**
|
||||
* Normally the contents of the barcode are displayed to the user in a TextView. Setting this
|
||||
* boolean to false will hide that TextView, showing only the encode barcode.
|
||||
*/
|
||||
public static final String SHOW_CONTENTS = "ENCODE_SHOW_CONTENTS";
|
||||
|
||||
private Encode() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants related to the {@link SearchBookContents#ACTION} Intent.
|
||||
*/
|
||||
public static final class SearchBookContents {
|
||||
/**
|
||||
* Use Google Book Search to search the contents of the book provided.
|
||||
*/
|
||||
public static final String ACTION = "com.google.zxing.client.android.SEARCH_BOOK_CONTENTS";
|
||||
|
||||
/**
|
||||
* The book to search, identified by ISBN number.
|
||||
*/
|
||||
public static final String ISBN = "ISBN";
|
||||
|
||||
/**
|
||||
* An optional field which is the text to search for.
|
||||
*/
|
||||
public static final String QUERY = "QUERY";
|
||||
|
||||
private SearchBookContents() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants related to the {@link WifiConnect#ACTION} Intent.
|
||||
*/
|
||||
public static final class WifiConnect {
|
||||
/**
|
||||
* Internal intent used to trigger connection to a wi-fi network.
|
||||
*/
|
||||
public static final String ACTION = "com.google.zxing.client.android.WIFI_CONNECT";
|
||||
|
||||
/**
|
||||
* The network to connect to, all the configuration provided here.
|
||||
*/
|
||||
public static final String SSID = "SSID";
|
||||
|
||||
/**
|
||||
* The network to connect to, all the configuration provided here.
|
||||
*/
|
||||
public static final String TYPE = "TYPE";
|
||||
|
||||
/**
|
||||
* The network to connect to, all the configuration provided here.
|
||||
*/
|
||||
public static final String PASSWORD = "PASSWORD";
|
||||
|
||||
private WifiConnect() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constants related to the {@link Share#ACTION} Intent.
|
||||
*/
|
||||
public static final class Share {
|
||||
/**
|
||||
* Give the user a choice of items to encode as a barcode, then render it as a QR Code and
|
||||
* display onscreen for a friend to scan with their phone.
|
||||
*/
|
||||
public static final String ACTION = "com.google.zxing.client.android.SHARE";
|
||||
|
||||
private Share() {
|
||||
}
|
||||
}
|
||||
|
||||
// Not the best place for this, but, better than a new class
|
||||
// Should be FLAG_ACTIVITY_NEW_DOCUMENT in API 21+.
|
||||
// Defined once here because the current value is deprecated, so generates just one warning
|
||||
public static final int FLAG_NEW_DOC = Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET;
|
||||
}
|
||||
50
lib/src/main/java/com/king/zxing/Preferences.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package com.king.zxing;
|
||||
/*
|
||||
* Copyright (C) 2008 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
public final class Preferences {
|
||||
|
||||
public static final String KEY_DECODE_1D_PRODUCT = "preferences_decode_1D_product";
|
||||
public static final String KEY_DECODE_1D_INDUSTRIAL = "preferences_decode_1D_industrial";
|
||||
public static final String KEY_DECODE_QR = "preferences_decode_QR";
|
||||
public static final String KEY_DECODE_DATA_MATRIX = "preferences_decode_Data_Matrix";
|
||||
public static final String KEY_DECODE_AZTEC = "preferences_decode_Aztec";
|
||||
public static final String KEY_DECODE_PDF417 = "preferences_decode_PDF417";
|
||||
|
||||
public static final String KEY_CUSTOM_PRODUCT_SEARCH = "preferences_custom_product_search";
|
||||
|
||||
public static final String KEY_PLAY_BEEP = "preferences_play_beep";
|
||||
public static final String KEY_VIBRATE = "preferences_vibrate";
|
||||
public static final String KEY_COPY_TO_CLIPBOARD = "preferences_copy_to_clipboard";
|
||||
public static final String KEY_FRONT_LIGHT_MODE = "preferences_front_light_mode";
|
||||
public static final String KEY_BULK_MODE = "preferences_bulk_mode";
|
||||
public static final String KEY_REMEMBER_DUPLICATES = "preferences_remember_duplicates";
|
||||
public static final String KEY_ENABLE_HISTORY = "preferences_history";
|
||||
public static final String KEY_SUPPLEMENTAL = "preferences_supplemental";
|
||||
public static final String KEY_AUTO_FOCUS = "preferences_auto_focus";
|
||||
public static final String KEY_INVERT_SCAN = "preferences_invert_scan";
|
||||
public static final String KEY_SEARCH_COUNTRY = "preferences_search_country";
|
||||
public static final String KEY_DISABLE_AUTO_ORIENTATION = "preferences_orientation";
|
||||
|
||||
public static final String KEY_DISABLE_CONTINUOUS_FOCUS = "preferences_disable_continuous_focus";
|
||||
public static final String KEY_DISABLE_EXPOSURE = "preferences_disable_exposure";
|
||||
public static final String KEY_DISABLE_METERING = "preferences_disable_metering";
|
||||
public static final String KEY_DISABLE_BARCODE_SCENE_MODE = "preferences_disable_barcode_scene_mode";
|
||||
public static final String KEY_AUTO_OPEN_WEB = "preferences_auto_open_web";
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.king.zxing;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2009 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.google.zxing.ResultPointCallback;
|
||||
|
||||
final class ViewfinderResultPointCallback implements ResultPointCallback {
|
||||
|
||||
private final ViewfinderView viewfinderView;
|
||||
|
||||
ViewfinderResultPointCallback(ViewfinderView viewfinderView) {
|
||||
this.viewfinderView = viewfinderView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void foundPossibleResultPoint(ResultPoint point) {
|
||||
viewfinderView.addPossibleResultPoint(point);
|
||||
}
|
||||
|
||||
}
|
||||
345
lib/src/main/java/com/king/zxing/ViewfinderView.java
Normal file
@@ -0,0 +1,345 @@
|
||||
package com.king.zxing;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.text.Layout;
|
||||
import android.text.StaticLayout;
|
||||
import android.text.TextPaint;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
|
||||
import com.google.zxing.ResultPoint;
|
||||
import com.king.zxing.camera.CameraManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This view is overlaid on top of the camera preview. It adds the viewfinder rectangle and partial
|
||||
* transparency outside it, as well as the laser scanner animation and result points.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
public final class ViewfinderView extends View {
|
||||
|
||||
private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64};
|
||||
private static final long ANIMATION_DELAY = 20L;
|
||||
private static final int CURRENT_POINT_OPACITY = 0xA0;
|
||||
private static final int MAX_RESULT_POINTS = 20;
|
||||
private static final int POINT_SIZE = 6;
|
||||
|
||||
private static final int CORNER_RECT_WIDTH = 8; //扫描区边角的宽
|
||||
private static final int CORNER_RECT_HEIGHT = 40; //扫描区边角的高
|
||||
private static final int SCANNER_LINE_MOVE_DISTANCE = 6; //扫描线移动距离
|
||||
private static final int SCANNER_LINE_HEIGHT = 10; //扫描线宽度
|
||||
|
||||
private CameraManager cameraManager;
|
||||
private final Paint paint;
|
||||
private final TextPaint textPaint;
|
||||
private Bitmap resultBitmap;
|
||||
private final int maskColor;
|
||||
// private final int resultColor;
|
||||
//扫描区域边框颜色
|
||||
private final int frameColor;
|
||||
//扫描线颜色
|
||||
private final int laserColor;
|
||||
//四角颜色
|
||||
private final int cornerColor;
|
||||
private final int resultPointColor;
|
||||
private int scannerAlpha;
|
||||
private final float textPadding;
|
||||
private TextLocation textLocation;
|
||||
//扫描区域提示文本
|
||||
private String labelText;
|
||||
//扫描区域提示文本颜色
|
||||
private final int labelTextColor;
|
||||
private final float labelTextSize;
|
||||
public static int scannerStart = 0;
|
||||
public static int scannerEnd = 0;
|
||||
|
||||
private List<ResultPoint> possibleResultPoints;
|
||||
private List<ResultPoint> lastPossibleResultPoints;
|
||||
|
||||
public enum TextLocation {
|
||||
TOP(0),BOTTOM(1);
|
||||
|
||||
private int mValue;
|
||||
|
||||
TextLocation(int value){
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
private static TextLocation getFromInt(int value){
|
||||
|
||||
for(TextLocation location : TextLocation.values()){
|
||||
if(location.mValue == value){
|
||||
return location;
|
||||
}
|
||||
}
|
||||
|
||||
return TextLocation.TOP;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// This constructor is used when the class is built from an XML resource.
|
||||
public ViewfinderView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
//初始化自定义属性信息
|
||||
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ViewfinderView);
|
||||
maskColor = array.getColor(R.styleable.ViewfinderView_maskColor, ContextCompat.getColor(context,R.color.viewfinder_mask));
|
||||
frameColor = array.getColor(R.styleable.ViewfinderView_frameColor, ContextCompat.getColor(context,R.color.viewfinder_frame));
|
||||
cornerColor = array.getColor(R.styleable.ViewfinderView_cornerColor, ContextCompat.getColor(context,R.color.viewfinder_corner));
|
||||
laserColor = array.getColor(R.styleable.ViewfinderView_laserColor, ContextCompat.getColor(context,R.color.viewfinder_laser));
|
||||
resultPointColor = array.getColor(R.styleable.ViewfinderView_resultPointColor, ContextCompat.getColor(context,R.color.viewfinder_result_point_color));
|
||||
|
||||
labelText = array.getString(R.styleable.ViewfinderView_text);
|
||||
labelTextColor = array.getColor(R.styleable.ViewfinderView_textColor, ContextCompat.getColor(context,R.color.viewfinder_text_color));
|
||||
labelTextSize = array.getDimension(R.styleable.ViewfinderView_textSize, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,14f,getResources().getDisplayMetrics()));
|
||||
textPadding = array.getDimension(R.styleable.ViewfinderView_textPadding,TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,24,getResources().getDisplayMetrics()));
|
||||
textLocation = TextLocation.getFromInt(array.getInt(R.styleable.ViewfinderView_textLocation,0));
|
||||
|
||||
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
|
||||
scannerAlpha = 0;
|
||||
possibleResultPoints = new ArrayList<>(5);
|
||||
lastPossibleResultPoints = null;
|
||||
}
|
||||
|
||||
public void setCameraManager(CameraManager cameraManager) {
|
||||
this.cameraManager = cameraManager;
|
||||
}
|
||||
|
||||
@SuppressLint("DrawAllocation")
|
||||
@Override
|
||||
public void onDraw(Canvas canvas) {
|
||||
if (cameraManager == null) {
|
||||
return; // not ready yet, early draw before done configuring
|
||||
}
|
||||
Rect frame = cameraManager.getFramingRect();
|
||||
Rect previewFrame = cameraManager.getFramingRectInPreview();
|
||||
if (frame == null || previewFrame == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(scannerStart == 0 || scannerEnd == 0) {
|
||||
scannerStart = frame.top;
|
||||
scannerEnd = frame.bottom - SCANNER_LINE_HEIGHT;
|
||||
}
|
||||
|
||||
int width = canvas.getWidth();
|
||||
int height = canvas.getHeight();
|
||||
|
||||
// Draw the exterior (i.e. outside the framing rect) darkened
|
||||
drawExterior(canvas,frame,width,height);
|
||||
|
||||
if (resultBitmap != null) {
|
||||
// Draw the opaque result bitmap over the scanning rectangle
|
||||
paint.setAlpha(CURRENT_POINT_OPACITY);
|
||||
canvas.drawBitmap(resultBitmap, null, frame, paint);
|
||||
} else {
|
||||
|
||||
// Draw a red "laser scanner" line through the middle to show decoding is active
|
||||
// paint.setColor(laserColor);
|
||||
// paint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
|
||||
// scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
|
||||
// Draw a two pixel solid black border inside the framing rect
|
||||
drawFrame(canvas, frame);
|
||||
// 绘制边角
|
||||
drawCorner(canvas, frame);
|
||||
// Draw a red "laser scanner" line through the middle to show decoding is active
|
||||
drawLaserScanner(canvas,frame);
|
||||
//绘制提示信息
|
||||
drawTextInfo(canvas, frame);
|
||||
|
||||
float scaleX = frame.width() / (float) previewFrame.width();
|
||||
float scaleY = frame.height() / (float) previewFrame.height();
|
||||
|
||||
List<ResultPoint> currentPossible = possibleResultPoints;
|
||||
List<ResultPoint> currentLast = lastPossibleResultPoints;
|
||||
int frameLeft = frame.left;
|
||||
int frameTop = frame.top;
|
||||
if (currentPossible.isEmpty()) {
|
||||
lastPossibleResultPoints = null;
|
||||
} else {
|
||||
possibleResultPoints = new ArrayList<>(5);
|
||||
lastPossibleResultPoints = currentPossible;
|
||||
paint.setAlpha(CURRENT_POINT_OPACITY);
|
||||
paint.setColor(resultPointColor);
|
||||
synchronized (currentPossible) {
|
||||
for (ResultPoint point : currentPossible) {
|
||||
canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX),
|
||||
frameTop + (int) (point.getY() * scaleY),
|
||||
POINT_SIZE, paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentLast != null) {
|
||||
paint.setAlpha(CURRENT_POINT_OPACITY / 2);
|
||||
paint.setColor(resultPointColor);
|
||||
synchronized (currentLast) {
|
||||
float radius = POINT_SIZE / 2.0f;
|
||||
for (ResultPoint point : currentLast) {
|
||||
canvas.drawCircle(frameLeft + (int) (point.getX() * scaleX),
|
||||
frameTop + (int) (point.getY() * scaleY),
|
||||
radius, paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Request another update at the animation interval, but only repaint the laser line,
|
||||
// not the entire viewfinder mask.
|
||||
postInvalidateDelayed(ANIMATION_DELAY,
|
||||
frame.left - POINT_SIZE,
|
||||
frame.top - POINT_SIZE,
|
||||
frame.right + POINT_SIZE,
|
||||
frame.bottom + POINT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
//绘制文本
|
||||
private void drawTextInfo(Canvas canvas, Rect frame) {
|
||||
if(!TextUtils.isEmpty(labelText)){
|
||||
textPaint.setColor(labelTextColor);
|
||||
textPaint.setTextSize(labelTextSize);
|
||||
textPaint.setTextAlign(Paint.Align.CENTER);
|
||||
StaticLayout staticLayout = new StaticLayout(labelText,textPaint,canvas.getWidth(), Layout.Alignment.ALIGN_NORMAL,1.0f,0.0f,true);
|
||||
if(textLocation == TextLocation.BOTTOM){
|
||||
canvas.translate(frame.left + frame.width() / 2,frame.bottom + textPadding);
|
||||
staticLayout.draw(canvas);
|
||||
}else{
|
||||
canvas.translate(frame.left + frame.width() / 2,frame.top - textPadding - staticLayout.getHeight());
|
||||
staticLayout.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//绘制边角
|
||||
private void drawCorner(Canvas canvas, Rect frame) {
|
||||
paint.setColor(cornerColor);
|
||||
//左上
|
||||
canvas.drawRect(frame.left, frame.top, frame.left + CORNER_RECT_WIDTH, frame.top + CORNER_RECT_HEIGHT, paint);
|
||||
canvas.drawRect(frame.left, frame.top, frame.left + CORNER_RECT_HEIGHT, frame.top + CORNER_RECT_WIDTH, paint);
|
||||
//右上
|
||||
canvas.drawRect(frame.right - CORNER_RECT_WIDTH, frame.top, frame.right, frame.top + CORNER_RECT_HEIGHT, paint);
|
||||
canvas.drawRect(frame.right - CORNER_RECT_HEIGHT, frame.top, frame.right, frame.top + CORNER_RECT_WIDTH, paint);
|
||||
//左下
|
||||
canvas.drawRect(frame.left, frame.bottom - CORNER_RECT_WIDTH, frame.left + CORNER_RECT_HEIGHT, frame.bottom, paint);
|
||||
canvas.drawRect(frame.left, frame.bottom - CORNER_RECT_HEIGHT, frame.left + CORNER_RECT_WIDTH, frame.bottom, paint);
|
||||
//右下
|
||||
canvas.drawRect(frame.right - CORNER_RECT_WIDTH, frame.bottom - CORNER_RECT_HEIGHT, frame.right, frame.bottom, paint);
|
||||
canvas.drawRect(frame.right - CORNER_RECT_HEIGHT, frame.bottom - CORNER_RECT_WIDTH, frame.right, frame.bottom, paint);
|
||||
}
|
||||
|
||||
//绘制扫描线
|
||||
private void drawLaserScanner(Canvas canvas, Rect frame) {
|
||||
paint.setColor(laserColor);
|
||||
//线性渐变
|
||||
LinearGradient linearGradient = new LinearGradient(
|
||||
frame.left, scannerStart,
|
||||
frame.left, scannerStart + SCANNER_LINE_HEIGHT,
|
||||
shadeColor(laserColor),
|
||||
laserColor,
|
||||
Shader.TileMode.MIRROR);
|
||||
|
||||
paint.setShader(linearGradient);
|
||||
if(scannerStart <= scannerEnd) {
|
||||
//椭圆
|
||||
RectF rectF = new RectF(frame.left + 2 * SCANNER_LINE_HEIGHT, scannerStart, frame.right - 2 * SCANNER_LINE_HEIGHT, scannerStart + SCANNER_LINE_HEIGHT);
|
||||
canvas.drawOval(rectF, paint);
|
||||
scannerStart += SCANNER_LINE_MOVE_DISTANCE;
|
||||
} else {
|
||||
scannerStart = frame.top;
|
||||
}
|
||||
paint.setShader(null);
|
||||
}
|
||||
|
||||
//处理颜色模糊
|
||||
public int shadeColor(int color) {
|
||||
String hax = Integer.toHexString(color);
|
||||
String result = "20"+hax.substring(2);
|
||||
return Integer.valueOf(result, 16);
|
||||
}
|
||||
|
||||
// 绘制扫描区边框 Draw a two pixel solid black border inside the framing rect
|
||||
private void drawFrame(Canvas canvas, Rect frame) {
|
||||
paint.setColor(frameColor);
|
||||
canvas.drawRect(frame.left, frame.top, frame.right + 1, frame.top + 2, paint);
|
||||
canvas.drawRect(frame.left, frame.top + 2, frame.left + 2, frame.bottom - 1, paint);
|
||||
canvas.drawRect(frame.right - 1, frame.top, frame.right + 1, frame.bottom - 1, paint);
|
||||
canvas.drawRect(frame.left, frame.bottom - 1, frame.right + 1, frame.bottom + 1, paint);
|
||||
}
|
||||
|
||||
// 绘制模糊区域 Draw the exterior (i.e. outside the framing rect) darkened
|
||||
private void drawExterior(Canvas canvas, Rect frame, int width, int height) {
|
||||
paint.setColor(maskColor);
|
||||
canvas.drawRect(0, 0, width, frame.top, paint);
|
||||
canvas.drawRect(0, frame.top, frame.left, frame.bottom + 1, paint);
|
||||
canvas.drawRect(frame.right + 1, frame.top, width, frame.bottom + 1, paint);
|
||||
canvas.drawRect(0, frame.bottom + 1, width, height, paint);
|
||||
}
|
||||
|
||||
public void drawViewfinder() {
|
||||
Bitmap resultBitmap = this.resultBitmap;
|
||||
this.resultBitmap = null;
|
||||
if (resultBitmap != null) {
|
||||
resultBitmap.recycle();
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Draw a bitmap with the result points highlighted instead of the live scanning display.
|
||||
// *
|
||||
// * @param barcode An image of the decoded barcode.
|
||||
// */
|
||||
// public void drawResultBitmap(Bitmap barcode) {
|
||||
// resultBitmap = barcode;
|
||||
// invalidate();
|
||||
// }
|
||||
|
||||
public void addPossibleResultPoint(ResultPoint point) {
|
||||
List<ResultPoint> points = possibleResultPoints;
|
||||
synchronized (points) {
|
||||
points.add(point);
|
||||
int size = points.size();
|
||||
if (size > MAX_RESULT_POINTS) {
|
||||
// trim it
|
||||
points.subList(0, size - MAX_RESULT_POINTS / 2).clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
132
lib/src/main/java/com/king/zxing/camera/AutoFocusManager.java
Normal file
@@ -0,0 +1,132 @@
|
||||
package com.king.zxing.camera;
|
||||
/*
|
||||
* Copyright (C) 2012 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.hardware.Camera;
|
||||
import android.os.AsyncTask;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
import com.king.zxing.Preferences;
|
||||
|
||||
@SuppressWarnings("deprecation") // camera APIs
|
||||
final class AutoFocusManager implements Camera.AutoFocusCallback {
|
||||
|
||||
private static final String TAG = AutoFocusManager.class.getSimpleName();
|
||||
|
||||
private static final long AUTO_FOCUS_INTERVAL_MS = 1000L;
|
||||
private static final Collection<String> FOCUS_MODES_CALLING_AF;
|
||||
static {
|
||||
FOCUS_MODES_CALLING_AF = new ArrayList<>(2);
|
||||
FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_AUTO);
|
||||
FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_MACRO);
|
||||
}
|
||||
|
||||
private boolean stopped;
|
||||
private boolean focusing;
|
||||
private final boolean useAutoFocus;
|
||||
private final Camera camera;
|
||||
private AsyncTask<?,?,?> outstandingTask;
|
||||
|
||||
AutoFocusManager(Context context, Camera camera) {
|
||||
this.camera = camera;
|
||||
SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
String currentFocusMode = camera.getParameters().getFocusMode();
|
||||
useAutoFocus =
|
||||
sharedPrefs.getBoolean(Preferences.KEY_AUTO_FOCUS, true) &&
|
||||
FOCUS_MODES_CALLING_AF.contains(currentFocusMode);
|
||||
Log.i(TAG, "Current focus mode '" + currentFocusMode + "'; use auto focus? " + useAutoFocus);
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void onAutoFocus(boolean success, Camera theCamera) {
|
||||
focusing = false;
|
||||
autoFocusAgainLater();
|
||||
}
|
||||
|
||||
private synchronized void autoFocusAgainLater() {
|
||||
if (!stopped && outstandingTask == null) {
|
||||
AutoFocusTask newTask = new AutoFocusTask();
|
||||
try {
|
||||
newTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
outstandingTask = newTask;
|
||||
} catch (RejectedExecutionException ree) {
|
||||
Log.w(TAG, "Could not request auto focus", ree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void start() {
|
||||
if (useAutoFocus) {
|
||||
outstandingTask = null;
|
||||
if (!stopped && !focusing) {
|
||||
try {
|
||||
camera.autoFocus(this);
|
||||
focusing = true;
|
||||
} catch (RuntimeException re) {
|
||||
// Have heard RuntimeException reported in Android 4.0.x+; continue?
|
||||
Log.w(TAG, "Unexpected exception while focusing", re);
|
||||
// Try again later to keep cycle going
|
||||
autoFocusAgainLater();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void cancelOutstandingTask() {
|
||||
if (outstandingTask != null) {
|
||||
if (outstandingTask.getStatus() != AsyncTask.Status.FINISHED) {
|
||||
outstandingTask.cancel(true);
|
||||
}
|
||||
outstandingTask = null;
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void stop() {
|
||||
stopped = true;
|
||||
if (useAutoFocus) {
|
||||
cancelOutstandingTask();
|
||||
// Doesn't hurt to call this even if not focusing
|
||||
try {
|
||||
camera.cancelAutoFocus();
|
||||
} catch (RuntimeException re) {
|
||||
// Have heard RuntimeException reported in Android 4.0.x+; continue?
|
||||
Log.w(TAG, "Unexpected exception while cancelling focusing", re);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class AutoFocusTask extends AsyncTask<Object,Object,Object> {
|
||||
@Override
|
||||
protected Object doInBackground(Object... voids) {
|
||||
try {
|
||||
Thread.sleep(AUTO_FOCUS_INTERVAL_MS);
|
||||
} catch (InterruptedException e) {
|
||||
// continue
|
||||
}
|
||||
start();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
package com.king.zxing.camera;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2010 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Point;
|
||||
import android.hardware.Camera;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.Surface;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.king.zxing.Preferences;
|
||||
import com.king.zxing.camera.open.CameraFacing;
|
||||
import com.king.zxing.camera.open.OpenCamera;
|
||||
|
||||
/**
|
||||
* A class which deals with reading, parsing, and setting the camera parameters which are used to
|
||||
* configure the camera hardware.
|
||||
*/
|
||||
@SuppressWarnings("deprecation") // camera APIs
|
||||
final class CameraConfigurationManager {
|
||||
|
||||
private static final String TAG = "CameraConfiguration";
|
||||
|
||||
private final Context context;
|
||||
private int cwNeededRotation;
|
||||
private int cwRotationFromDisplayToCamera;
|
||||
private Point screenResolution;
|
||||
private Point cameraResolution;
|
||||
private Point bestPreviewSize;
|
||||
private Point previewSizeOnScreen;
|
||||
|
||||
CameraConfigurationManager(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads, one time, values from the camera that are needed by the app.
|
||||
*/
|
||||
void initFromCameraParameters(OpenCamera camera) {
|
||||
Camera.Parameters parameters = camera.getCamera().getParameters();
|
||||
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
Display display = manager.getDefaultDisplay();
|
||||
|
||||
int displayRotation = display.getRotation();
|
||||
int cwRotationFromNaturalToDisplay;
|
||||
switch (displayRotation) {
|
||||
case Surface.ROTATION_0:
|
||||
cwRotationFromNaturalToDisplay = 0;
|
||||
break;
|
||||
case Surface.ROTATION_90:
|
||||
cwRotationFromNaturalToDisplay = 90;
|
||||
break;
|
||||
case Surface.ROTATION_180:
|
||||
cwRotationFromNaturalToDisplay = 180;
|
||||
break;
|
||||
case Surface.ROTATION_270:
|
||||
cwRotationFromNaturalToDisplay = 270;
|
||||
break;
|
||||
default:
|
||||
// Have seen this return incorrect values like -90
|
||||
if (displayRotation % 90 == 0) {
|
||||
cwRotationFromNaturalToDisplay = (360 + displayRotation) % 360;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad rotation: " + displayRotation);
|
||||
}
|
||||
}
|
||||
Log.i(TAG, "Display at: " + cwRotationFromNaturalToDisplay);
|
||||
|
||||
int cwRotationFromNaturalToCamera = camera.getOrientation();
|
||||
Log.i(TAG, "Camera at: " + cwRotationFromNaturalToCamera);
|
||||
|
||||
// Still not 100% sure about this. But acts like we need to flip this:
|
||||
if (camera.getFacing() == CameraFacing.FRONT) {
|
||||
cwRotationFromNaturalToCamera = (360 - cwRotationFromNaturalToCamera) % 360;
|
||||
Log.i(TAG, "Front camera overriden to: " + cwRotationFromNaturalToCamera);
|
||||
}
|
||||
|
||||
/*
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
String overrideRotationString;
|
||||
if (camera.getFacing() == CameraFacing.FRONT) {
|
||||
overrideRotationString = prefs.getString(PreferencesActivity.KEY_FORCE_CAMERA_ORIENTATION_FRONT, null);
|
||||
} else {
|
||||
overrideRotationString = prefs.getString(PreferencesActivity.KEY_FORCE_CAMERA_ORIENTATION, null);
|
||||
}
|
||||
if (overrideRotationString != null && !"-".equals(overrideRotationString)) {
|
||||
Log.i(TAG, "Overriding camera manually to " + overrideRotationString);
|
||||
cwRotationFromNaturalToCamera = Integer.parseInt(overrideRotationString);
|
||||
}
|
||||
*/
|
||||
|
||||
cwRotationFromDisplayToCamera =
|
||||
(360 + cwRotationFromNaturalToCamera - cwRotationFromNaturalToDisplay) % 360;
|
||||
Log.i(TAG, "Final display orientation: " + cwRotationFromDisplayToCamera);
|
||||
if (camera.getFacing() == CameraFacing.FRONT) {
|
||||
Log.i(TAG, "Compensating rotation for front camera");
|
||||
cwNeededRotation = (360 - cwRotationFromDisplayToCamera) % 360;
|
||||
} else {
|
||||
cwNeededRotation = cwRotationFromDisplayToCamera;
|
||||
}
|
||||
Log.i(TAG, "Clockwise rotation from display to camera: " + cwNeededRotation);
|
||||
|
||||
Point theScreenResolution = new Point();
|
||||
display.getSize(theScreenResolution);
|
||||
screenResolution = theScreenResolution;
|
||||
Log.i(TAG, "Screen resolution in current orientation: " + screenResolution);
|
||||
cameraResolution = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);
|
||||
Log.i(TAG, "Camera resolution: " + cameraResolution);
|
||||
bestPreviewSize = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);
|
||||
Log.i(TAG, "Best available preview size: " + bestPreviewSize);
|
||||
|
||||
boolean isScreenPortrait = screenResolution.x < screenResolution.y;
|
||||
boolean isPreviewSizePortrait = bestPreviewSize.x < bestPreviewSize.y;
|
||||
|
||||
if (isScreenPortrait == isPreviewSizePortrait) {
|
||||
previewSizeOnScreen = bestPreviewSize;
|
||||
} else {
|
||||
previewSizeOnScreen = new Point(bestPreviewSize.y, bestPreviewSize.x);
|
||||
}
|
||||
Log.i(TAG, "Preview size on screen: " + previewSizeOnScreen);
|
||||
}
|
||||
|
||||
void setDesiredCameraParameters(OpenCamera camera, boolean safeMode) {
|
||||
|
||||
Camera theCamera = camera.getCamera();
|
||||
Camera.Parameters parameters = theCamera.getParameters();
|
||||
|
||||
if (parameters == null) {
|
||||
Log.w(TAG, "Device error: no camera parameters are available. Proceeding without configuration.");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.i(TAG, "Initial camera parameters: " + parameters.flatten());
|
||||
|
||||
if (safeMode) {
|
||||
Log.w(TAG, "In camera config safe mode -- most settings will not be honored");
|
||||
}
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
|
||||
initializeTorch(parameters, prefs, safeMode);
|
||||
|
||||
CameraConfigurationUtils.setFocus(
|
||||
parameters,
|
||||
prefs.getBoolean(Preferences.KEY_AUTO_FOCUS, true),
|
||||
prefs.getBoolean(Preferences.KEY_DISABLE_CONTINUOUS_FOCUS, true),
|
||||
safeMode);
|
||||
|
||||
if (!safeMode) {
|
||||
if (prefs.getBoolean(Preferences.KEY_INVERT_SCAN, false)) {
|
||||
CameraConfigurationUtils.setInvertColor(parameters);
|
||||
}
|
||||
|
||||
if (!prefs.getBoolean(Preferences.KEY_DISABLE_BARCODE_SCENE_MODE, true)) {
|
||||
CameraConfigurationUtils.setBarcodeSceneMode(parameters);
|
||||
}
|
||||
|
||||
if (!prefs.getBoolean(Preferences.KEY_DISABLE_METERING, true)) {
|
||||
CameraConfigurationUtils.setVideoStabilization(parameters);
|
||||
CameraConfigurationUtils.setFocusArea(parameters);
|
||||
CameraConfigurationUtils.setMetering(parameters);
|
||||
}
|
||||
|
||||
//SetRecordingHint to true also a workaround for low framerate on Nexus 4
|
||||
//https://stackoverflow.com/questions/14131900/extreme-camera-lag-on-nexus-4
|
||||
parameters.setRecordingHint(true);
|
||||
|
||||
}
|
||||
|
||||
parameters.setPreviewSize(bestPreviewSize.x, bestPreviewSize.y);
|
||||
|
||||
theCamera.setParameters(parameters);
|
||||
|
||||
theCamera.setDisplayOrientation(cwRotationFromDisplayToCamera);
|
||||
|
||||
Camera.Parameters afterParameters = theCamera.getParameters();
|
||||
Camera.Size afterSize = afterParameters.getPreviewSize();
|
||||
if (afterSize != null && (bestPreviewSize.x != afterSize.width || bestPreviewSize.y != afterSize.height)) {
|
||||
Log.w(TAG, "Camera said it supported preview size " + bestPreviewSize.x + 'x' + bestPreviewSize.y +
|
||||
", but after setting it, preview size is " + afterSize.width + 'x' + afterSize.height);
|
||||
bestPreviewSize.x = afterSize.width;
|
||||
bestPreviewSize.y = afterSize.height;
|
||||
}
|
||||
}
|
||||
|
||||
Point getBestPreviewSize() {
|
||||
return bestPreviewSize;
|
||||
}
|
||||
|
||||
Point getPreviewSizeOnScreen() {
|
||||
return previewSizeOnScreen;
|
||||
}
|
||||
|
||||
Point getCameraResolution() {
|
||||
return cameraResolution;
|
||||
}
|
||||
|
||||
Point getScreenResolution() {
|
||||
return screenResolution;
|
||||
}
|
||||
|
||||
int getCWNeededRotation() {
|
||||
return cwNeededRotation;
|
||||
}
|
||||
|
||||
boolean getTorchState(Camera camera) {
|
||||
if (camera != null) {
|
||||
Camera.Parameters parameters = camera.getParameters();
|
||||
if (parameters != null) {
|
||||
String flashMode = parameters.getFlashMode();
|
||||
return
|
||||
Camera.Parameters.FLASH_MODE_ON.equals(flashMode) ||
|
||||
Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void setTorch(Camera camera, boolean newSetting) {
|
||||
Camera.Parameters parameters = camera.getParameters();
|
||||
doSetTorch(parameters, newSetting, false);
|
||||
camera.setParameters(parameters);
|
||||
}
|
||||
|
||||
private void initializeTorch(Camera.Parameters parameters, SharedPreferences prefs, boolean safeMode) {
|
||||
boolean currentSetting = FrontLightMode.readPref(prefs) == FrontLightMode.ON;
|
||||
doSetTorch(parameters, currentSetting, safeMode);
|
||||
}
|
||||
|
||||
private void doSetTorch(Camera.Parameters parameters, boolean newSetting, boolean safeMode) {
|
||||
CameraConfigurationUtils.setTorch(parameters, newSetting);
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (!safeMode && !prefs.getBoolean(Preferences.KEY_DISABLE_EXPOSURE, true)) {
|
||||
CameraConfigurationUtils.setBestExposure(parameters, newSetting);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
package com.king.zxing.camera;
|
||||
/*
|
||||
* Copyright (C) 2014 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.Camera;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Utility methods for configuring the Android camera.
|
||||
*
|
||||
* @author Sean Owen
|
||||
*/
|
||||
@SuppressWarnings("deprecation") // camera APIs
|
||||
public final class CameraConfigurationUtils {
|
||||
|
||||
private static final String TAG = "CameraConfiguration";
|
||||
|
||||
private static final Pattern SEMICOLON = Pattern.compile(";");
|
||||
|
||||
private static final int MIN_PREVIEW_PIXELS = 480 * 320; // normal screen
|
||||
private static final float MAX_EXPOSURE_COMPENSATION = 1.5f;
|
||||
private static final float MIN_EXPOSURE_COMPENSATION = 0.0f;
|
||||
private static final double MAX_ASPECT_DISTORTION = 0.15;
|
||||
private static final int MIN_FPS = 10;
|
||||
private static final int MAX_FPS = 20;
|
||||
private static final int AREA_PER_1000 = 400;
|
||||
|
||||
private CameraConfigurationUtils() {
|
||||
}
|
||||
|
||||
public static void setFocus(Camera.Parameters parameters,
|
||||
boolean autoFocus,
|
||||
boolean disableContinuous,
|
||||
boolean safeMode) {
|
||||
List<String> supportedFocusModes = parameters.getSupportedFocusModes();
|
||||
String focusMode = null;
|
||||
if (autoFocus) {
|
||||
if (safeMode || disableContinuous) {
|
||||
focusMode = findSettableValue("focus mode",
|
||||
supportedFocusModes,
|
||||
Camera.Parameters.FOCUS_MODE_AUTO);
|
||||
} else {
|
||||
focusMode = findSettableValue("focus mode",
|
||||
supportedFocusModes,
|
||||
Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
|
||||
Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO,
|
||||
Camera.Parameters.FOCUS_MODE_AUTO);
|
||||
}
|
||||
}
|
||||
// Maybe selected auto-focus but not available, so fall through here:
|
||||
if (!safeMode && focusMode == null) {
|
||||
focusMode = findSettableValue("focus mode",
|
||||
supportedFocusModes,
|
||||
Camera.Parameters.FOCUS_MODE_MACRO,
|
||||
Camera.Parameters.FOCUS_MODE_EDOF);
|
||||
}
|
||||
if (focusMode != null) {
|
||||
if (focusMode.equals(parameters.getFocusMode())) {
|
||||
Log.i(TAG, "Focus mode already set to " + focusMode);
|
||||
} else {
|
||||
parameters.setFocusMode(focusMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void setTorch(Camera.Parameters parameters, boolean on) {
|
||||
List<String> supportedFlashModes = parameters.getSupportedFlashModes();
|
||||
String flashMode;
|
||||
if (on) {
|
||||
flashMode = findSettableValue("flash mode",
|
||||
supportedFlashModes,
|
||||
Camera.Parameters.FLASH_MODE_TORCH,
|
||||
Camera.Parameters.FLASH_MODE_ON);
|
||||
} else {
|
||||
flashMode = findSettableValue("flash mode",
|
||||
supportedFlashModes,
|
||||
Camera.Parameters.FLASH_MODE_OFF);
|
||||
}
|
||||
if (flashMode != null) {
|
||||
if (flashMode.equals(parameters.getFlashMode())) {
|
||||
Log.i(TAG, "Flash mode already set to " + flashMode);
|
||||
} else {
|
||||
Log.i(TAG, "Setting flash mode to " + flashMode);
|
||||
parameters.setFlashMode(flashMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void setBestExposure(Camera.Parameters parameters, boolean lightOn) {
|
||||
int minExposure = parameters.getMinExposureCompensation();
|
||||
int maxExposure = parameters.getMaxExposureCompensation();
|
||||
float step = parameters.getExposureCompensationStep();
|
||||
if ((minExposure != 0 || maxExposure != 0) && step > 0.0f) {
|
||||
// Set low when light is on
|
||||
float targetCompensation = lightOn ? MIN_EXPOSURE_COMPENSATION : MAX_EXPOSURE_COMPENSATION;
|
||||
int compensationSteps = Math.round(targetCompensation / step);
|
||||
float actualCompensation = step * compensationSteps;
|
||||
// Clamp value:
|
||||
compensationSteps = Math.max(Math.min(compensationSteps, maxExposure), minExposure);
|
||||
if (parameters.getExposureCompensation() == compensationSteps) {
|
||||
Log.i(TAG, "Exposure compensation already set to " + compensationSteps + " / " + actualCompensation);
|
||||
} else {
|
||||
Log.i(TAG, "Setting exposure compensation to " + compensationSteps + " / " + actualCompensation);
|
||||
parameters.setExposureCompensation(compensationSteps);
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, "Camera does not support exposure compensation");
|
||||
}
|
||||
}
|
||||
|
||||
public static void setBestPreviewFPS(Camera.Parameters parameters) {
|
||||
setBestPreviewFPS(parameters, MIN_FPS, MAX_FPS);
|
||||
}
|
||||
|
||||
public static void setBestPreviewFPS(Camera.Parameters parameters, int minFPS, int maxFPS) {
|
||||
List<int[]> supportedPreviewFpsRanges = parameters.getSupportedPreviewFpsRange();
|
||||
Log.i(TAG, "Supported FPS ranges: " + toString(supportedPreviewFpsRanges));
|
||||
if (supportedPreviewFpsRanges != null && !supportedPreviewFpsRanges.isEmpty()) {
|
||||
int[] suitableFPSRange = null;
|
||||
for (int[] fpsRange : supportedPreviewFpsRanges) {
|
||||
int thisMin = fpsRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX];
|
||||
int thisMax = fpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
|
||||
if (thisMin >= minFPS * 1000 && thisMax <= maxFPS * 1000) {
|
||||
suitableFPSRange = fpsRange;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (suitableFPSRange == null) {
|
||||
Log.i(TAG, "No suitable FPS range?");
|
||||
} else {
|
||||
int[] currentFpsRange = new int[2];
|
||||
parameters.getPreviewFpsRange(currentFpsRange);
|
||||
if (Arrays.equals(currentFpsRange, suitableFPSRange)) {
|
||||
Log.i(TAG, "FPS range already set to " + Arrays.toString(suitableFPSRange));
|
||||
} else {
|
||||
Log.i(TAG, "Setting FPS range to " + Arrays.toString(suitableFPSRange));
|
||||
parameters.setPreviewFpsRange(suitableFPSRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
|
||||
suitableFPSRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void setFocusArea(Camera.Parameters parameters) {
|
||||
if (parameters.getMaxNumFocusAreas() > 0) {
|
||||
Log.i(TAG, "Old focus areas: " + toString(parameters.getFocusAreas()));
|
||||
List<Camera.Area> middleArea = buildMiddleArea(AREA_PER_1000);
|
||||
Log.i(TAG, "Setting focus area to : " + toString(middleArea));
|
||||
parameters.setFocusAreas(middleArea);
|
||||
} else {
|
||||
Log.i(TAG, "Device does not support focus areas");
|
||||
}
|
||||
}
|
||||
|
||||
public static void setMetering(Camera.Parameters parameters) {
|
||||
if (parameters.getMaxNumMeteringAreas() > 0) {
|
||||
Log.i(TAG, "Old metering areas: " + parameters.getMeteringAreas());
|
||||
List<Camera.Area> middleArea = buildMiddleArea(AREA_PER_1000);
|
||||
Log.i(TAG, "Setting metering area to : " + toString(middleArea));
|
||||
parameters.setMeteringAreas(middleArea);
|
||||
} else {
|
||||
Log.i(TAG, "Device does not support metering areas");
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Camera.Area> buildMiddleArea(int areaPer1000) {
|
||||
return Collections.singletonList(
|
||||
new Camera.Area(new Rect(-areaPer1000, -areaPer1000, areaPer1000, areaPer1000), 1));
|
||||
}
|
||||
|
||||
public static void setVideoStabilization(Camera.Parameters parameters) {
|
||||
if (parameters.isVideoStabilizationSupported()) {
|
||||
if (parameters.getVideoStabilization()) {
|
||||
Log.i(TAG, "Video stabilization already enabled");
|
||||
} else {
|
||||
Log.i(TAG, "Enabling video stabilization...");
|
||||
parameters.setVideoStabilization(true);
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, "This device does not support video stabilization");
|
||||
}
|
||||
}
|
||||
|
||||
public static void setBarcodeSceneMode(Camera.Parameters parameters) {
|
||||
if (Camera.Parameters.SCENE_MODE_BARCODE.equals(parameters.getSceneMode())) {
|
||||
Log.i(TAG, "Barcode scene mode already set");
|
||||
return;
|
||||
}
|
||||
String sceneMode = findSettableValue("scene mode",
|
||||
parameters.getSupportedSceneModes(),
|
||||
Camera.Parameters.SCENE_MODE_BARCODE);
|
||||
if (sceneMode != null) {
|
||||
parameters.setSceneMode(sceneMode);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setZoom(Camera.Parameters parameters, double targetZoomRatio) {
|
||||
if (parameters.isZoomSupported()) {
|
||||
Integer zoom = indexOfClosestZoom(parameters, targetZoomRatio);
|
||||
if (zoom == null) {
|
||||
return;
|
||||
}
|
||||
if (parameters.getZoom() == zoom) {
|
||||
Log.i(TAG, "Zoom is already set to " + zoom);
|
||||
} else {
|
||||
Log.i(TAG, "Setting zoom to " + zoom);
|
||||
parameters.setZoom(zoom);
|
||||
}
|
||||
} else {
|
||||
Log.i(TAG, "Zoom is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
private static Integer indexOfClosestZoom(Camera.Parameters parameters, double targetZoomRatio) {
|
||||
List<Integer> ratios = parameters.getZoomRatios();
|
||||
Log.i(TAG, "Zoom ratios: " + ratios);
|
||||
int maxZoom = parameters.getMaxZoom();
|
||||
if (ratios == null || ratios.isEmpty() || ratios.size() != maxZoom + 1) {
|
||||
Log.w(TAG, "Invalid zoom ratios!");
|
||||
return null;
|
||||
}
|
||||
double target100 = 100.0 * targetZoomRatio;
|
||||
double smallestDiff = Double.POSITIVE_INFINITY;
|
||||
int closestIndex = 0;
|
||||
for (int i = 0; i < ratios.size(); i++) {
|
||||
double diff = Math.abs(ratios.get(i) - target100);
|
||||
if (diff < smallestDiff) {
|
||||
smallestDiff = diff;
|
||||
closestIndex = i;
|
||||
}
|
||||
}
|
||||
Log.i(TAG, "Chose zoom ratio of " + (ratios.get(closestIndex) / 100.0));
|
||||
return closestIndex;
|
||||
}
|
||||
|
||||
public static void setInvertColor(Camera.Parameters parameters) {
|
||||
if (Camera.Parameters.EFFECT_NEGATIVE.equals(parameters.getColorEffect())) {
|
||||
Log.i(TAG, "Negative effect already set");
|
||||
return;
|
||||
}
|
||||
String colorMode = findSettableValue("color effect",
|
||||
parameters.getSupportedColorEffects(),
|
||||
Camera.Parameters.EFFECT_NEGATIVE);
|
||||
if (colorMode != null) {
|
||||
parameters.setColorEffect(colorMode);
|
||||
}
|
||||
}
|
||||
|
||||
public static Point findBestPreviewSizeValue(Camera.Parameters parameters, Point screenResolution) {
|
||||
|
||||
List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();
|
||||
if (rawSupportedSizes == null) {
|
||||
Log.w(TAG, "Device returned no supported preview sizes; using default");
|
||||
Camera.Size defaultSize = parameters.getPreviewSize();
|
||||
if (defaultSize == null) {
|
||||
throw new IllegalStateException("Parameters contained no preview size!");
|
||||
}
|
||||
return new Point(defaultSize.width, defaultSize.height);
|
||||
}
|
||||
|
||||
if (Log.isLoggable(TAG, Log.INFO)) {
|
||||
StringBuilder previewSizesString = new StringBuilder();
|
||||
for (Camera.Size size : rawSupportedSizes) {
|
||||
previewSizesString.append(size.width).append('x').append(size.height).append(' ');
|
||||
}
|
||||
Log.i(TAG, "Supported preview sizes: " + previewSizesString);
|
||||
}
|
||||
|
||||
double screenAspectRatio = screenResolution.x / (double) screenResolution.y;
|
||||
|
||||
// Find a suitable size, with max resolution
|
||||
int maxResolution = 0;
|
||||
Camera.Size maxResPreviewSize = null;
|
||||
for (Camera.Size size : rawSupportedSizes) {
|
||||
int realWidth = size.width;
|
||||
int realHeight = size.height;
|
||||
int resolution = realWidth * realHeight;
|
||||
if (resolution < MIN_PREVIEW_PIXELS) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean isCandidatePortrait = realWidth < realHeight;
|
||||
int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth;
|
||||
int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight;
|
||||
double aspectRatio = maybeFlippedWidth / (double) maybeFlippedHeight;
|
||||
double distortion = Math.abs(aspectRatio - screenAspectRatio);
|
||||
if (distortion > MAX_ASPECT_DISTORTION) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (maybeFlippedWidth == screenResolution.x && maybeFlippedHeight == screenResolution.y) {
|
||||
Point exactPoint = new Point(realWidth, realHeight);
|
||||
Log.i(TAG, "Found preview size exactly matching screen size: " + exactPoint);
|
||||
return exactPoint;
|
||||
}
|
||||
|
||||
// Resolution is suitable; record the one with max resolution
|
||||
if (resolution > maxResolution) {
|
||||
maxResolution = resolution;
|
||||
maxResPreviewSize = size;
|
||||
}
|
||||
}
|
||||
|
||||
// If no exact match, use largest preview size. This was not a great idea on older devices because
|
||||
// of the additional computation needed. We're likely to get here on newer Android 4+ devices, where
|
||||
// the CPU is much more powerful.
|
||||
if (maxResPreviewSize != null) {
|
||||
Point largestSize = new Point(maxResPreviewSize.width, maxResPreviewSize.height);
|
||||
Log.i(TAG, "Using largest suitable preview size: " + largestSize);
|
||||
return largestSize;
|
||||
}
|
||||
|
||||
// If there is nothing at all suitable, return current preview size
|
||||
Camera.Size defaultPreview = parameters.getPreviewSize();
|
||||
if (defaultPreview == null) {
|
||||
throw new IllegalStateException("Parameters contained no preview size!");
|
||||
}
|
||||
Point defaultSize = new Point(defaultPreview.width, defaultPreview.height);
|
||||
Log.i(TAG, "No suitable preview sizes, using default: " + defaultSize);
|
||||
return defaultSize;
|
||||
}
|
||||
|
||||
private static String findSettableValue(String name,
|
||||
Collection<String> supportedValues,
|
||||
String... desiredValues) {
|
||||
Log.i(TAG, "Requesting " + name + " value from among: " + Arrays.toString(desiredValues));
|
||||
Log.i(TAG, "Supported " + name + " values: " + supportedValues);
|
||||
if (supportedValues != null) {
|
||||
for (String desiredValue : desiredValues) {
|
||||
if (supportedValues.contains(desiredValue)) {
|
||||
Log.i(TAG, "Can set " + name + " to: " + desiredValue);
|
||||
return desiredValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.i(TAG, "No supported values match");
|
||||
return null;
|
||||
}
|
||||
|
||||
private static String toString(Collection<int[]> arrays) {
|
||||
if (arrays == null || arrays.isEmpty()) {
|
||||
return "[]";
|
||||
}
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append('[');
|
||||
Iterator<int[]> it = arrays.iterator();
|
||||
while (it.hasNext()) {
|
||||
buffer.append(Arrays.toString(it.next()));
|
||||
if (it.hasNext()) {
|
||||
buffer.append(", ");
|
||||
}
|
||||
}
|
||||
buffer.append(']');
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
private static String toString(Iterable<Camera.Area> areas) {
|
||||
if (areas == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (Camera.Area area : areas) {
|
||||
result.append(area.rect).append(':').append(area.weight).append(' ');
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static String collectStats(Camera.Parameters parameters) {
|
||||
return collectStats(parameters.flatten());
|
||||
}
|
||||
|
||||
public static String collectStats(CharSequence flattenedParams) {
|
||||
StringBuilder result = new StringBuilder(1000);
|
||||
|
||||
result.append("BOARD=").append(Build.BOARD).append('\n');
|
||||
result.append("BRAND=").append(Build.BRAND).append('\n');
|
||||
result.append("CPU_ABI=").append(Build.CPU_ABI).append('\n');
|
||||
result.append("DEVICE=").append(Build.DEVICE).append('\n');
|
||||
result.append("DISPLAY=").append(Build.DISPLAY).append('\n');
|
||||
result.append("FINGERPRINT=").append(Build.FINGERPRINT).append('\n');
|
||||
result.append("HOST=").append(Build.HOST).append('\n');
|
||||
result.append("ID=").append(Build.ID).append('\n');
|
||||
result.append("MANUFACTURER=").append(Build.MANUFACTURER).append('\n');
|
||||
result.append("MODEL=").append(Build.MODEL).append('\n');
|
||||
result.append("PRODUCT=").append(Build.PRODUCT).append('\n');
|
||||
result.append("TAGS=").append(Build.TAGS).append('\n');
|
||||
result.append("TIME=").append(Build.TIME).append('\n');
|
||||
result.append("TYPE=").append(Build.TYPE).append('\n');
|
||||
result.append("USER=").append(Build.USER).append('\n');
|
||||
result.append("VERSION.CODENAME=").append(Build.VERSION.CODENAME).append('\n');
|
||||
result.append("VERSION.INCREMENTAL=").append(Build.VERSION.INCREMENTAL).append('\n');
|
||||
result.append("VERSION.RELEASE=").append(Build.VERSION.RELEASE).append('\n');
|
||||
result.append("VERSION.SDK_INT=").append(Build.VERSION.SDK_INT).append('\n');
|
||||
|
||||
if (flattenedParams != null) {
|
||||
String[] params = SEMICOLON.split(flattenedParams);
|
||||
Arrays.sort(params);
|
||||
for (String param : params) {
|
||||
result.append(param).append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
||||
336
lib/src/main/java/com/king/zxing/camera/CameraManager.java
Normal file
@@ -0,0 +1,336 @@
|
||||
package com.king.zxing.camera;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2008 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.Camera;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceHolder;
|
||||
import com.google.zxing.PlanarYUVLuminanceSource;
|
||||
import com.king.zxing.camera.open.OpenCamera;
|
||||
import com.king.zxing.camera.open.OpenCameraInterface;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This object wraps the Camera service object and expects to be the only one talking to it. The
|
||||
* implementation encapsulates the steps needed to take preview-sized images, which are used for
|
||||
* both preview and decoding.
|
||||
*
|
||||
* @author dswitkin@google.com (Daniel Switkin)
|
||||
*/
|
||||
@SuppressWarnings("deprecation") // camera APIs
|
||||
public final class CameraManager {
|
||||
|
||||
private static final String TAG = CameraManager.class.getSimpleName();
|
||||
|
||||
private static final int MIN_FRAME_WIDTH = 240;
|
||||
private static final int MIN_FRAME_HEIGHT = 240;
|
||||
private static final int MAX_FRAME_WIDTH = 1200; // = 5/8 * 1920
|
||||
private static final int MAX_FRAME_HEIGHT = 675; // = 5/8 * 1080
|
||||
|
||||
private final Context context;
|
||||
private final CameraConfigurationManager configManager;
|
||||
private OpenCamera camera;
|
||||
private AutoFocusManager autoFocusManager;
|
||||
private Rect framingRect;
|
||||
private Rect framingRectInPreview;
|
||||
private boolean initialized;
|
||||
private boolean previewing;
|
||||
private int requestedCameraId = OpenCameraInterface.NO_REQUESTED_CAMERA;
|
||||
private int requestedFramingRectWidth;
|
||||
private int requestedFramingRectHeight;
|
||||
/**
|
||||
* Preview frames are delivered here, which we pass on to the registered handler. Make sure to
|
||||
* clear the handler so it will only receive one message.
|
||||
*/
|
||||
private final PreviewCallback previewCallback;
|
||||
|
||||
public CameraManager(Context context) {
|
||||
this.context = context;
|
||||
this.configManager = new CameraConfigurationManager(context);
|
||||
previewCallback = new PreviewCallback(configManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the camera driver and initializes the hardware parameters.
|
||||
*
|
||||
* @param holder The surface object which the camera will draw preview frames into.
|
||||
* @throws IOException Indicates the camera driver failed to open.
|
||||
*/
|
||||
public synchronized void openDriver(SurfaceHolder holder) throws IOException {
|
||||
OpenCamera theCamera = camera;
|
||||
if (theCamera == null) {
|
||||
theCamera = OpenCameraInterface.open(requestedCameraId);
|
||||
if (theCamera == null) {
|
||||
throw new IOException("Camera.open() failed to return object from driver");
|
||||
}
|
||||
camera = theCamera;
|
||||
}
|
||||
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
configManager.initFromCameraParameters(theCamera);
|
||||
if (requestedFramingRectWidth > 0 && requestedFramingRectHeight > 0) {
|
||||
setManualFramingRect(requestedFramingRectWidth, requestedFramingRectHeight);
|
||||
requestedFramingRectWidth = 0;
|
||||
requestedFramingRectHeight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Camera cameraObject = theCamera.getCamera();
|
||||
Camera.Parameters parameters = cameraObject.getParameters();
|
||||
String parametersFlattened = parameters == null ? null : parameters.flatten(); // Save these, temporarily
|
||||
try {
|
||||
configManager.setDesiredCameraParameters(theCamera, false);
|
||||
} catch (RuntimeException re) {
|
||||
// Driver failed
|
||||
Log.w(TAG, "Camera rejected parameters. Setting only minimal safe-mode parameters");
|
||||
Log.i(TAG, "Resetting to saved camera params: " + parametersFlattened);
|
||||
// Reset:
|
||||
if (parametersFlattened != null) {
|
||||
parameters = cameraObject.getParameters();
|
||||
parameters.unflatten(parametersFlattened);
|
||||
try {
|
||||
cameraObject.setParameters(parameters);
|
||||
configManager.setDesiredCameraParameters(theCamera, true);
|
||||
} catch (RuntimeException re2) {
|
||||
// Well, darn. Give up
|
||||
Log.w(TAG, "Camera rejected even safe-mode parameters! No configuration");
|
||||
}
|
||||
}
|
||||
}
|
||||
cameraObject.setPreviewDisplay(holder);
|
||||
|
||||
}
|
||||
|
||||
public synchronized boolean isOpen() {
|
||||
return camera != null;
|
||||
}
|
||||
|
||||
public OpenCamera getOpenCamera() {
|
||||
return camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the camera driver if still in use.
|
||||
*/
|
||||
public synchronized void closeDriver() {
|
||||
if (camera != null) {
|
||||
camera.getCamera().release();
|
||||
camera = null;
|
||||
// Make sure to clear these each time we close the camera, so that any scanning rect
|
||||
// requested by intent is forgotten.
|
||||
framingRect = null;
|
||||
framingRectInPreview = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asks the camera hardware to begin drawing preview frames to the screen.
|
||||
*/
|
||||
public synchronized void startPreview() {
|
||||
OpenCamera theCamera = camera;
|
||||
if (theCamera != null && !previewing) {
|
||||
theCamera.getCamera().startPreview();
|
||||
previewing = true;
|
||||
autoFocusManager = new AutoFocusManager(context, theCamera.getCamera());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells the camera to stop drawing preview frames.
|
||||
*/
|
||||
public synchronized void stopPreview() {
|
||||
if (autoFocusManager != null) {
|
||||
autoFocusManager.stop();
|
||||
autoFocusManager = null;
|
||||
}
|
||||
if (camera != null && previewing) {
|
||||
camera.getCamera().stopPreview();
|
||||
previewCallback.setHandler(null, 0);
|
||||
previewing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for {@link com.king.zxing.CaptureActivity}
|
||||
*
|
||||
* @param newSetting if {@code true}, light should be turned on if currently off. And vice versa.
|
||||
*/
|
||||
public synchronized void setTorch(boolean newSetting) {
|
||||
OpenCamera theCamera = camera;
|
||||
if (theCamera != null && newSetting != configManager.getTorchState(theCamera.getCamera())) {
|
||||
boolean wasAutoFocusManager = autoFocusManager != null;
|
||||
if (wasAutoFocusManager) {
|
||||
autoFocusManager.stop();
|
||||
autoFocusManager = null;
|
||||
}
|
||||
configManager.setTorch(theCamera.getCamera(), newSetting);
|
||||
if (wasAutoFocusManager) {
|
||||
autoFocusManager = new AutoFocusManager(context, theCamera.getCamera());
|
||||
autoFocusManager.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A single preview frame will be returned to the handler supplied. The data will arrive as byte[]
|
||||
* in the message.obj field, with width and height encoded as message.arg1 and message.arg2,
|
||||
* respectively.
|
||||
*
|
||||
* @param handler The handler to send the message to.
|
||||
* @param message The what field of the message to be sent.
|
||||
*/
|
||||
public synchronized void requestPreviewFrame(Handler handler, int message) {
|
||||
OpenCamera theCamera = camera;
|
||||
if (theCamera != null && previewing) {
|
||||
previewCallback.setHandler(handler, message);
|
||||
theCamera.getCamera().setOneShotPreviewCallback(previewCallback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the framing rect which the UI should draw to show the user where to place the
|
||||
* barcode. This target helps with alignment as well as forces the user to hold the device
|
||||
* far enough away to ensure the image will be in focus.
|
||||
*
|
||||
* @return The rectangle to draw on screen in window coordinates.
|
||||
*/
|
||||
public synchronized Rect getFramingRect() {
|
||||
if (framingRect == null) {
|
||||
if (camera == null) {
|
||||
return null;
|
||||
}
|
||||
Point screenResolution = configManager.getScreenResolution();
|
||||
if (screenResolution == null) {
|
||||
// Called early, before init even finished
|
||||
return null;
|
||||
}
|
||||
|
||||
int width = findDesiredDimensionInRange(screenResolution.x, MIN_FRAME_WIDTH, MAX_FRAME_WIDTH);
|
||||
int height = findDesiredDimensionInRange(screenResolution.y, MIN_FRAME_HEIGHT, MAX_FRAME_HEIGHT);
|
||||
|
||||
int leftOffset = (screenResolution.x - width) / 2;
|
||||
int topOffset = (screenResolution.y - height) / 2;
|
||||
framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
|
||||
Log.d(TAG, "Calculated framing rect: " + framingRect);
|
||||
}
|
||||
return framingRect;
|
||||
}
|
||||
|
||||
private static int findDesiredDimensionInRange(int resolution, int hardMin, int hardMax) {
|
||||
int dim = 5 * resolution / 8; // Target 5/8 of each dimension
|
||||
if (dim < hardMin) {
|
||||
return hardMin;
|
||||
}
|
||||
if (dim > hardMax) {
|
||||
return hardMax;
|
||||
}
|
||||
return dim;
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #getFramingRect} but coordinates are in terms of the preview frame,
|
||||
* not UI / screen.
|
||||
*
|
||||
* @return {@link Rect} expressing barcode scan area in terms of the preview size
|
||||
*/
|
||||
public synchronized Rect getFramingRectInPreview() {
|
||||
if (framingRectInPreview == null) {
|
||||
Rect framingRect = getFramingRect();
|
||||
if (framingRect == null) {
|
||||
return null;
|
||||
}
|
||||
Rect rect = new Rect(framingRect);
|
||||
Point cameraResolution = configManager.getCameraResolution();
|
||||
Point screenResolution = configManager.getScreenResolution();
|
||||
if (cameraResolution == null || screenResolution == null) {
|
||||
// Called early, before init even finished
|
||||
return null;
|
||||
}
|
||||
rect.left = rect.left * cameraResolution.x / screenResolution.x;
|
||||
rect.right = rect.right * cameraResolution.x / screenResolution.x;
|
||||
rect.top = rect.top * cameraResolution.y / screenResolution.y;
|
||||
rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
|
||||
framingRectInPreview = rect;
|
||||
}
|
||||
return framingRectInPreview;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allows third party apps to specify the camera ID, rather than determine
|
||||
* it automatically based on available cameras and their orientation.
|
||||
*
|
||||
* @param cameraId camera ID of the camera to use. A negative value means "no preference".
|
||||
*/
|
||||
public synchronized void setManualCameraId(int cameraId) {
|
||||
requestedCameraId = cameraId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows third party apps to specify the scanning rectangle dimensions, rather than determine
|
||||
* them automatically based on screen resolution.
|
||||
*
|
||||
* @param width The width in pixels to scan.
|
||||
* @param height The height in pixels to scan.
|
||||
*/
|
||||
public synchronized void setManualFramingRect(int width, int height) {
|
||||
if (initialized) {
|
||||
Point screenResolution = configManager.getScreenResolution();
|
||||
if (width > screenResolution.x) {
|
||||
width = screenResolution.x;
|
||||
}
|
||||
if (height > screenResolution.y) {
|
||||
height = screenResolution.y;
|
||||
}
|
||||
int leftOffset = (screenResolution.x - width) / 2;
|
||||
int topOffset = (screenResolution.y - height) / 2;
|
||||
framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
|
||||
Log.d(TAG, "Calculated manual framing rect: " + framingRect);
|
||||
framingRectInPreview = null;
|
||||
} else {
|
||||
requestedFramingRectWidth = width;
|
||||
requestedFramingRectHeight = height;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A factory method to build the appropriate LuminanceSource object based on the format
|
||||
* of the preview buffers, as described by Camera.Parameters.
|
||||
*
|
||||
* @param data A preview frame.
|
||||
* @param width The width of the image.
|
||||
* @param height The height of the image.
|
||||
* @return A PlanarYUVLuminanceSource instance.
|
||||
*/
|
||||
public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
|
||||
Rect rect = getFramingRectInPreview();
|
||||
if (rect == null) {
|
||||
return null;
|
||||
}
|
||||
// Go ahead and assume it's YUV rather than die.
|
||||
return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
|
||||
rect.width(), rect.height(), false);
|
||||
}
|
||||
|
||||
}
|
||||
43
lib/src/main/java/com/king/zxing/camera/FrontLightMode.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package com.king.zxing.camera;
|
||||
/*
|
||||
* Copyright (C) 2012 ZXing authors
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import com.king.zxing.Preferences;
|
||||
|
||||
/**
|
||||
* Enumerates settings of the preference controlling the front light.
|
||||
*/
|
||||
public enum FrontLightMode {
|
||||
|
||||
/** Always on. */
|
||||
ON,
|
||||
/** On only when ambient light is low. */
|
||||
AUTO,
|
||||
/** Always off. */
|
||||
OFF;
|
||||
|
||||
private static FrontLightMode parse(String modeString) {
|
||||
return modeString == null ? OFF : valueOf(modeString);
|
||||
}
|
||||
|
||||
public static FrontLightMode readPref(SharedPreferences sharedPrefs) {
|
||||
return parse(sharedPrefs.getString(Preferences.KEY_FRONT_LIGHT_MODE, OFF.toString()));
|
||||
}
|
||||
|
||||
}
|
||||