Updated on 2026-08-14

This commit is contained in:
Tangem 2019-06-13 17:23:45 +03:00
commit bd8d84a85a
680 changed files with 51751 additions and 6799 deletions

1
card-android/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

Binary file not shown.

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

21
card-android/.idea/gradle.xml generated Normal file
View file

@ -0,0 +1,21 @@
<?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$" />
<option value="$PROJECT_DIR$/../tangemcard-common" />
<option value="$PROJECT_DIR$/../tangemserver-android" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>

View file

@ -0,0 +1,9 @@
<component name="libraryTable">
<library name="Gradle: com.google.code.findbugs:jsr305:2.0.1@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.findbugs/jsr305/2.0.1/516c03b21d50a644d538de0f0369c620989cd8f0/jsr305-2.0.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.google.code.gson:gson:2.8.5@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/f645ed69d595b24d4cf8b3fbb64cc505bede8829/gson-2.8.5.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.code.gson/gson/2.8.5/c5b4c491aecb72e7c32a78da0b5c6b9cda8dee0f/gson-2.8.5-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.google.guava:guava:18.0@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/18.0/cce0823396aa693798f8882e64213b1772032b09/guava-18.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/18.0/ad97fe8faaf01a3d3faacecd58e8fa6e78a973ca/guava-18.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.google.protobuf:protobuf-java:2.6.1@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.protobuf/protobuf-java/2.6.1/d9521f2aecb909835746b7a5facf612af5e890e8/protobuf-java-2.6.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.protobuf/protobuf-java/2.6.1/cf47bfc6a10f85d14bc4c078a48d163b5903a291/protobuf-java-2.6.1-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.google.zxing:core:3.3.3@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.zxing/core/3.3.3/b640badcc97f18867c4dfd249ef8d20ec0204c07/core-3.3.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.zxing/core/3.3.3/536b6ab3b3b4607ce86e2ff7f6643db11c473aa7/core-3.3.3-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.jakewharton.retrofit/retrofit2-rxjava2-adapter/1.0.0/e117362a2103f8ec01091ebd2f40aca4e1ada599/retrofit2-rxjava2-adapter-1.0.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.jakewharton.retrofit/retrofit2-rxjava2-adapter/1.0.0/7c4445a9e74b30d72c2dc78165a3b68ec26c800c/retrofit2-rxjava2-adapter-1.0.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.lambdaworks:scrypt:1.4.0@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.lambdaworks/scrypt/1.4.0/906506b74f30c8c20bccd9ed4a11112d8941fe87/scrypt-1.4.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.lambdaworks/scrypt/1.4.0/57d1aeaa56daeed9ef55a163aa12c6d5f5c333bf/scrypt-1.4.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.madgag.spongycastle:core:1.56.0.0@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.madgag.spongycastle/core/1.56.0.0/9622d6de1407dd3506254fb9b0292eb1206f6991/core-1.56.0.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.madgag.spongycastle/core/1.56.0.0/6e8d501d6dd43960fd553356e352be5abc7ae0a5/core-1.56.0.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.madgag.spongycastle:prov:1.56.0.0@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.madgag.spongycastle/prov/1.56.0.0/871d2315bc3a171c080684ac7fbc55ecd85b6548/prov-1.56.0.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.madgag.spongycastle/prov/1.56.0.0/161658a1584b921311b70e5442e65ae42b083bc3/prov-1.56.0.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.squareup:javawriter:2.1.1@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup/javawriter/2.1.1/67ff45d9ae02e583d0f9b3432a5ebbe05c30c966/javawriter-2.1.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup/javawriter/2.1.1/5b31387d839a5cdaf5b6de3990da01f7f2b963c5/javawriter-2.1.1-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.squareup.okhttp3:logging-interceptor:3.11.0@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/logging-interceptor/3.11.0/f24576b8fd39d34149654df9702f070042083b65/logging-interceptor-3.11.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/logging-interceptor/3.11.0/3e024776aafc5bc55c136a90c2a357a8dc116858/logging-interceptor-3.11.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.squareup.okhttp3:okhttp:3.12.0@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.12.0/b36f4a04584c0fb0d9af2d3401cdff8dacb1ea54/okhttp-3.12.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp3/okhttp/3.12.0/2647551116856a6403d1a21e236837a7501c08a8/okhttp-3.12.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.squareup.okhttp:okhttp:2.7.2@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp/okhttp/2.7.2/20f6463eb19ac61960c5d91a094c2f4f0727dc2e/okhttp-2.7.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okhttp/okhttp/2.7.2/8cd1e7b0888d8d8a591b3c67adf3dc86475bdadc/okhttp-2.7.2-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.squareup.okio:okio:1.15.0@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.15.0/bc28b5a964c8f5721eb58ee3f3c47a9bcbf4f4d8/okio-1.15.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.okio/okio/1.15.0/5667938926bdda03add0eac8c522164ce4180e5e/okio-1.15.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.squareup.retrofit2:converter-gson:2.5.0@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/1c96fc5d0230f57d36cd09e2541d10829a3352a7/converter-gson-2.5.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/converter-gson/2.5.0/e33b9ee015798ca86bc1ce78b4a17aa9f9a53e63/converter-gson-2.5.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: com.squareup.retrofit2:retrofit:2.5.0@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/713ce36037bf24a76a3974c05cb85c3f754b1cc3/retrofit-2.5.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.squareup.retrofit2/retrofit/2.5.0/5c17035548a41ec60f00291163d36596a23ddd1a/retrofit-2.5.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: core-1.56.0.0">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.madgag.spongycastle/core/1.56.0.0/9622d6de1407dd3506254fb9b0292eb1206f6991/core-1.56.0.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.madgag.spongycastle/core/1.56.0.0/6e8d501d6dd43960fd553356e352be5abc7ae0a5/core-1.56.0.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: io.reactivex.rxjava2:rxjava:2.2.0@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.reactivex.rxjava2/rxjava/2.2.0/2c622e8f1cab8d5871d32bb2023af515216056d8/rxjava-2.2.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.reactivex.rxjava2/rxjava/2.2.0/ba88ed6468f28df009c013256be77ad72d313965/rxjava-2.2.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: io.reactivex.rxjava2:rxkotlin:2.3.0@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.reactivex.rxjava2/rxkotlin/2.3.0/37d8fb3406df28bfc84fa0234b11067931369bc5/rxkotlin-2.3.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.reactivex.rxjava2/rxkotlin/2.3.0/ec9571b67d8c8d0f672bb671c6f0ae025636d55c/rxkotlin-2.3.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: javax.inject:javax.inject:1@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/javax.inject/javax.inject/1/6975da39a7040257bd51d21a231b76c915872d38/javax.inject-1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/javax.inject/javax.inject/1/a00123f261762a7c5e0ec916a2c7c8298d29c400/javax.inject-1-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: junit:junit:4.12@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/2973d150c0dc1fefe998f834810d68f278ea58ec/junit-4.12.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/junit/junit/4.12/a6c32b40bf3d76eca54e3c601e5d1470c86fcdfa/junit-4.12-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: net.jcip:jcip-annotations:1.0@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.jcip/jcip-annotations/1.0/afba4942caaeaf46aab0b976afd57cc7c181467e/jcip-annotations-1.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.jcip/jcip-annotations/1.0/d853625d8001c00d40abed553e670279504699f9/jcip-annotations-1.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: net.sf.kxml:kxml2:2.3.0@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.kxml/kxml2/2.3.0/ccbc77a5fd907ef863c29f3596c6f54ffa4e9442/kxml2-2.3.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/net.sf.kxml/kxml2/2.3.0/309cd2cff7260e465792fda3dcbb063b730d8050/kxml2-2.3.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: org.bitcoinj:bitcoinj-core:0.14.7@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.bitcoinj/bitcoinj-core/0.14.7/5e58d6921e1d8dfce81525b22c0de97f34be1f5c/bitcoinj-core-0.14.7.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.bitcoinj/bitcoinj-core/0.14.7/ab6570ca374f7b8bbafb3e7cb5e2169c0c01c96e/bitcoinj-core-0.14.7-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: org.bitcoinj:orchid:1.2.1@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.bitcoinj/orchid/1.2.1/53beecbbb57040608d0c54a7408ee3eb06e71a59/orchid-1.2.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.bitcoinj/orchid/1.2.1/e03d75301b0670b314fe38242bae4326933efba8/orchid-1.2.1-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: org.greenrobot:eventbus:3.1.1@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.greenrobot/eventbus/3.1.1/5cd91fd014f5b72292f959748c20a2cefefae77d/eventbus-3.1.1.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.greenrobot/eventbus/3.1.1/29acbdd4c7d904e414d33955e367b9561b13079f/eventbus-3.1.1-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: org.hamcrest:hamcrest-core:1.3@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/1dc37250fbc78e23a65a67fbbaf71d2e9cbc3c0b/hamcrest-core-1.3-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: org.hamcrest:hamcrest-integration:1.3@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-integration/1.3/5de0c73fef18917cd85d0ab70bb23818685e4dfd/hamcrest-integration-1.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-integration/1.3/ae7787a563e6a1b1f17fd4ac43be3a3c8830cfda/hamcrest-integration-1.3-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: org.hamcrest:hamcrest-library:1.3@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-library/1.3/4785a3c21320980282f9f33d0d1264a69040538f/hamcrest-library-1.3.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-library/1.3/47a7ee46628ab7133129cd7cef1e92657bc275e/hamcrest-library-1.3-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: org.jetbrains:annotations:13.0@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/919f0dfe192fb4e063e7dacadee7f8bb9a2672a9/annotations-13.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.jetbrains/annotations/13.0/5991ca87ef1fb5544943d9abc5a9a37583fabe03/annotations-13.0-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: org.reactivestreams:reactive-streams:1.0.2@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.reactivestreams/reactive-streams/1.0.2/323964c36556eb0e6209f65c1cef72b53b461ab8/reactive-streams-1.0.2.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.reactivestreams/reactive-streams/1.0.2/fb592a3d57b11e71eb7a6211dd12ba824c5dd037/reactive-streams-1.0.2-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: org.slf4j:slf4j-api:1.7.20@jar">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.20/867d63093eff0a0cb527bf13d397d850af3dcae3/slf4j-api-1.7.20.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.20/a12636375205fa54af1ec30d1ca2e6dbb96bf9bd/slf4j-api-1.7.20-sources.jar!/" />
</SOURCES>
</library>
</component>

View file

@ -0,0 +1,11 @@
<component name="libraryTable">
<library name="Gradle: prov-1.56.0.0">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.madgag.spongycastle/prov/1.56.0.0/871d2315bc3a171c080684ac7fbc55ecd85b6548/prov-1.56.0.0.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.madgag.spongycastle/prov/1.56.0.0/161658a1584b921311b70e5442e65ae42b083bc3/prov-1.56.0.0-sources.jar!/" />
</SOURCES>
</library>
</component>

14
card-android/.idea/misc.xml generated Normal file
View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CMakeSettings">
<configurations>
<configuration PROFILE_NAME="Debug" CONFIG_NAME="Debug" />
</configurations>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" 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
card-android/.idea/modules.xml generated Normal file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/../app/app.iml" filepath="$PROJECT_DIR$/../app/app.iml" />
<module fileurl="file://$PROJECT_DIR$/../tangem-access.iml" filepath="$PROJECT_DIR$/../tangem-access.iml" />
<module fileurl="file://$PROJECT_DIR$/tangemcard-android.iml" filepath="$PROJECT_DIR$/tangemcard-android.iml" />
<module fileurl="file://$PROJECT_DIR$/../tangemcard-common/tangemcard-common.iml" filepath="$PROJECT_DIR$/../tangemcard-common/tangemcard-common.iml" />
<module fileurl="file://$PROJECT_DIR$/../tangemserver-android/tangemserver-android.iml" filepath="$PROJECT_DIR$/../tangemserver-android/tangemserver-android.iml" />
</modules>
</component>
</project>

12
card-android/.idea/runConfigurations.xml generated Normal file
View 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>

5549
card-android/.idea/workspace.xml generated Normal file

File diff suppressed because it is too large Load diff

43
card-android/build.gradle Normal file
View file

@ -0,0 +1,43 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'com.github.dcendents.android-maven'
group='com.tangem'
android {
compileSdkVersion 28
defaultConfig {
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
buildToolsVersion '28.0.3'
}
dependencies {
implementation project(':card-common')
// implementation 'com.github.TangemCash.card_android-common:card_android-android:0.1.0'
implementation 'androidx.appcompat:appcompat:1.1.0-alpha04'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.scottyab:rootbeer-lib:0.0.7'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
repositories {
mavenCentral()
}

0
card-android/git Normal file
View file

Binary file not shown.

View file

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

172
card-android/gradlew vendored Normal file
View 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
card-android/gradlew.bat vendored Normal file
View 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

View file

@ -0,0 +1,8 @@
## This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Mon Feb 11 13:27:51 MSK 2019
sdk.dir=/Users/eugene/Library/Android/sdk

21
card-android/proguard-rules.pro vendored Normal file
View 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

View file

@ -0,0 +1,26 @@
package com.tangem.card_android;
import android.content.Context;
import androidx.test.InstrumentationRegistry;
import androidx.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.tangem.card_android.test", appContext.getPackageName());
}
}

View file

@ -0,0 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tangem.card_android" >
<uses-feature
android:name="android.hardware.nfc"
android:required="true" />
<uses-permission android:name="android.permission.NFC" />
</manifest>

View file

@ -0,0 +1,54 @@
package com.tangem.card_android.android.data;
import android.content.Context;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.tangem.card_common.data.external.FirmwaresDigestsProvider;
import com.tangem.card_common.util.Util;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
public class Firmwares implements FirmwaresDigestsProvider {
private static JsonArray jaFirmwares = null;
public Firmwares(Context context) {
try (InputStream is = context.getAssets().open("fw_hashes.json")) {
try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
JsonParser parser = new JsonParser();
jaFirmwares = parser.parse(reader).getAsJsonArray();
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public FirmwaresDigestsProvider.VerifyCodeRecord selectRandomVerifyCodeBlock(String firmwareVersion) {
try {
for (int i = 0; i < jaFirmwares.size(); i++) {
JsonObject jsVersion = jaFirmwares.get(i).getAsJsonObject();
if (jsVersion.get("fw").getAsString().equals(firmwareVersion)) {
FirmwaresDigestsProvider.VerifyCodeRecord result = new FirmwaresDigestsProvider.VerifyCodeRecord();
result.hashAlg = "sha-256";
JsonArray jsHashes = jsVersion.get(result.hashAlg).getAsJsonArray();
result.challenge = Util.hexToBytes(jsVersion.get("challenge").getAsString());
int caseIndex = (Util.byteArrayToInt(Util.generateRandomBytes(4)) & 0xFFFFFF) % jsHashes.size();
JsonObject jsRecord = jsHashes.get(caseIndex).getAsJsonObject();
result.blockIndex = jsRecord.get("block").getAsInt();
result.blockCount = jsRecord.get("count").getAsInt();
result.digest = Util.hexToBytes(jsRecord.get("digest").getAsString());
return result;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

View file

@ -0,0 +1,197 @@
package com.tangem.card_android.android.data;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Base64;
import com.tangem.card_common.data.external.PINsProvider;
import com.tangem.card_common.reader.CardProtocol;
import java.util.ArrayList;
import java.util.List;
import javax.crypto.Cipher;
/**
* Created by dvol on 12.09.2017.
* Global PIN Storage
*/
public class PINStorage implements PINsProvider {
private static String mSavedPIN, mUserPIN, mLastUsedPIN, mEncryptedPIN, mPIN2;
private static SharedPreferences sharedPreferences = null;
public static void init(Context context) {
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
mSavedPIN = sharedPreferences.getString("SavedPIN", null);
mUserPIN = null;
mLastUsedPIN = null;
mEncryptedPIN = null;
mPIN2 = null;
}
@Override
public List<String> getPINs() {
ArrayList<String> result = new ArrayList<>();
if (mLastUsedPIN != null) result.add(mLastUsedPIN);
if (mEncryptedPIN != null && !result.contains(mEncryptedPIN)) result.add(mEncryptedPIN);
if (mUserPIN != null && !result.contains(mUserPIN)) result.add(mUserPIN);
if (mSavedPIN != null && !result.contains(mSavedPIN)) result.add(mSavedPIN);
if (!result.contains(CardProtocol.DefaultPIN)) result.add(CardProtocol.DefaultPIN);
return result;
}
public void setLastUsedPIN(String PIN) {
mLastUsedPIN = PIN;
}
public static void setUserPIN(String PIN) {
mUserPIN = PIN;
}
public static void setPIN2(String PIN) {
mPIN2 = PIN;
}
public static void savePIN(String PIN) {
mSavedPIN = PIN;
if (mSavedPIN != null && !mSavedPIN.isEmpty()) {
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("SavedPIN", mSavedPIN);
editor.apply();
} else {
deletePIN();
}
}
public static void deletePIN() {
SharedPreferences.Editor editor = sharedPreferences.edit();
if (mSavedPIN != null && mSavedPIN.equals(mLastUsedPIN)) {
mLastUsedPIN = null;
}
mSavedPIN = null;
editor.remove("SavedPIN");
editor.apply();
}
public static void saveEncryptedPIN(Cipher cipher, String PIN) {
try {
byte[] iv = cipher.getIV();
byte[] bytes = cipher.doFinal(PIN.getBytes());
String encryptedPIN = Base64.encodeToString(bytes, Base64.NO_WRAP);
String sIV = Base64.encodeToString(iv, Base64.NO_WRAP);
// Log.d("PINStorage", String.format("saveEncryptedPIN: %s, encrypted: %s, iv: %s",PIN,encryptedPIN,sIV));
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("EncryptedPIN", encryptedPIN);
editor.putString("EncryptedIV", sIV);
editor.apply();
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] loadEncryptedIV() {
String sIV = sharedPreferences.getString("EncryptedIV", "");
// Log.d("PINStorage", String.format("loadEncryptedIV: %s",sIV));
return Base64.decode(sIV, Base64.NO_WRAP);
}
public static String loadEncryptedPIN(Cipher cipher) {
String encryptedPIN = sharedPreferences.getString("EncryptedPIN", null);
try {
byte[] bytes = Base64.decode(encryptedPIN, Base64.NO_WRAP);
mEncryptedPIN = new String(cipher.doFinal(bytes));
// Log.d("PINStorage", String.format("loadEncryptedPIN: %s (encrypted: %s)",mEncryptedPIN,encryptedPIN));
} catch (Exception e) {
e.printStackTrace();
mEncryptedPIN = null;
}
return mEncryptedPIN;
}
public static boolean haveEncryptedPIN() {
return sharedPreferences.getString("EncryptedPIN", null) != null;
}
public static void deleteEncryptedPIN() {
if (mEncryptedPIN != null && mEncryptedPIN.equals(mLastUsedPIN)) {
mLastUsedPIN = null;
}
mEncryptedPIN = null;
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.remove("EncryptedPIN");
editor.remove("EncryptedIV");
editor.apply();
}
public static void saveEncryptedPIN2(Cipher cipher, String PIN) {
try {
byte[] iv = cipher.getIV();
byte[] bytes = cipher.doFinal(PIN.getBytes());
String encryptedPIN = Base64.encodeToString(bytes, Base64.NO_WRAP);
String sIV = Base64.encodeToString(iv, Base64.NO_WRAP);
// Log.d("PINStorage", String.format("saveEncryptedPIN: %s, encrypted: %s, iv: %s",PIN,encryptedPIN,sIV));
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("EncryptedPIN2", encryptedPIN);
editor.putString("EncryptedIV2", sIV);
editor.apply();
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] loadEncryptedIV2() {
String sIV = sharedPreferences.getString("EncryptedIV2", "");
// Log.d("PINStorage", String.format("loadEncryptedIV: %s",sIV));
return Base64.decode(sIV, Base64.NO_WRAP);
}
public static String loadEncryptedPIN2(Cipher cipher) {
String encryptedPIN = sharedPreferences.getString("EncryptedPIN2", null);
try {
byte[] bytes = Base64.decode(encryptedPIN, Base64.NO_WRAP);
mPIN2 = new String(cipher.doFinal(bytes));
// Log.d("PINStorage", String.format("loadEncryptedPIN: %s (encrypted: %s)",mEncryptedPIN,encryptedPIN));
} catch (Exception e) {
e.printStackTrace();
mPIN2 = null;
}
return mPIN2;
}
public static boolean haveEncryptedPIN2() {
return sharedPreferences.getString("EncryptedPIN2", null) != null;
}
public static void deleteEncryptedPIN2() {
mPIN2 = null;
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.remove("EncryptedPIN2");
editor.remove("EncryptedIV2");
editor.apply();
}
public String getPIN2() {
return mPIN2;
}
public static String getDefaultPIN() {
return CardProtocol.DefaultPIN;
}
public static String getDefaultPIN2() {
return CardProtocol.DefaultPIN2;
}
public static boolean needInit() {
return sharedPreferences == null;
}
}

View file

@ -0,0 +1,99 @@
package com.tangem.card_android.android.nfc
import android.content.Context
import android.os.Build
import android.view.View
import android.view.animation.Animation
import android.view.animation.DecelerateInterpolator
import android.view.animation.Transformation
import android.widget.ImageView
import android.widget.RelativeLayout
import androidx.appcompat.widget.LinearLayoutCompat
class NfcDeviceAntennaLocation(private var context: Context,
private var ivHandCardHorizontal: ImageView,
private var ivHandCardVertical: ImageView,
private var llHand: LinearLayoutCompat,
private var llNfc: LinearLayoutCompat) {
companion object {
const val CARD_ON_BACK = 0
const val CARD_ON_FRONT = 1
const val CARD_ORIENTATION_HORIZONTAL = 0
const val CARD_ORIENTATION_VERTICAL = 1
}
var orientation: Int = 0
var fullName: String = ""
var x: Float = 0.toFloat()
var y: Float = 0.toFloat()
var z: Int = 0
fun init() {
getAntennaLocation()
setCardOrientation()
animate()
}
fun animate() {
val lp = llHand.layoutParams as RelativeLayout.LayoutParams
val lp2 = llNfc.layoutParams as RelativeLayout.LayoutParams
val dp = context.resources.displayMetrics.density
val lm = dp * (69 + x * 75)
lp.topMargin = (dp * (-100 + y * 250)).toInt()
lp2.topMargin = (dp * (-125 + y * 250)).toInt()
llNfc.layoutParams = lp2
val a = object : Animation() {
override fun applyTransformation(interpolatedTime: Float, t: Transformation) {
lp.leftMargin = (lm * interpolatedTime).toInt()
llHand.layoutParams = lp
}
}
a.duration = 2000
a.interpolator = DecelerateInterpolator()
llHand.startAnimation(a)
}
private fun getAntennaLocation() {
val codename = Build.DEVICE
// default values
this.orientation = 0
this.fullName = ""
this.x = 0.5f
this.y = 0.35f
this.z = 0
for (nfcLocation in NfcLocation.values()) {
if (codename.startsWith(nfcLocation.codename)) {
this.fullName = nfcLocation.fullName
this.orientation = nfcLocation.orientation
this.x = nfcLocation.x / 100f
this.y = nfcLocation.y / 100f
this.z = nfcLocation.z
}
}
}
private fun setCardOrientation() {
when (orientation) {
CARD_ORIENTATION_HORIZONTAL -> {
ivHandCardHorizontal.visibility = View.VISIBLE
ivHandCardVertical.visibility = View.GONE
}
CARD_ORIENTATION_VERTICAL -> {
ivHandCardVertical.visibility = View.VISIBLE
ivHandCardHorizontal.visibility = View.GONE
}
}
// set card z position
when (z) {
NfcDeviceAntennaLocation.CARD_ON_BACK -> llHand.elevation = 0.0f
NfcDeviceAntennaLocation.CARD_ON_FRONT -> llHand.elevation = 30.0f
}
}
}

View file

@ -0,0 +1,26 @@
package com.tangem.card_android.android.nfc
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import com.tangem.card_android.android.reader.NfcManager
/**
* Lifecycle observer for all activities and fragments with nfc
*
* @see NfcManager
*/
class NfcLifecycleObserver(private var nfcManager: NfcManager) : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() {
nfcManager.onResume()
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause() {
nfcManager.onPause()
nfcManager.onStop()
}
}

View file

@ -0,0 +1,245 @@
package com.tangem.card_android.android.nfc
enum class NfcLocation(val codename: String, val fullName: String, val orientation: Int, val x: Int, val y: Int, val z: Int) {
model1("sailfish", "Google Pixel", 0, 65, 25, 0),
model2("walleye", "Google Pixel 2", 0, 40, 15, 0),
model3("taimen", "Google Pixel 2 XL", 0, 40, 15, 0),
model4("marlin", "Google Pixel XL", 0, 65, 25, 0),
model5("blueline", "Google Pixel 3 ", 0, 40, 30, 0),
model6("crosshatch", "Google Pixel 3 XL", 0, 15, 20, 0),
model7("htc_pme", "HTC 10 ", 0, 50, 20, 0),
model8("htc_himau", "HTC One M9", 0, 50, 20, 0),
model9("htc_himaw", "HTC One M9", 0, 50, 20, 0),
model10("htc_oce", "HTC U Ultra", 0, 50, 20, 0),
model11("htc_ocn", "HTC U11", 0, 50, 25, 0),
model12("htc_ocm", "HTC U11+", 0, 50, 60, 0),
model13("htc_ocluhljapan", "HTC U11 Life", 1, 50, 25, 0),
model14("htc_haydugl", "HTC U11 EYEs", 0, 50, 65, 0),
model15("htc_haydtwl", "HTC U11 EYEs", 0, 50, 65, 0),
model16("htc_ime", "HTC U12+", 0, 50, 20, 0),
model17("htc_bre2dugl", "HTC Desire 12s", 0, 50, 10, 0),
model18("htc_imldugl", "HTC U12 Life", 0, 60, 20, 0),
model19("htc_exodugl", "HTC EXODUS 1", 0, 50, 20, 0),
model20("HWFRD", "Huawei Honor 8", 0, 50, 0, 0),
model21("HWDUK", "Huawei Honor 8 Pro", 0, 50, 0, 0),
model22("HWSTF", "Huawei Honor 9", 0, 50, 0, 0),
model23("HWCOL", "Huawei Honor 10", 0, 50, 0, 0),
model24("HWRVL", "Huawei Honor Note 10", 0, 50, 0, 0),
model25("HWBKL", "Huawei Honor View 10", 0, 50, 0, 0),
model26("HWPCT", "Huawei HONOR V20", 0, 60, 20, 0),
model27("HWTNY", "Huawei Honor Magic 2", 0, 50, 0, 0),
model28("HWBLN-H", "Huawei Mate 9 Lite / Honor 6X", 0, 40, 0, 0),
model29("HWALP", "Huawei Mate 10", 0, 50, 0, 0),
model30("HWBLA", "Huawei Mate 10 Pro", 0, 50, 0, 0),
model31("HWHMA", "Huawei Mate 20", 0, 50, 20, 0),
model32("HWEVR", "Huawei Mate 20 X", 0, 50, 20, 0),
model33("HWLYA", "Huawei Mate 20 Pro", 0, 50, 20, 0),
model34("angler", "Huawei Nexus 6P", 0, 40, 15, 0),
model35("hwALE-", "Huawei P8 Lite ", 1, 75, 65, 0),
model36("HWPRA-H", "Huawei P8 Lite 2017", 0, 50, 25, 0),
model37("HWEVA", "Huawei P9", 0, 50, 0, 0),
model38("HWVIE", "Huawei P9 Plus", 0, 50, 0, 0),
model39("HWVNS-?", "Huawei P9 Lite", 0, 60, 45, 0),
model40("HWVTR", "Huawei P10", 0, 50, 0, 0),
model41("HWWAS-H", "Huawei P10 lite", 0, 50, 0, 0),
model42("HWVKY", "Huawei P10 Plus", 0, 50, 0, 0),
model43("HWEML", "Huawei P20", 0, 50, 50, 0),
model44("HWANE", "Huawei P20 Lite", 0, 50, 20, 0),
model45("HWCLT", "Huawei P20 Pro", 0, 50, 50, 0),
model46("HW-01K", "Huawei P20 Pro", 0, 50, 50, 0),
model47("HWELE", "Huawei P30", 0, 50, 50, 0),
model48("HWVOG", "Huawei P30 Pro", 0, 50, 50, 0),
model49("HW-02L", "Huawei P30 Pro", 0, 50, 50, 0),
model50("HWFIG-H", "Huawei P Smart", 0, 45, 0, 0),
model51("HWPOT-H", "Huawei P smart 2019", 0, 50, 20, 0),
model52("h1", "LG G5", 0, 30, 20, 0),
model53("lucye", "LG G6", 0, 50, 50, 0),
model54("judyln", "LG G7", 0, 50, 50, 0),
model55("hammerhead", "LG Nexus 5", 0, 50, 50, 0),
model56("bullhead", "LG Nexus 5X", 0, 45, 15, 0),
model57("mh", "LG Q6", 1, 25, 50, 0),
model58("ph2n", "LG Stylo 2+", 0, 75, 20, 0),
model59("sf340n", "LG Stylo 3+", 0, 75, 20, 0),
model60("ph1n", "LG Stylus 2", 0, 75, 20, 0),
model61("msf3", "LG Stylus 3", 0, 75, 20, 0),
model62("elsa", "LG V20", 0, 50, 15, 0),
model63("joan", "LG V30", 0, 50, 50, 0),
model64("L-01K", "LG V30+", 0, 50, 50, 0),
model65("judyp", "LG V35 ThinQ", 0, 50, 50, 0),
model66("judyln", "LG G7 ThinQ", 0, 50, 50, 0),
model67("L-02K", "LG JOJO", 0, 50, 60, 0),
model68("mcv5a", "LG Q7", 0, 50, 60, 0),
model69("cv7a", "LG Stylo 4", 0, 50, 60, 0),
model70("nora_8917_n", "Motorola Moto E5", 0, 50, 40, 0),
model71("potter_n", "Motorola Moto G5 Plus", 0, 50, 5, 0),
model72("montana_n", "Motorola Moto G5s", 0, 50, 5, 0),
model73("sanders_n", "Motorola Moto G5s Plus", 0, 50, 5, 0),
model74("ali_n", "Motorola Moto G6", 0, 50, 35, 0),
model75("aljeter_n", "Motorola Moto G6 Play", 0, 50, 35, 0),
model76("evert_n", "Motorola Moto G6 Plus", 0, 40, 40, 0),
model77("river_n", "Motorola moto g(7)", 0, 50, 50, 0),
model78("lake_n", "Motorola moto g(7) plus", 0, 50, 50, 0),
model79("payton", "Motorola Moto X4", 0, 50, 40, 0),
model80("nash", "Motorola Moto Z2 Force", 0, 50, 40, 0),
model81("albus", "Motorola Moto Z2 Play", 0, 50, 5, 0),
model82("beckham", "Motorola Moto Z3 Play", 0, 50, 0, 0),
model83("shamu", "Motorola Nexus 6", 0, 50, 60, 0),
model84("NE1", "Nokia 3", 0, 40, 30, 0),
model85("ND1", "Nokia 5", 0, 40, 30, 0),
model86("PLE", "Nokia 6", 0, 50, 20, 0),
model87("PL2_sprout", "Nokia 6.1", 0, 50, 0, 0),
model88("C1N", "Nokia 7", 0, 50, 25, 0),
model89("B2N", "Nokia 7 Plus", 0, 75, 10, 0),
model90("NB1", "Nokia 8", 0, 50, 30, 0),
model91("A1N_sprout", "Nokia 8 Sirocco", 0, 90, 0, 0),
model92("A1N", "Nokia 8 Sirocco", 0, 90, 0, 0),
model93("OnePlus3", "OnePlus 3", 0, 50, 15, 0),
model94("OnePlus3T", "OnePlus 3t", 0, 50, 15, 0),
model95("OnePlus5", "OnePlus 5", 0, 80, 5, 0),
model96("OnePlus5T", "OnePlus 5t", 0, 80, 5, 0),
model97("OnePlus6", "OnePlus 6", 0, 50, 20, 0),
model98("A0001", "OnePlus One", 0, 50, 50, 0),
model99("OnePlus6T", "OnePlus6T", 0, 50, 20, 0),
model100("OnePlus6TSingle", "OnePlus6T", 0, 50, 20, 0),
model101("OnePlus7Pro", "OnePlus 7 Pro", 0, 30, 25, 0),
model102("OnePlus7ProTMO", "OnePlus 7 Pro", 0, 30, 25, 0),
model103("a5x", "Samsung Galaxy A5 (2016)", 1, 50, 45, 0),
model104("a5y17lte", "Samsung Galaxy A5 (2017)", 1, 65, 45, 0),
model105("a7y17lte", "Samsung Galaxy A7 (2017)", 1, 60, 40, 0),
model106("a7y18lte", "Samsung Galaxy A7 (2018)", 0, 40, 20, 0),
model107("a7y18lteks", "Samsung Galaxy A7 (2018)", 0, 40, 20, 0),
model108("jackpotlte", "Samsung Galaxy A8 (2018)", 1, 50, 50, 0),
model109("a8", "Samsung Galaxy A8 / A8 (2016)", 0, 50, 15, 0),
model110("jackpot2lte", "Samsung Galaxy A8+", 1, 50, 45, 0),
model111("a8sqlte", "Samsung Galaxy A8s", 0, 50, 25, 0),
model112("a8sqltechn", "Samsung Galaxy A8s", 0, 50, 25, 0),
model113("a9xltechn", "Samsung Galaxy A9 (2016)", 1, 50, 45, 0),
model114("a9y18qltekx", "Samsung Galaxy A9 (2018)", 0, 40, 25, 0),
model115("a9y18qlte", "Samsung Galaxy A9 (2018)", 0, 40, 25, 0),
model116("c5ltechn", "Samsung Galaxy C5", 0, 45, 15, 0),
model117("c5pltechn", "Samsung Galaxy C5", 0, 45, 15, 0),
model118("c5proltechn", "Samsung Galaxy C5 Pro", 1, 45, 15, 0),
model119("c7ltechn", "Samsung Galaxy C7", 0, 50, 15, 0),
model120("c7prolte", "Samsung Galaxy C7 Pro", 1, 50, 15, 0),
model121("c9lte", "Samsung Galaxy C9 Pro", 0, 45, 15, 0),
model122("j5x", "Samsung Galaxy J5 (2016)", 1, 40, 50, 0),
model123("j5y17lte", "Samsung Galaxy J5 (2017) / J5 Pro", 1, 60, 50, 0),
model124("j6lte", "Samsung Galaxy J6", 1, 60, 50, 0),
model125("j7y17lte", "Samsung Galaxy J7 Pro", 1, 40, 50, 0),
model126("noblelte", "Samsung Galaxy Note5", 1, 50, 60, 0),
model127("SCV37", "Samsung Galaxy Note8", 1, 50, 45, 0),
model128("SC-01K", "Samsung Galaxy Note8", 1, 50, 45, 0),
model129("great", "Samsung Galaxy Note8", 1, 50, 45, 0),
model130("crownqle", "Samsung Galaxy Note9", 1, 50, 50, 0),
model131("crownlte", "Samsung Galaxy Note9", 1, 50, 50, 0),
model132("SC-01L", "Samsung Galaxy Note9", 1, 50, 50, 0),
model133("SCV40", "Samsung Galaxy Note9", 1, 50, 50, 0),
model134("SC-05G", "Samsung Galaxy S6", 1, 30, 60, 0),
model135("zeroflte", "Samsung Galaxy S6", 1, 30, 60, 0),
model136("SCV31", "Samsung Galaxy S6 edge", 1, 30, 60, 0),
model137("404SC", "Samsung Galaxy S6 edge", 1, 30, 60, 0),
model138("SC-04G", "Samsung Galaxy S6 edge", 1, 30, 60, 0),
model139("zerolte", "Samsung Galaxy S6 edge", 1, 30, 60, 0),
model140("zenlte", "Samsung Galaxy S6 edge+", 1, 50, 55, 0),
model141("heroqlte", "Samsung Galaxy S7", 1, 50, 45, 0),
model142("herolte", "Samsung Galaxy S7", 1, 50, 45, 0),
model143("poseidonlteatt", "Samsung Galaxy S7 Active", 1, 45, 55, 0),
model144("SCV33", "Samsung Galaxy S7 Edge", 1, 50, 45, 0),
model145("SC-02H", "Samsung Galaxy S7 Edge", 1, 50, 45, 0),
model146("hero2", "Samsung Galaxy S7 Edge", 1, 50, 45, 0),
model147("SCV36", "Samsung Galaxy S8", 0, 50, 55, 0),
model148("SC-02J", "Samsung Galaxy S8", 0, 50, 55, 0),
model149("dreamlte", "Samsung Galaxy S8", 0, 50, 55, 0),
model150("dreamqlte", "Samsung Galaxy S8", 0, 50, 55, 0),
model151("cruiserlte", "Samsung Galaxy S8 Active", 0, 50, 50, 0),
model152("SCV35", "Samsung Galaxy S8+", 0, 50, 45, 0),
model153("SC-03J", "Samsung Galaxy S8+", 0, 50, 45, 0),
model154("dream2", "Samsung Galaxy S8+", 0, 50, 45, 0),
model155("SCV38", "Samsung Galaxy S9", 0, 50, 50, 0),
model156("SC-02K", "Samsung Galaxy S9", 0, 50, 50, 0),
model157("starlte", "Samsung Galaxy S9", 0, 50, 50, 0),
model158("starqlte", "Samsung Galaxy S9", 0, 50, 50, 0),
model159("SCV39", "Samsung Galaxy S9+", 0, 50, 60, 0),
model160("SC-03K", "Samsung Galaxy S9+", 0, 50, 60, 0),
model161("star2", "Samsung Galaxy S9+", 0, 50, 60, 0),
model162("beyond1q", "Samsung Galaxy S10", 0, 50, 50, 0),
model163("beyond1", "Samsung Galaxy S10", 0, 50, 50, 0),
model164("beyond2q", "Samsung Galaxy S10+", 0, 50, 50, 0),
model165("beyond2", "Samsung Galaxy S10+", 0, 50, 50, 0),
model166("beyond0q", "Samsung Galaxy S10e", 0, 50, 50, 0),
model167("beyond0", "Samsung Galaxy S10e", 0, 50, 50, 0),
model168("F331", "Sony Xperia E5", 0, 50, 25, 0),
model169("G33", "Sony Xperia L1", 0, 50, 30, 0),
model170("H33", "Sony Xperia L2", 0, 50, 20, 0),
model171("H43", "Sony Xperia L2", 0, 50, 20, 0),
model172("F512", "Sony Xperia X", 0, 10, 5, 1),
model173("suzu", "Sony Xperia X", 0, 10, 5, 1),
model174("F5321", "Sony Xperia X Compact", 0, 50, 55, 0),
model175("SO-02J", "Sony Xperia X Compact", 0, 50, 55, 0),
model176("F813", "Sony Xperia X Performance", 0, 10, 5, 1),
model177("SOV33", "Sony Xperia X Performance", 0, 10, 5, 1),
model178("SO-04H", "Sony Xperia X Performance", 0, 10, 5, 1),
model179("502SO", "Sony Xperia X Performance", 0, 10, 5, 1),
model180("F311", "Sony Xperia XA", 0, 50, 20, 0),
model181("F321", "Sony Xperia XA Ultra", 0, 50, 25, 0),
model182("G31", "Sony Xperia XA1", 0, 50, 20, 0),
model183("G34", "Sony Xperia XA1 Plus", 0, 50, 25, 0),
model184("G32", "Sony Xperia XA1 Ultra", 0, 50, 20, 0),
model185("H31", "Sony Xperia XA2", 0, 50, 15, 0),
model186("H41", "Sony Xperia XA2", 0, 50, 15, 0),
model187("H4493", "Sony Xperia XA2 Plus", 0, 50, 20, 0),
model188("H4413", "Sony Xperia XA2 Plus", 0, 50, 20, 0),
model189("H3413", "Sony Xperia XA2 Plus", 0, 50, 20, 0),
model190("H32", "Sony Xperia XA2 Ultra", 0, 50, 20, 0),
model191("H42", "Sony Xperia XA2 Ultra", 0, 50, 20, 0),
model192("F833", "Sony Xperia XZ", 0, 20, 5, 1),
model193("SO-01J", "Sony Xperia XZ", 0, 20, 5, 1),
model194("601SO", "Sony Xperia XZ", 0, 20, 5, 1),
model195("SOV34", "Sony Xperia XZ", 0, 20, 5, 1),
model196("G81", "Sony Xperia XZ Premium", 0, 50, 55, 0),
model197("SO-04J", "Sony Xperia XZ Premium", 0, 50, 55, 0),
model198("G834", "Sony Xperia XZ1", 0, 45, 5, 0),
model199("701SO", "Sony Xperia XZ1", 0, 45, 5, 0),
model200("SOV36", "Sony Xperia XZ1", 0, 45, 5, 0),
model201("SO-01K", "Sony Xperia XZ1", 0, 45, 5, 0),
model202("G8441", "Sony Xperia XZ1 Compact", 0, 50, 30, 0),
model203("SO-02K", "Sony Xperia XZ1 Compact", 0, 50, 30, 0),
model204("H82", "Sony Xperia XZ2", 0, 20, 40, 0),
model205("SOV37", "Sony Xperia XZ2", 0, 20, 40, 0),
model206("SO-03K", "Sony Xperia XZ2", 0, 20, 40, 0),
model207("702SO", "Sony Xperia XZ2", 0, 20, 40, 0),
model208("H8166", "Sony Xperia XZ2 Premium", 0, 60, 20, 0),
model209("SO-04K", "Sony Xperia XZ2 Premium", 0, 60, 20, 0),
model210("SOV38", "Sony Xperia XZ2 Premium", 0, 60, 20, 0),
model211("H8116", "Sony Xperia XZ2 Premium", 0, 60, 20, 0),
model212("H8324", "Sony Xperia XZ2 Compact", 0, 50, 25, 0),
model213("SO-05K", "Sony Xperia XZ2 Compact", 0, 50, 25, 0),
model214("H8314", "Sony Xperia XZ2 Compact", 0, 50, 25, 0),
model215("G823", "Sony Xperia XZs", 0, 20, 5, 1),
model216("602SO", "Sony Xperia XZs", 0, 20, 5, 1),
model217("SOV35", "Sony Xperia XZs", 0, 20, 5, 1),
model218("SO-03J", "Sony Xperia XZs", 0, 20, 5, 1),
model219("801SO", "Sony Xperia XZ3", 0, 60, 20, 0),
model220("H9493", "Sony Xperia XZ3", 0, 60, 20, 0),
model221("H9436", "Sony Xperia XZ3", 0, 60, 20, 0),
model222("SOV39", "Sony Xperia XZ3", 0, 60, 20, 0),
model223("SO-01L", "Sony Xperia XZ3", 0, 60, 20, 0),
model224("H8416", "Sony Xperia XZ3", 0, 60, 20, 0),
model225("gemini", "Xiaomi Mi 5", 0, 40, 20, 0),
model226("capricorn", "Xiaomi Mi 5S", 0, 50, 10, 0),
model227("natrium", "Xiaomi Mi 5S Plus", 0, 45, 15, 0),
model228("sagit", "Xiaomi Mi 6", 0, 50, 20, 0),
model229("dipper", "Xiaomi Mi 8", 0, 45, 20, 0),
model230("ursa", "Xiaomi MI 8 Explorer Edition", 0, 50, 40, 0),
model231("cepheus", "Xiaomi MI 9", 0, 40, 20, 0),
model232("grus", "Xiaomi MI 9 SE", 0, 40, 20, 0),
model233("lithium", "Xiaomi Mi MIX", 0, 20, 20, 0),
model234("chiron", "Xiaomi Mi MIX 2", 0, 45, 20, 0),
model235("polaris", "Xiaomi Mi MIX 2S", 0, 45, 20, 0),
model236("scorpio", "Xiaomi Mi Note 2", 0, 50, 20, 0),
model237("jason", "Xiaomi Mi Note 3", 0, 50, 20, 0),
model238("perseus", "Xiaomi MIX 3", 0, 60, 20, 0),
model239("bbb100", "BlackBerry KEYone", 0, 60, 20, 0),
model240("bbf100", "BlackBerry KEY2", 0, 50, 30, 0),
model241("CatS61", "Cat S61", 0, 50, 50, 0),
}

View file

@ -0,0 +1,141 @@
package com.tangem.card_android.android.reader
import android.Manifest
import android.annotation.TargetApi
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.nfc.tech.IsoDep
import android.os.Build
import android.os.Bundle
import androidx.core.app.ActivityCompat
import androidx.fragment.app.FragmentActivity
import com.tangem.card_android.android.ui.NfcEnableDialog
import com.tangem.card_common.util.Log
import java.io.IOException
class NfcManager(private val activity: FragmentActivity, private val readerCallback: NfcAdapter.ReaderCallback) {
companion object {
val TAG: String = NfcManager::class.java.simpleName
// reader mode flags: listen for type A (not B), skipping ndef check
private const val READER_FLAGS = NfcAdapter.FLAG_READER_NFC_A or NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK or NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS
private const val DELAY_PRESENCE = 1500
private const val REQUEST_NFC_PERMISSIONS = 1
private val PERMISSIONS_NFC = arrayOf(Manifest.permission.NFC)
// checks if the app has NFC permission if the app does not has permission then the user will be prompted to grant permissions
fun verifyPermissions(activity: Activity) {
// check if we have write permission
val permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.NFC)
if (permission != PackageManager.PERMISSION_GRANTED) {
// we don't have permission so prompt the user
ActivityCompat.requestPermissions(activity, PERMISSIONS_NFC, REQUEST_NFC_PERMISSIONS)
}
}
}
private val nfcAdapter: NfcAdapter? = NfcAdapter.getDefaultAdapter(activity)
private var nfcEnableDialog: NfcEnableDialog? = null
private val broadcomWorkaround = false
internal var errorCount = 0
private val mBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action ?: return
if (action == NfcAdapter.ACTION_ADAPTER_STATE_CHANGED) {
val state = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, NfcAdapter.STATE_ON)
if (state == NfcAdapter.STATE_ON || state == NfcAdapter.STATE_TURNING_ON) {
Log.i(TAG, "state: $state , dialog: $nfcEnableDialog")
nfcEnableDialog?.dismiss()
if (state == NfcAdapter.STATE_ON)
enableReaderMode()
} else {
if (nfcEnableDialog == null || !nfcEnableDialog!!.isVisible)
showNFCEnableDialog()
}
}
}
}
fun onResume() {
// register broadcast receiver
val filter = IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED)
activity.registerReceiver(mBroadcastReceiver, filter)
if (nfcAdapter == null || !nfcAdapter.isEnabled)
showNFCEnableDialog()
else
enableReaderMode()
}
fun onPause() {
activity.unregisterReceiver(mBroadcastReceiver)
disableReaderMode()
}
fun onStop() {
nfcEnableDialog?.dismiss()
}
@Throws(IOException::class)
fun ignoreTag(tag: Tag) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
nfcAdapter?.ignore(tag, 1500, null, null);
} else {
}
val isoDep = IsoDep.get(tag)
isoDep?.close()
}
fun notifyReadResult(success: Boolean) {
// if (success) {
// errorCount = 0;
// } else {
// errorCount++;
// }
// if (errorCount >= 3) {
// disableReaderMode();
// nfcAdapter = null;
//// Toast.makeText(activity,"NFC restarted!",Toast.LENGTH_SHORT).show();
// activity.runOnUiThread(() -> {
// nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
// enableReaderMode();
// });
// }
}
private fun showNFCEnableDialog() {
nfcEnableDialog = NfcEnableDialog()
nfcEnableDialog?.show(activity.supportFragmentManager, NfcEnableDialog.TAG)
}
@TargetApi(Build.VERSION_CODES.KITKAT)
private fun enableReaderMode() {
val options = Bundle()
if (broadcomWorkaround) {
/* This is a work around for some Broadcom chipsets that does
* the presence check by sending commands that interrupt the
* processing of the ongoing command.
*/
options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, DELAY_PRESENCE)
}
nfcAdapter!!.enableReaderMode(activity, readerCallback, READER_FLAGS, options)
}
@TargetApi(Build.VERSION_CODES.KITKAT)
private fun disableReaderMode() {
nfcAdapter?.disableReaderMode(activity)
}
}

View file

@ -0,0 +1,41 @@
package com.tangem.card_android.android.reader
import android.nfc.tech.IsoDep
data class NfcReader(
val nfcManager: NfcManager,
val isoDep: IsoDep
) : com.tangem.card_common.reader.NfcReader {
override fun getId(): ByteArray {
return isoDep.tag.id
}
override fun setTimeout(timeout: Int) {
isoDep.timeout = timeout
}
override fun getTimeout(): Int {
return isoDep.timeout
}
override fun transceive(data: ByteArray?): ByteArray {
return isoDep.transceive(data)
}
override fun ignoreTag() {
nfcManager.ignoreTag(isoDep.tag)
}
override fun notifyReadResult(success: Boolean) {
nfcManager.notifyReadResult(success)
}
override fun connect() {
val timeout = isoDep.timeout
isoDep.connect()
isoDep.close()
isoDep.connect()
isoDep.timeout = timeout
}
}

View file

@ -0,0 +1,36 @@
package com.tangem.card_android.android.ui
import android.app.Dialog
import android.content.Intent
import android.os.Bundle
import android.provider.Settings
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import com.tangem.card_android.R
class NfcEnableDialog : DialogFragment() {
companion object {
val TAG: String = NfcEnableDialog::class.java.simpleName
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val builder = context?.let { AlertDialog.Builder(it) }
builder?.setCancelable(false)
?.setIcon(R.drawable.ic_action_nfc_gray)
?.setTitle(R.string.nfc_disabled)
?.setMessage(R.string.enable_nfc)
?.setPositiveButton(R.string.dialog_ok
) { _, _ ->
activity?.startActivity(Intent(Settings.ACTION_NFC_SETTINGS))
}
?.setNegativeButton(R.string.dialog_quit
) { dialog, _ ->
dialog.cancel()
activity?.finish()
}
return builder!!.create()
}
}

View file

@ -0,0 +1,159 @@
package com.tangem.card_android.data
import android.os.Bundle
import com.tangem.card_common.data.Manufacturer
import com.tangem.card_common.data.TangemCard
import com.tangem.card_common.util.Log
import java.util.*
const val EXTRA_TANGEM_CARD = "Card"
const val EXTRA_TANGEM_CARD_UID = "UID"
fun TangemCard.loadFromBundle(B: Bundle) {
uid = B.getString("UID")
cid = B.getByteArray("CID")
pin = B.getString("PIN")
PIN2 = TangemCard.PIN2_Mode.valueOf(B.getString("PIN2")!!)
status = TangemCard.Status.valueOf(B.getString("Status")!!)
blockchainID = B.getString("Blockchain")
tokensDecimal = B.getInt("TokensDecimal", 18)
tokenSymbol = B.getString("TokenSymbol", "")
contractAddress = B.getString("ContractAddress", "")
// if (B.containsKey("BlockchainName"))
// blockchainName = B.getString("BlockchainName", "");
if (B.containsKey("dtPersonalization")) {
personalizationDateTime = Date(B.getLong("dtPersonalization"))
}
remainingSignatures = B.getInt("RemainingSignatures")
maxSignatures = B.getInt("MaxSignatures")
health = B.getInt("health")
if (B.containsKey("settingsMask")) settingsMask = B.getInt("settingsMask")
pauseBeforePIN2 = B.getInt("pauseBeforePIN2")
if (B.containsKey("signingMethod"))
setSigningMethod(B.getInt("signingMethod"))
if (B.containsKey("Manufacturer"))
setManufacturer(Manufacturer.valueOf(B.getString("Manufacturer")!!), B.getBoolean("ManufacturerConfirmed", false))
if (B.containsKey("EncryptionMode"))
encryptionMode = TangemCard.EncryptionMode.valueOf(B.getString("EncryptionMode")!!)
else
encryptionMode = null
if (B.containsKey("SignedHashes")) signedHashes = B.getInt("SignedHashes")
if (B.containsKey("Issuer")) setIssuer(B.getString("Issuer"), B.getByteArray("IssuerPublicDataKey"))
if (B.containsKey("FirmwareVersion")) firmwareVersion = B.getString("FirmwareVersion")
if (B.containsKey("Batch")) batch = B.getString("Batch")
isCardPublicKeyValid = B.getBoolean("CardPublicKeyValid")
if (B.containsKey("CardPublicKey")) cardPublicKey = B.getByteArray("CardPublicKey")
if (B.containsKey("OfflineBalance")) offlineBalance = B.getByteArray("OfflineBalance")
else clearOfflineBalance()
if (B.containsKey("Denomination") && B.containsKey("DenominationText")) {
setDenomination(B.getByteArray("Denomination"), B.getString("DenominationText"))
} else if (B.containsKey("Denomination")) {
setDenomination(B.getByteArray("Denomination"), "N/A")
} else clearDenomination()
if (B.containsKey("IssuerData") && B.containsKey("IssuerDataSignature"))
setIssuerData(B.getByteArray("IssuerData"), B.getByteArray("IssuerDataSignature"))
else setIssuerData(null, null)
if (B.containsKey("NeedWriteIssuerData"))
needWriteIssuerData = B.getBoolean("NeedWriteIssuerData")
isWalletPublicKeyValid = B.getBoolean("WalletPublicKeyValid")
if (B.containsKey("PublicKey")) {
walletPublicKey = B.getByteArray("PublicKey")
}
if (B.containsKey("PublicKeyRar")) {
walletPublicKeyRar = B.getByteArray("PublicKeyRar")
}
if (B.containsKey("codeConfirmed"))
isCodeConfirmed = B.getBoolean("codeConfirmed")
if (B.containsKey("onlineVerified"))
isOnlineVerified = B.getBoolean("onlineVerified")
if (B.containsKey("onlineValidated"))
isOnlineValidated = B.getBoolean("onlineValidated")
}
val TangemCard.asBundle: Bundle
get() {
val bundle = Bundle()
saveToBundle(bundle)
return bundle
}
fun TangemCard.saveToBundle(B: Bundle) {
try {
B.putString("UID", uid)
B.putByteArray("CID", cid)
B.putString("PIN", pin)
B.putString("PIN2", PIN2.name)
B.putString("Status", status.name)
B.putString("Blockchain", blockchainID)
B.putInt("TokensDecimal", tokensDecimal)
B.putString("TokenSymbol", tokenSymbol)
B.putString("ContractAddress", contractAddress)
if (personalizationDateTime != null) B.putLong("dtPersonalization", personalizationDateTime.time)
B.putInt("RemainingSignatures", remainingSignatures)
B.putInt("MaxSignatures", maxSignatures)
B.putInt("Health", health)
if (settingsMask != null) B.putInt("settingsMask", settingsMask)
B.putInt("pauseBeforePIN2", pauseBeforePIN2)
if( allowedSigningMethod!=null ){
var iSigningMethod=0x80
for(sM in allowedSigningMethod)
{
iSigningMethod=iSigningMethod.or(0x01.shl(sM.ID))
}
B.putInt("signingMethod", iSigningMethod)
}
if (manufacturer != null) B.putString("Manufacturer", manufacturer.name)
if (encryptionMode != null) B.putString("EncryptionMode", encryptionMode.name)
if (issuer != null) B.putString("Issuer", issuer.getID())
if (issuerPublicDataKey != null) B.putByteArray("IssuerPublicDataKey", issuerPublicDataKey)
if (firmwareVersion != null) B.putString("FirmwareVersion", firmwareVersion)
if (batch != null) B.putString("Batch", batch)
B.putBoolean("ManufacturerConfirmed", isManufacturerConfirmed)
B.putBoolean("CardPublicKeyValid", isCardPublicKeyValid)
B.putByteArray("CardPublicKey", cardPublicKey)
B.putInt("SignedHashes", signedHashes)
B.putBoolean("WalletPublicKeyValid", isWalletPublicKeyValid)
if (walletPublicKey != null)
B.putByteArray("PublicKey", walletPublicKey)
if (walletPublicKeyRar != null)
B.putByteArray("PublicKeyRar", walletPublicKeyRar)
if (offlineBalance != null) B.putByteArray("OfflineBalance", offlineBalance)
if (denomination != null) B.putByteArray("Denomination", denomination)
if (denominationText != null) B.putString("DenominationText", denominationText)
if (issuerData != null && issuerDataSignature != null) {
B.putByteArray("IssuerData", issuerData)
B.putByteArray("IssuerDataSignature", issuerDataSignature)
B.putBoolean("NeedWriteIssuerData", needWriteIssuerData)
}
if (isCodeConfirmed != null)
B.putBoolean("codeConfirmed", isCodeConfirmed)
if (isOnlineVerified != null)
B.putBoolean("onlineVerified", isOnlineVerified)
if (isOnlineValidated != null)
B.putBoolean("onlineValidated", isOnlineValidated)
} catch (e: Exception) {
Log.e("Can't save to bundle ", e.message)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 536 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -0,0 +1,9 @@
<resources>
<string name="app_name">tangemcard</string>
<string name="dialog_ok">OK</string>
<string name="dialog_quit">Quit</string>
<string name="nfc_disabled">Enable NFC?</string>
<string name="enable_nfc">This app is useless when NFC is disabled. Reader mode depends on it. Hit OK to go to Settings, where you can enable NFC.</string>
</resources>

View file

@ -0,0 +1,17 @@
package com.tangem.card_android;
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);
}
}