├── .circleci └── config.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── blockstack-sdk ├── .gitignore ├── build.gradle ├── module.md ├── proguard-rules.pro └── src │ ├── androidTest │ ├── AndroidManifest.xml │ ├── java │ │ └── org │ │ │ └── blockstack │ │ │ └── android │ │ │ └── sdk │ │ │ ├── AppLinkVerifierTest.kt │ │ │ ├── BlockstackSessionAuthProfileTest.kt │ │ │ ├── BlockstackSessionCompanionTest.kt │ │ │ ├── BlockstackSessionStorageOfflineTest.kt │ │ │ ├── BlockstackSessionStorageTest.kt │ │ │ ├── BlockstackSessionTokenTest.kt │ │ │ ├── BlockstackSignInTest.kt │ │ │ ├── BlockstackTest.kt │ │ │ ├── EncryptionColendiKotlinTest.kt │ │ │ ├── EncryptionColendiLibTest.kt │ │ │ ├── HubTest.kt │ │ │ ├── NetworkTest.kt │ │ │ ├── ZoneFileTest.kt │ │ │ └── test │ │ │ └── TestActivity.kt │ └── res │ │ ├── drawable │ │ └── blockstackteam.jpg │ │ └── layout │ │ └── activity_test.xml │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ ├── com │ │ │ └── colendi │ │ │ │ └── ecies │ │ │ │ ├── EncryptedResult.java │ │ │ │ ├── EncryptedResultForm.java │ │ │ │ ├── Encryption.java │ │ │ │ └── MacAesPair.java │ │ └── org │ │ │ └── blockstack │ │ │ └── android │ │ │ └── sdk │ │ │ ├── AppLinkVerifier.kt │ │ │ ├── Blockstack.kt │ │ │ ├── BlockstackConnect.kt │ │ │ ├── BlockstackSession.kt │ │ │ ├── BlockstackSignIn.kt │ │ │ ├── DIDs.kt │ │ │ ├── Defaults.kt │ │ │ ├── Errors.kt │ │ │ ├── Network.kt │ │ │ ├── Result.kt │ │ │ ├── Scopes.kt │ │ │ ├── SessionStore.kt │ │ │ ├── SigningAlgorithm.kt │ │ │ ├── ZoneFile.kt │ │ │ ├── ecies │ │ │ ├── EncryptionColendi.kt │ │ │ └── Signature.kt │ │ │ ├── extensions │ │ │ ├── Addresses.kt │ │ │ └── Crockford32.kt │ │ │ ├── model │ │ │ ├── BlockstackAccount.kt │ │ │ ├── BlockstackConfig.kt │ │ │ ├── BlockstackIdentity.kt │ │ │ ├── CipherObject.kt │ │ │ ├── CryptoOptions.kt │ │ │ ├── DeleteFileOptions.kt │ │ │ ├── Entity.kt │ │ │ ├── GaiaHubConfig.kt │ │ │ ├── GetFileOptions.kt │ │ │ ├── Hub.kt │ │ │ ├── Profile.kt │ │ │ ├── ProfileToken.kt │ │ │ ├── ProfileTokenPair.kt │ │ │ ├── Proof.kt │ │ │ ├── PutFileOptions.kt │ │ │ ├── SessionData.kt │ │ │ ├── SignatureObject.kt │ │ │ ├── UserData.kt │ │ │ ├── ZoneFile.kt │ │ │ └── network │ │ │ │ ├── AccountStatus.kt │ │ │ │ ├── Denomination.kt │ │ │ │ ├── NameInfo.kt │ │ │ │ └── NamespaceInfo.kt │ │ │ └── ui │ │ │ ├── BlockstackConnectActivity.kt │ │ │ ├── BlockstackSignInButton.kt │ │ │ └── ConnectHowItWorksActivity.kt │ └── res │ │ ├── color │ │ ├── org_blockstack_text.xml │ │ └── org_blockstack_tint.xml │ │ ├── drawable-hdpi │ │ └── ic_arrow_back.png │ │ ├── drawable-mdpi │ │ └── ic_arrow_back.png │ │ ├── drawable-xhdpi │ │ └── ic_arrow_back.png │ │ ├── drawable-xxhdpi │ │ └── ic_arrow_back.png │ │ ├── drawable-xxxhdpi │ │ └── ic_arrow_back.png │ │ ├── drawable │ │ ├── ic_back.xml │ │ ├── ic_close.xml │ │ ├── ic_link.xml │ │ ├── ic_lock.xml │ │ ├── ic_secret_key.xml │ │ ├── ic_track.xml │ │ ├── org_blockstack_black_logo.xml │ │ └── org_blockstack_logo.xml │ │ ├── font │ │ ├── inter.xml │ │ ├── inter_bold.ttf │ │ ├── inter_medium.ttf │ │ └── inter_regular.ttf │ │ ├── layout │ │ ├── activity_connect.xml │ │ └── activity_connect_how_it_works.xml │ │ ├── values-de │ │ └── strings.xml │ │ ├── values-fr │ │ └── strings.xml │ │ ├── values-zh │ │ └── strings.xml │ │ └── values │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ ├── com │ └── colendi │ │ └── ecies │ │ └── EncryptionApplicationTests.java │ └── org │ └── blockstack │ └── android │ └── sdk │ ├── Crockford32Test.kt │ ├── ScopesUnitTest.kt │ ├── SignatureTest.kt │ └── extensions │ └── AddressesTest.kt ├── blockstack-sdktest ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ ├── AndroidManifest.xml │ ├── java │ │ └── org │ │ │ └── blockstack │ │ │ └── android │ │ │ └── sdktest │ │ │ ├── BlockstackSession2AuthTest.kt │ │ │ ├── BlockstackSession2EncryptionTest.kt │ │ │ ├── BlockstackSession2StorageTest.kt │ │ │ ├── BlockstackSignInWithBrowserTest.kt │ │ │ ├── j2v8 │ │ │ ├── BlockstackSessionJ2V8.kt │ │ │ ├── Console.kt │ │ │ └── Network.kt │ │ │ └── test │ │ │ └── TestActivity.kt │ └── res │ │ └── layout │ │ └── activity_test.xml │ └── main │ ├── AndroidManifest.xml │ └── res │ ├── raw │ ├── org_blockstack_base64.js │ ├── org_blockstack_blockstack.js │ ├── org_blockstack_blockstack_android.js │ ├── org_blockstack_globals.js │ ├── org_blockstack_zone_file.js │ └── webview.html │ └── values │ └── strings.xml ├── build.gradle ├── config └── debug.keystore ├── docs ├── images │ ├── app-flow.png │ ├── blockstack-install.png │ ├── blockstack-signin.png │ ├── chrome-prompt.png │ ├── configure-activity.png │ ├── create-restore.png │ ├── final-app.png │ ├── hello-andriod-1.png │ ├── initial-build.png │ ├── new-interface.png │ ├── oreo-api.png │ ├── running-app.png │ ├── select-hdw.png │ ├── studio-download.png │ ├── sync-project.png │ └── sync-success.png └── tutorial.md ├── example-multi-activities ├── .gitignore ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── org │ │ └── blockstack │ │ └── android │ │ └── ExampleInstrumentedTest.kt │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── org │ │ └── blockstack │ │ └── android │ │ ├── AccountActivity.kt │ │ ├── CipherActivity.kt │ │ ├── DefaultConfig.kt │ │ └── MainActivity.kt │ └── res │ ├── drawable-mdpi │ └── default_avatar.png │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ ├── activity_account.xml │ ├── activity_cipher.xml │ ├── activity_main.xml │ ├── content_account.xml │ ├── content_cipher.xml │ └── content_main.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── example-service ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── org │ │ └── blockstack │ │ └── android │ │ └── ExampleInstrumentedTest.kt │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── org │ │ └── blockstack │ │ └── android │ │ ├── BlockstackService.kt │ │ ├── Config.kt │ │ └── MainActivity.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ ├── activity_main.xml │ └── content_main.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ └── ic_launcher_round.png │ └── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml ├── example ├── .gitignore ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── org │ │ └── blockstack │ │ └── android │ │ └── MainActivityTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── org │ │ │ └── blockstack │ │ │ └── android │ │ │ └── MainActivity.kt │ └── res │ │ ├── drawable-mdpi │ │ ├── blockstackteam.jpg │ │ └── default_avatar.png │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ └── content_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── org │ └── blockstack │ └── android │ └── ExampleUnitTest.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── api-15.png ├── blockstack-id.png ├── create-blockstack-id.png ├── hello-andriod-0.png ├── helloandroid.zip ├── import-android.png ├── select-api-27.png ├── select-device.png └── studio-app.png ├── lib ├── .gitignore ├── build.gradle └── src │ ├── main │ └── java │ │ └── com │ │ └── colendi │ │ └── ecies │ │ ├── EncryptedResult.java │ │ ├── EncryptedResultForm.java │ │ ├── Encryption.java │ │ └── MacAesPair.java │ └── test │ └── java │ └── com │ └── colendi │ └── ecies │ └── EncryptionApplicationTests.java ├── settings.gradle └── tools └── blockstack-android-web-app ├── .editorconfig ├── .gitignore ├── firebase.json ├── package-lock.json ├── package.json ├── public ├── .well-known │ └── assetlinks.json ├── _headers ├── app.css ├── app.js ├── bootstrap.min.css ├── bundle.js ├── icon-192x192.png ├── index.html ├── manifest.json ├── redirect.html └── robots.txt ├── requires.js └── server.js /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | jobs: 4 | test: 5 | docker: 6 | - image: circleci/android:api-28 7 | environment: 8 | JVM_OPTS: -Xmx3200m 9 | steps: 10 | - checkout 11 | - restore_cache: 12 | key: jars-{{ checksum "build.gradle" }}-{{ checksum "blockstack-sdk/build.gradle" }}-{{ checksum "example/build.gradle" }}-{{ checksum "example-multi-activities/build.gradle" }} 13 | # - run: 14 | # name: Chmod permissions #if permission for Gradlew Dependencies fail, use this. 15 | # command: sudo chmod +x ./gradlew 16 | - run: 17 | name: Download Dependencies 18 | command: ./gradlew androidDependencies 19 | - save_cache: 20 | paths: 21 | - ~/.gradle 22 | key: jars-{{ checksum "build.gradle" }}-{{ checksum "blockstack-sdk/build.gradle" }}-{{ checksum "example/build.gradle" }}-{{ checksum "example-multi-activities/build.gradle" }} 23 | - run: 24 | name: Run Tests 25 | command: ./gradlew lint test assDebug assAndroidTest 26 | - run: 27 | name: Export google services key 28 | command: echo 'export $GCLOUD_SERVICE_KEY="$GCLOUD_SERVICE_KEY"' >> $BASH_ENV 29 | - run: 30 | name: Decode google services key 31 | command: echo $GCLOUD_SERVICE_KEY | base64 -di > ${HOME}/client-secret.json 32 | - run: 33 | name: Set Google Cloud target project 34 | command: gcloud config set project blockstack-android 35 | - run: 36 | name: Authenticate with Google Cloud 37 | command: gcloud auth activate-service-account --key-file ${HOME}/client-secret.json 38 | - run: 39 | name: Run instrumented test on Firebase Test Lab 40 | command: gcloud firebase test android run --type instrumentation --app example/build/outputs/apk/debug/example-debug.apk --test blockstack-sdk/build/outputs/apk/androidTest/debug/blockstack-sdk-debug-androidTest.apk --timeout 5m 41 | - store_artifacts: 42 | path: blockstack-sdk/build/reports 43 | destination: reports 44 | - store_test_results: 45 | path: blockstack-sdk/build/test-results 46 | 47 | docs-deploy: 48 | docker: 49 | - image: circleci/android:api-28 50 | environment: 51 | JVM_OPTS: -Xmx3200m 52 | steps: 53 | - checkout 54 | - restore_cache: 55 | key: jars-{{ checksum "build.gradle" }}-{{ checksum "blockstack-sdk/build.gradle" }}-{{ checksum "example/build.gradle" }}-{{ checksum "example-multi-activities/build.gradle" }} 56 | # - run: 57 | # name: Chmod permissions #if permission for Gradlew Dependencies fail, use this. 58 | # command: sudo chmod +x ./gradlew 59 | - run: 60 | name: Download Dependencies 61 | command: ./gradlew androidDependencies 62 | - save_cache: 63 | paths: 64 | - ~/.gradle 65 | key: jars-{{ checksum "build.gradle" }}-{{ checksum "blockstack-sdk/build.gradle" }}-{{ checksum "example/build.gradle" }}-{{ checksum "example-multi-activities/build.gradle" }} 66 | - run: 67 | name: Generate Documentation 68 | command: ./gradlew dokka 69 | - run: 70 | name: Disable jekyll builds 71 | command: touch blockstack-sdk/build/javadoc/blockstack-sdk/.nojekyll 72 | - run: 73 | name: Configure git 74 | command: | 75 | git config user.email "ci-build@blockstack.org" 76 | git config user.name "ci-build" 77 | - add_ssh_keys: 78 | fingerprints: 79 | - "c4:c7:e7:d1:0a:12:d9:66:ed:57:5b:40:14:db:e8:78" 80 | - run: 81 | name: Publish Documentation 82 | command: ./gradlew gitPublishPush 83 | 84 | workflows: 85 | version: 2 86 | build: 87 | jobs: 88 | - test 89 | - docs-deploy: 90 | requires: 91 | - test 92 | filters: 93 | branches: 94 | only: master -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/ 38 | 39 | # Keystore files 40 | *.jks 41 | 42 | # External native build folder generated in Android Studio 2.2 and later 43 | .externalNativeBuild 44 | 45 | # Google Services (e.g. APIs or Firebase) 46 | google-services.json 47 | 48 | # Freeline 49 | freeline.py 50 | freeline/ 51 | freeline_project_description.json 52 | 53 | .idea/caches 54 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to become a contributor and submit your own code 2 | 3 | ## Contributor License Agreements 4 | 5 | We'd love to accept your patches! Before we can take them, we 6 | have to jump a couple of legal hurdles. 7 | 8 | Please sign the [Contributor License Agreement(CLA)](https://cla-assistant.io/blockstack/blockstack-android) 9 | 10 | 11 | Follow the links above to access the CLA and sign with your github account. Once that is done, we'll be able to 12 | accept your pull requests. 13 | 14 | ## Contributing A Patch 15 | 16 | 1. Submit an issue describing your proposed change. 17 | 1. The repo owner will respond to your issue promptly. 18 | 1. If your proposed change is accepted, and you haven't already done so, sign a 19 | Contributor License Agreement (see details above). 20 | 1. Fork the desired repo, develop and test your code changes. 21 | 1. Ensure that your code adheres to the existing style in the repo to which 22 | you are contributing. 23 | 1. Ensure that your code has an appropriate set of unit and/or integration tests which all pass. 24 | 1. Submit a pull request. 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Blockstack Public Benefit Corporation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /blockstack-sdk/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /blockstack-sdk/module.md: -------------------------------------------------------------------------------- 1 | # Module blockstack-sdk 2 | 3 | The Blockstack Android SDK provides a simple way to integrate Blockstack's authentication, 4 | storage and cryptographic methods. 5 | 6 | # Package org.blockstack.android.sdk 7 | 8 | Contains all the code needed for interacting with blockstack APIs 9 | 10 | # Package org.blockstack.android.sdk.ui 11 | 12 | Contains a customized button for sign in 13 | -------------------------------------------------------------------------------- /blockstack-sdk/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /blockstack-sdk/src/androidTest/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /blockstack-sdk/src/androidTest/java/org/blockstack/android/sdk/AppLinkVerifierTest.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.rule.ActivityTestRule 5 | import org.blockstack.android.sdk.model.toBlockstackConfig 6 | import org.blockstack.android.sdk.test.TestActivity 7 | import org.hamcrest.MatcherAssert.assertThat 8 | import org.hamcrest.Matchers.`is` 9 | import org.hamcrest.Matchers.isEmptyOrNullString 10 | import org.junit.Before 11 | import org.junit.Rule 12 | import org.junit.Test 13 | import org.junit.runner.RunWith 14 | 15 | @RunWith(AndroidJUnit4::class) 16 | class AppLinkVerifierTest { 17 | 18 | @get:Rule 19 | val rule = ActivityTestRule(TestActivity::class.java) 20 | private lateinit var appLinkVerifier: AppLinkVerifier 21 | 22 | @Before 23 | fun setup() { 24 | appLinkVerifier = AppLinkVerifier(rule.activity, "https://flamboyant-darwin-d11c17.netlify.app".toBlockstackConfig(arrayOf())) 25 | } 26 | 27 | @Test 28 | fun testCallOnMainThread() { 29 | rule.runOnUiThread { 30 | val warning = appLinkVerifier.verify() 31 | assertThat(warning, `is`("Failed to check verified app links. android.os.NetworkOnMainThreadException")) 32 | } 33 | 34 | } 35 | 36 | @Test 37 | fun testValidFingerprints() { 38 | val betaAppLinkVerifier = AppLinkVerifier(rule.activity, "https://flamboyant-darwin-d11c17.netlify.app".toBlockstackConfig(arrayOf())) 39 | val warning = betaAppLinkVerifier.verify() 40 | assertThat(warning, isEmptyOrNullString()) 41 | } 42 | 43 | @Test 44 | fun testNotExistingDigitalAssetLinksFile() { 45 | val exampleDotComAppLinkVerifier = AppLinkVerifier(rule.activity, "https://example.com".toBlockstackConfig(arrayOf())) 46 | val warning = exampleDotComAppLinkVerifier.verify() 47 | assertThat(warning, `is`("Digital Asset Links file for https://example.com does not contain a fingerprint for this app org.blockstack.android.sdk.test.\nPlease verify https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://example.com&relation=delegate_permission/common.handle_all_urls")) 48 | } 49 | 50 | @Test 51 | fun testInvalidFingerprintsWithExistingDigitalAssetLinksFile() { 52 | val afariAppLinkVerifier = AppLinkVerifier(rule.activity, "https://app.afari.io".toBlockstackConfig(arrayOf())) 53 | val warning = afariAppLinkVerifier.verify() 54 | assertThat(warning, `is`("Digital Asset Links file for https://app.afari.io does not contain a fingerprint for this app org.blockstack.android.sdk.test.\nPlease verify https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://app.afari.io&relation=delegate_permission/common.handle_all_urls")) 55 | } 56 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/androidTest/java/org/blockstack/android/sdk/BlockstackSessionCompanionTest.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk; 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.rule.ActivityTestRule 5 | import org.blockstack.android.sdk.test.TestActivity 6 | import org.hamcrest.CoreMatchers.`is` 7 | import org.hamcrest.MatcherAssert.assertThat 8 | import org.hamcrest.Matchers.isEmptyOrNullString 9 | import org.junit.Rule 10 | import org.junit.Test 11 | import org.junit.runner.RunWith 12 | 13 | private val TOKEN = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJqdGkiOiJhMzU4MzdmNS1jOGNmLTRiYTMtYjk4ZS03OTY5YTUzZmVjNDgiLCJpYXQiOiIyMDE3LTAzLTA0VDE2OjEzOjA2Ljc5MFoiLCJleHAiOiIyMDE4LTAzLTA0VDE2OjEzOjA2Ljc5MFoiLCJzdWJqZWN0Ijp7InB1YmxpY0tleSI6IjAzZTdhNGQ3OTgzMzY5ZDMzZWQxMzAyMDg4NTk4NWQ2OGY4YjA1ZGVlNjE2OGY3NWY5ZDk3ZTFhMDcyY2RmY2RjNSJ9LCJpc3N1ZXIiOnsicHVibGljS2V5IjoiMDNlN2E0ZDc5ODMzNjlkMzNlZDEzMDIwODg1OTg1ZDY4ZjhiMDVkZWU2MTY4Zjc1ZjlkOTdlMWEwNzJjZGZjZGM1In0sImNsYWltIjp7IkBjb250ZXh0IjoiaHR0cDovL3NjaGVtYS5vcmcvIiwiQHR5cGUiOiJDcmVhdGl2ZVdvcmsiLCJuYW1lIjoiQmFsbG9vbiBEb2ciLCJjcmVhdG9yIjpbeyJAdHlwZSI6IlBlcnNvbiIsIkBpZCI6InRoZXJlYWxqZWZma29vbnMuaWQiLCJuYW1lIjoiSmVmZiBLb29ucyJ9XSwiZGF0ZUNyZWF0ZWQiOiIxOTk0LTA1LTA5VDAwOjAwOjAwLTA0MDAiLCJkYXRlUHVibGlzaGVkIjoiMjAxNS0xMi0xMFQxNDo0NDoyNi0wNTAwIn19.MF7ru91rk8IIKNEEqo9wjLHkvW3jSlcDJmeZZeOVSj9KlXApBp67q_3ke0-LzSO_YyYsUnGOplMYiNxY1XynAA" 14 | 15 | @RunWith(AndroidJUnit4::class) 16 | class BlockstackSessionCompanionTest { 17 | @get:Rule 18 | val rule = ActivityTestRule(TestActivity::class.java) 19 | 20 | @Test 21 | fun verifyAuthResponseReturnsNullForValidAuthResponse() { 22 | assertThat(Blockstack.verifyAuthResponse(TOKEN)?.message, isEmptyOrNullString()) 23 | } 24 | 25 | @Test 26 | fun verifyAuthResponseReturnsErrorForInValidAuthResponse() { 27 | assertThat(Blockstack.verifyAuthResponse("a.b.c")?.message, `is`("The authResponse parameter is an invalid base64 encoded token\nbad base-64\nAuth response: a.b.c")) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /blockstack-sdk/src/androidTest/java/org/blockstack/android/sdk/BlockstackSessionStorageOfflineTest.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.rule.ActivityTestRule 5 | import kotlinx.coroutines.runBlocking 6 | import okhttp3.Call 7 | import okhttp3.OkHttpClient 8 | import org.blockstack.android.sdk.model.GetFileOptions 9 | import org.blockstack.android.sdk.model.toBlockstackConfig 10 | import org.blockstack.android.sdk.test.TestActivity 11 | import org.hamcrest.CoreMatchers.* 12 | import org.hamcrest.MatcherAssert.assertThat 13 | import org.junit.Before 14 | import org.junit.Rule 15 | import org.junit.Test 16 | import org.junit.runner.RunWith 17 | import java.io.IOException 18 | 19 | 20 | @RunWith(AndroidJUnit4::class) 21 | class BlockstackSessionStorageOfflineTest { 22 | @get:Rule 23 | val rule = ActivityTestRule(TestActivity::class.java) 24 | 25 | private lateinit var session: BlockstackSession 26 | 27 | @Before 28 | fun setup() { 29 | val realCallFactory = OkHttpClient() 30 | val callFactory = Call.Factory { 31 | if (it.url.encodedPath.contains("/hub_info")) { 32 | realCallFactory.newCall(it) 33 | } else { 34 | throw IOException("offline") 35 | } 36 | } 37 | 38 | session = BlockstackSession( 39 | appConfig = "https://flamboyant-darwin-d11c17.netlify.app".toBlockstackConfig(emptyArray()), 40 | sessionStore = sessionStoreforIntegrationTests(rule), 41 | callFactory = callFactory, blockstack = Blockstack()) 42 | runBlocking { 43 | session.getOrSetLocalGaiaHubConnection() 44 | } 45 | } 46 | 47 | 48 | @Test 49 | fun testOfflineGetFile() { 50 | var result: Result? = null 51 | 52 | if (session.isUserSignedIn()) { 53 | runBlocking { 54 | result = session.getFile("404file.txt", GetFileOptions(false)) 55 | 56 | } 57 | } 58 | assertThat(result, `is`(notNullValue())) 59 | assertThat(result?.value, `is`(nullValue())) 60 | assertThat(result?.error?.message, `is`("offline")) 61 | } 62 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/androidTest/java/org/blockstack/android/sdk/BlockstackTest.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.rule.ActivityTestRule 5 | import kotlinx.coroutines.runBlocking 6 | import org.blockstack.android.sdk.model.CryptoOptions 7 | import org.blockstack.android.sdk.model.toBlockstackConfig 8 | import org.blockstack.android.sdk.test.TestActivity 9 | import org.hamcrest.CoreMatchers.startsWith 10 | import org.hamcrest.MatcherAssert.assertThat 11 | import org.hamcrest.Matchers.`is` 12 | import org.junit.Before 13 | import org.junit.Rule 14 | import org.junit.Test 15 | import org.junit.runner.RunWith 16 | import java.net.URI 17 | 18 | 19 | private val PRIVATE_KEY = "a5c61c6ca7b3e7e55edee68566aeab22e4da26baa285c7bd10e8d2218aa3b229" 20 | private val PUBLIC_KEY = "027d28f9951ce46538951e3697c62588a87f1f1f295de4a14fdd4c780fc52cfe69" 21 | private val ADDRESS = "1NZNxhoxobqwsNvTb16pdeiqvFvce3Yg8U" 22 | 23 | private val TAG = BlockstackTest::class.java.simpleName 24 | 25 | 26 | @RunWith(AndroidJUnit4::class) 27 | class BlockstackTest { 28 | 29 | 30 | @get:Rule 31 | val rule = ActivityTestRule(TestActivity::class.java) 32 | 33 | private lateinit var blockstack: Blockstack 34 | 35 | @Before 36 | fun setup() { 37 | blockstack = Blockstack() 38 | } 39 | 40 | @Test 41 | fun getPublicKeyFromPrivateReturnsCorrectKey() { 42 | assertThat(blockstack.getPublicKeyFromPrivate(PRIVATE_KEY), `is`(PUBLIC_KEY)) 43 | } 44 | 45 | @Test 46 | fun makeECPrivateKeyDoesNotThrow() { 47 | blockstack.makeECPrivateKey() 48 | // ok, no exception thrown 49 | } 50 | 51 | @Test 52 | fun publicKeyToAddressReturnsCorrectAddress() { 53 | assertThat(blockstack.publicKeyToAddress(PUBLIC_KEY), `is`(ADDRESS)) 54 | } 55 | 56 | @Test 57 | fun testLookupProfile() { 58 | 59 | val profile = runBlocking { 60 | blockstack.lookupProfile("public_profile_for_testing.id.blockstack", null) 61 | } 62 | // Note that this can fail due to updates on the profile (the secret key is publicly available 63 | assertThat(profile.json.toString(), startsWith("{\"@type\":\"Person\",\"@context\":\"http:\\/\\/schema.org\",\"apps\":{")) 64 | } 65 | 66 | 67 | @Test 68 | fun testEncryptThenDecrypt() { 69 | val message = "Hello Test" 70 | val result = blockstack.encryptContent(message, CryptoOptions(publicKey = PUBLIC_KEY)) 71 | val plainText = blockstack.decryptContent(result.value!!.json.toString(), false, CryptoOptions(privateKey = PRIVATE_KEY)) 72 | assertThat(plainText.value as String, `is`(message)) 73 | } 74 | 75 | @Test 76 | fun testOriginOrUri() { 77 | assertThat(URI("https://blockstack.org:443/wiki").getOrigin(), `is`("https://blockstack.org:443")) 78 | assertThat(URI("https://blockstack.org:443").getOrigin(), `is`("https://blockstack.org:443")) 79 | assertThat(URI("https://blockstack.org").getOrigin(), `is`("https://blockstack.org")) 80 | assertThat(URI("https://user@blockstack.org").getOrigin(), `is`("https://blockstack.org")) 81 | assertThat(URI("blockstack://blockstack.id").getOrigin(), `is`("blockstack://blockstack.id")) 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /blockstack-sdk/src/androidTest/java/org/blockstack/android/sdk/EncryptionColendiKotlinTest.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.rule.ActivityTestRule 5 | import org.blockstack.android.sdk.ecies.EncryptedResult 6 | import org.blockstack.android.sdk.ecies.EncryptionColendi 7 | import org.blockstack.android.sdk.test.TestActivity 8 | import org.junit.Ignore 9 | import org.junit.Rule 10 | import org.junit.Test 11 | import org.junit.runner.RunWith 12 | 13 | private val PRIVATE_KEY = "a5c61c6ca7b3e7e55edee68566aeab22e4da26baa285c7bd10e8d2218aa3b229" 14 | private val PUBLIC_KEY = "027d28f9951ce46538951e3697c62588a87f1f1f295de4a14fdd4c780fc52cfe69" 15 | 16 | @RunWith(AndroidJUnit4::class) 17 | class EncryptionColendiKotlinTest { 18 | 19 | @get:Rule 20 | val rule = ActivityTestRule(TestActivity::class.java) 21 | 22 | @Test 23 | @Ignore("Test not passing on 0.6.2, no changes made here in 0.6.3, marked as ignored until fixes are made") 24 | fun testEncryptDecryptWorks() { 25 | val encryption = EncryptionColendi() 26 | 27 | val message = "Colendi" 28 | 29 | 30 | val encryptedResult = encryption.encryptWithPublicKey(message, PUBLIC_KEY) 31 | 32 | val formData = EncryptedResult(encryptedResult.ephemPubString, 33 | encryptedResult.ivString, 34 | encryptedResult.macString, 35 | encryptedResult.encryptedText) 36 | 37 | val result = encryption.decryptWithPrivateKey(formData, PRIVATE_KEY) 38 | assert(result == message) 39 | } 40 | 41 | companion object { 42 | val TAG = EncryptionColendiKotlinTest.javaClass.simpleName 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /blockstack-sdk/src/androidTest/java/org/blockstack/android/sdk/HubTest.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.rule.ActivityTestRule 5 | import kotlinx.coroutines.runBlocking 6 | import okio.ByteString 7 | import okio.ByteString.Companion.encode 8 | import org.blockstack.android.sdk.model.CryptoOptions 9 | import org.blockstack.android.sdk.model.GaiaHubConfig 10 | import org.blockstack.android.sdk.model.Hub 11 | import org.blockstack.android.sdk.test.TestActivity 12 | import org.hamcrest.CoreMatchers.`is` 13 | import org.hamcrest.CoreMatchers.notNullValue 14 | import org.hamcrest.MatcherAssert.assertThat 15 | import org.junit.Before 16 | import org.junit.Rule 17 | import org.junit.Test 18 | import org.junit.runner.RunWith 19 | 20 | private val PRIVATE_KEY = "a5c61c6ca7b3e7e55edee68566aeab22e4da26baa285c7bd10e8d2218aa3b229" 21 | private val PUBLIC_KEY = "027d28f9951ce46538951e3697c62588a87f1f1f295de4a14fdd4c780fc52cfe69" 22 | 23 | @RunWith(AndroidJUnit4::class) 24 | class HubTest { 25 | 26 | 27 | @get:Rule 28 | val rule = ActivityTestRule(TestActivity::class.java) 29 | 30 | private lateinit var hub: Hub 31 | 32 | @Before 33 | fun setup() { 34 | hub = Hub() 35 | } 36 | 37 | @Test 38 | fun testGetFromGaiaHub() { 39 | 40 | val response = runBlocking { 41 | val hubConfig = hub.connectToGaia("https://hub.blockstack.org", PRIVATE_KEY, null) 42 | hub.uploadToGaiaHub("testGetFromGaiaHub.txt", "message".encode(Charsets.UTF_8), hubConfig) 43 | 44 | hub.getFromGaiaHub(hub.getFullReadUrl("testGetFromGaiaHub.txt", hubConfig)) 45 | } 46 | assertThat(response.body?.string(), `is`("message")) 47 | } 48 | 49 | } 50 | 51 | -------------------------------------------------------------------------------- /blockstack-sdk/src/androidTest/java/org/blockstack/android/sdk/ZoneFileTest.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk 2 | 3 | import androidx.test.ext.junit.runners.AndroidJUnit4 4 | import androidx.test.rule.ActivityTestRule 5 | import org.blockstack.android.sdk.test.TestActivity 6 | import org.hamcrest.CoreMatchers.`is` 7 | import org.hamcrest.MatcherAssert.assertThat 8 | import org.junit.Rule 9 | import org.junit.Test 10 | import org.junit.runner.RunWith 11 | 12 | 13 | private val ZONE_FILE = "\$ORIGIN public_profile_for_testing.id.blockstack\n\$TTL 3600\n_http._tcp\tIN\tURI\t10\t1\t\"https://gaia.blockstack.org/hub/1JeTQ5cQjsD57YGcsVFhwT7iuQUXJR6BSk/profile.json\"\n\n" 14 | 15 | 16 | @RunWith(AndroidJUnit4::class) 17 | class ZoneFileTest { 18 | 19 | 20 | @get:Rule 21 | val rule = ActivityTestRule(TestActivity::class.java) 22 | 23 | 24 | @Test 25 | fun testParseZoneFile() { 26 | val zoneFile = parseZoneFile(ZONE_FILE) 27 | assertThat(zoneFile.tokenFileUri, `is`("https://gaia.blockstack.org/hub/1JeTQ5cQjsD57YGcsVFhwT7iuQUXJR6BSk/profile.json")) 28 | } 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /blockstack-sdk/src/androidTest/java/org/blockstack/android/sdk/test/TestActivity.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.test 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | 6 | class TestActivity : AppCompatActivity() { 7 | 8 | override fun onCreate(savedInstanceState: Bundle?) { 9 | super.onCreate(savedInstanceState) 10 | setContentView(R.layout.activity_test) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /blockstack-sdk/src/androidTest/res/drawable/blockstackteam.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/blockstack-sdk/src/androidTest/res/drawable/blockstackteam.jpg -------------------------------------------------------------------------------- /blockstack-sdk/src/androidTest/res/layout/activity_test.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/com/colendi/ecies/EncryptedResult.java: -------------------------------------------------------------------------------- 1 | package com.colendi.ecies; 2 | 3 | public class EncryptedResult { 4 | 5 | public EncryptedResult() { } 6 | 7 | public EncryptedResult(String ephemPublicKey, String iv, String mac, String ciphertext) { 8 | this.ephemPublicKey = ephemPublicKey; 9 | this.iv = iv; 10 | this.mac = mac; 11 | this.ciphertext = ciphertext; 12 | } 13 | 14 | protected String ephemPublicKey; 15 | protected String iv; 16 | protected String mac; 17 | protected String ciphertext; 18 | 19 | public String getEphemPublicKey() { 20 | return ephemPublicKey; 21 | } 22 | 23 | public void setEphemPublicKey(String ephemPublicKey) { 24 | this.ephemPublicKey = ephemPublicKey; 25 | } 26 | 27 | public String getIv() { 28 | return iv; 29 | } 30 | 31 | public void setIv(String iv) { 32 | this.iv = iv; 33 | } 34 | 35 | public String getMac() { 36 | return mac; 37 | } 38 | 39 | public void setMac(String mac) { 40 | this.mac = mac; 41 | } 42 | 43 | public String getCiphertext() { 44 | return ciphertext; 45 | } 46 | 47 | public void setCiphertext(String ciphertext) { 48 | this.ciphertext = ciphertext; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/com/colendi/ecies/EncryptedResultForm.java: -------------------------------------------------------------------------------- 1 | package com.colendi.ecies; 2 | 3 | public class EncryptedResultForm extends EncryptedResult { 4 | 5 | public EncryptedResultForm() {} 6 | 7 | public EncryptedResultForm(String ephemPublicKey, String iv, String mac, String ciphertext, String privateKey) { 8 | super(ephemPublicKey, iv, mac, ciphertext); 9 | this.privateKey = privateKey; 10 | } 11 | 12 | private String privateKey; 13 | 14 | 15 | public String getPrivateKey() { 16 | return privateKey; 17 | } 18 | 19 | public void setPrivateKey(String privateKey) { 20 | this.privateKey = privateKey; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/com/colendi/ecies/MacAesPair.java: -------------------------------------------------------------------------------- 1 | package com.colendi.ecies; 2 | 3 | public class MacAesPair { 4 | String macKey; 5 | String encKeyAES; 6 | 7 | public MacAesPair(String macKey, String encKeyAES) { 8 | this.macKey = macKey; 9 | this.encKeyAES = encKeyAES; 10 | } 11 | 12 | public String getMacKey() { 13 | return macKey; 14 | } 15 | 16 | public void setMacKey(String macKey) { 17 | this.macKey = macKey; 18 | } 19 | 20 | public String getEncKeyAES() { 21 | return encKeyAES; 22 | } 23 | 24 | public void setEncKeyAES(String encKeyAES) { 25 | this.encKeyAES = encKeyAES; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/DIDs.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk 2 | 3 | import me.uport.sdk.universaldid.* 4 | import okhttp3.Call 5 | import okhttp3.Request 6 | import org.json.JSONObject 7 | import java.util.* 8 | 9 | class DIDs { 10 | 11 | 12 | companion object { 13 | fun getAddressFromDID(did: String?): String? { 14 | if (did == null) { 15 | return null 16 | } 17 | 18 | val didType = getDIDType(did) 19 | 20 | if (didType == "btc-addr") { 21 | return did.split(':')[2] 22 | } else { 23 | return null 24 | } 25 | } 26 | 27 | 28 | private fun getDIDType(decentralizedID: String): String { 29 | val didParts = decentralizedID.split(':') 30 | 31 | if (didParts.size != 3) { 32 | throw InvalidDIDError("Decentralized IDs must have 3 parts") 33 | } 34 | 35 | if (didParts[0].toLowerCase(Locale.US) != "did") { 36 | throw InvalidDIDError("Decentralized IDs must start with 'did'") 37 | } 38 | 39 | return didParts[1].toLowerCase(Locale.US) 40 | } 41 | } 42 | } 43 | 44 | class InvalidDIDError(msg: String) : Throwable(msg) 45 | 46 | class BitAddrResolver(private val okHttpClient: Call.Factory) : DIDResolver { 47 | override fun canResolve(potentialDID: String): Boolean { 48 | return DIDs.getAddressFromDID(potentialDID) != null 49 | } 50 | 51 | override suspend fun resolve(did: String): DIDDocument { 52 | 53 | val address = DIDs.getAddressFromDID(did) 54 | val publicKeyHex = getPublicKeyHex(address) 55 | ?: throw InvalidDIDError("no DID document available") 56 | 57 | return object : DIDDocument { 58 | override val authentication: List 59 | get() = listOf(AuthenticationEntry(PublicKeyType.Secp256k1SignatureAuthentication2018, publicKeyHex)) 60 | override val context: String? 61 | get() = null 62 | override val id: String? 63 | get() = did 64 | override val publicKey: List 65 | get() = listOf(PublicKeyEntry(did, PublicKeyType.Secp256k1VerificationKey2018, did, publicKeyHex = publicKeyHex)) 66 | override val service: List 67 | get() = emptyList() 68 | 69 | } 70 | } 71 | 72 | private fun getPublicKeyHex(address: String?): String? { 73 | if (publicKeyMap.contains(address)) { 74 | return publicKeyMap[address] 75 | } else { 76 | return null 77 | } 78 | } 79 | 80 | fun add(address: String?, publicKey: String?) { 81 | if (address != null) { 82 | publicKeyMap[address] = publicKey 83 | } 84 | } 85 | 86 | fun remove(address: String?) { 87 | if (address != null) { 88 | publicKeyMap.remove(address) 89 | } 90 | } 91 | 92 | private val publicKeyMap: MutableMap = mutableMapOf() 93 | override val method: String 94 | get() = "btc-addr" 95 | 96 | } 97 | 98 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/Defaults.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk 2 | 3 | const val BLOCKSTACK_DEFAULT_GAIA_HUB_URL = "https://hub.blockstack.org" 4 | const val DEFAULT_CORE_API_ENDPOINT = "https://core.blockstack.org" 5 | const val DEFAULT_BLOCKSTACK_ID_HOST = "https://app.blockstack.org" 6 | const val LEGACY_BLOCKSTACK_ID_HOST = "https://browser.blockstack.org/auth" 7 | const val VERSION = "1.3.1" 8 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/Result.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk 2 | 3 | /** 4 | * Object representing the result of a blockstack method call 5 | * 6 | * @property value the result of the method call 7 | * @property error the error with code and message 8 | */ 9 | class Result(val value: T?, val error: ResultError? = null) { 10 | 11 | /** 12 | * returns true if the method call returned a value successfully 13 | */ 14 | inline val hasValue: Boolean 15 | get() = value != null 16 | 17 | /** 18 | * returns true if the method did not return a value, but an error 19 | */ 20 | inline val hasErrors: Boolean 21 | get() = value == null && error != null 22 | 23 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/Scopes.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk 2 | 3 | data class Scope(val name: String) { 4 | companion object { 5 | /** 6 | * converts an array of scopes into a string usable by blockstack.js 7 | */ 8 | @JvmStatic 9 | fun scopesArrayToJSONString(scopes: Array): String { 10 | return scopes.joinToString(prefix = "[", transform = { "\"${it}\"" }, postfix = "]") 11 | } 12 | 13 | /** 14 | * converts an array of scopes into a string usable by blockstack.js 15 | */ 16 | @JvmStatic 17 | fun scopesArrayToJSONString(scopes: Array): String { 18 | return scopes.joinToString(prefix = "[", transform = { "\"${it.name}\"" }, postfix = "]") 19 | } 20 | 21 | /** 22 | * Creates `BaseScope` from its @property scope, i.e. the javascript name. 23 | * Throws IllegalArgumentException if scope name is not defined. 24 | ** 25 | * @param scopeJSName name of scope as defined in blockstack.js 26 | * 27 | */ 28 | @JvmStatic 29 | fun fromJSName(scopeJSName: String): BaseScope { 30 | for (baseScope in BaseScope.values()) { 31 | if (scopeJSName == baseScope.scope.name) { 32 | return baseScope 33 | } 34 | } 35 | throw IllegalArgumentException("scope '$scopeJSName' not defined, available scopes: ${BaseScope.values().joinToString()}") 36 | } 37 | } 38 | } 39 | 40 | /** 41 | * An enum of scopes supported in Blockstack Authentication. 42 | * 43 | * @property scope identifies the permission, same as in blockstack.js 44 | */ 45 | enum class BaseScope(val scope: Scope) { 46 | /** 47 | * Read and write data to the user's Gaia hub in an app-specific storage bucket. 48 | * 49 | * This is the default scope. 50 | */ 51 | StoreWrite(Scope("store_write")), 52 | 53 | /** 54 | * Publish data so that other users of the app can discover and interact with the user 55 | */ 56 | PublishData(Scope("publish_data")), 57 | 58 | /** 59 | * Requests the user's email if available 60 | */ 61 | Email(Scope("email")); 62 | 63 | /** 64 | * returns the scope as string 65 | */ 66 | override fun toString(): String { 67 | return scope.name 68 | } 69 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/SessionStore.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk 2 | 3 | import android.content.Context 4 | import android.content.SharedPreferences 5 | import android.util.Log 6 | import org.blockstack.android.sdk.model.SessionData 7 | import org.blockstack.android.sdk.model.UserData 8 | import org.json.JSONObject 9 | 10 | val BLOCKSTACK_SESSION = "blockstack_session" 11 | 12 | private val EMPTY_DATA = "{}" 13 | private val TAG = SessionStore::class.java.simpleName 14 | 15 | 16 | interface ISessionStore { 17 | var sessionData: SessionData 18 | fun deleteSessionData() 19 | fun updateUserData(userData: UserData) 20 | 21 | fun setTransitPrivateKey(transitPrivateKey: String) { 22 | sessionData = SessionData(this.sessionData.json.put("transitKey", transitPrivateKey)) 23 | } 24 | 25 | fun getTransitPrivateKey(): String { 26 | return sessionData.json.getString("transitKey") 27 | } 28 | } 29 | 30 | 31 | class SessionStore(private val prefs: SharedPreferences) : ISessionStore { 32 | private var sessionDataObject = SessionData(JSONObject(prefs.getString(BLOCKSTACK_SESSION, EMPTY_DATA))) 33 | override var sessionData: SessionData 34 | get() = sessionDataObject 35 | set(value) { 36 | sessionDataObject = value 37 | prefs.edit().putString(BLOCKSTACK_SESSION, value.json.toString()).apply() 38 | } 39 | 40 | override fun deleteSessionData() { 41 | prefs.edit().putString(BLOCKSTACK_SESSION, EMPTY_DATA).apply() 42 | sessionDataObject = SessionData(JSONObject()) 43 | } 44 | 45 | override fun updateUserData(userData: UserData) { 46 | sessionDataObject.json.put("userData", userData.json) 47 | prefs.edit().putString(BLOCKSTACK_SESSION, sessionDataObject.json.toString()).apply() 48 | } 49 | } 50 | 51 | fun Context.getBlockstackSharedPreferences(): SharedPreferences = 52 | getSharedPreferences("${packageName}_blockstack_prefs", Context.MODE_PRIVATE) -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/SigningAlgorithm.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk 2 | 3 | /** 4 | * An enum of algorithms used to sign tokens 5 | * 6 | * @property name identifies the algorithm 7 | */ 8 | enum class SigningAlgorithm(val algorithmName: String) { 9 | /** 10 | * Represents ECDSA with a P-256K curve. 11 | */ 12 | ES256K("ES256K") 13 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/extensions/Addresses.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.extensions 2 | 3 | import me.uport.sdk.core.hexToByteArray 4 | import org.kethereum.crypto.getCompressedPublicKey 5 | import org.kethereum.extensions.toBytesPadded 6 | import org.kethereum.model.ECKeyPair 7 | import org.kethereum.model.PUBLIC_KEY_SIZE 8 | import org.kethereum.model.PublicKey 9 | import org.komputing.kbase58.encodeToBase58String 10 | import org.komputing.khash.ripemd160.extensions.digestRipemd160 11 | import org.komputing.khash.sha256.extensions.sha256 12 | import org.komputing.khex.extensions.toNoPrefixHexString 13 | 14 | fun ECKeyPair.toHexPublicKey64(): String { 15 | return this.getCompressedPublicKey().toNoPrefixHexString() 16 | } 17 | 18 | fun ECKeyPair.toStxAddress(): String { 19 | val sha256 = toHexPublicKey64().hexToByteArray().sha256() 20 | val hash160 = sha256.digestRipemd160() 21 | val extended = "b0${hash160.toNoPrefixHexString()}" 22 | val cs = checksum("16${hash160.toNoPrefixHexString()}") 23 | return (extended + cs).hexToByteArray().encodeCrockford32() 24 | } 25 | 26 | fun ECKeyPair.toTestNetStxAddress() : String { 27 | val sha256 = toHexPublicKey64().hexToByteArray().sha256() 28 | val hash160 = sha256.digestRipemd160() 29 | val extended = "d0${hash160.toNoPrefixHexString()}" 30 | val cs = checksum("1a${hash160.toNoPrefixHexString()}") 31 | return (extended + cs).hexToByteArray().encodeCrockford32() 32 | } 33 | 34 | fun String.toBtcAddress(): String { 35 | val sha256 = hexToByteArray().sha256() 36 | val hash160 = sha256.digestRipemd160() 37 | val extended = "00${hash160.toNoPrefixHexString()}" 38 | val checksum = checksum(extended) 39 | return(extended + checksum).hexToByteArray().encodeToBase58String() 40 | } 41 | 42 | fun ECKeyPair.toBtcAddress(): String { 43 | val publicKey = toHexPublicKey64() 44 | return publicKey.toBtcAddress() 45 | } 46 | 47 | fun PublicKey.toBtcAddress(): String { 48 | //add the uncompressed prefix 49 | val ret = this.key.toBytesPadded(PUBLIC_KEY_SIZE + 1) 50 | ret[0] = 4 51 | val point = org.kethereum.crypto.CURVE.decodePoint(ret) 52 | val compressedPublicKey = point.encoded(true).toNoPrefixHexString() 53 | val sha256 = compressedPublicKey.hexToByteArray().sha256() 54 | val hash160 = sha256.digestRipemd160() 55 | val extended = "00${hash160.toNoPrefixHexString()}" 56 | val checksum = checksum(extended) 57 | return (extended + checksum).hexToByteArray().encodeToBase58String() 58 | } 59 | 60 | private fun checksum(extended: String): String { 61 | val checksum = extended.hexToByteArray().sha256().sha256() 62 | val shortPrefix = checksum.slice(0..3) 63 | return shortPrefix.toNoPrefixHexString() 64 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/BlockstackAccount.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | import org.blockstack.android.sdk.extensions.toBtcAddress 4 | import org.blockstack.android.sdk.getOrigin 5 | import org.json.JSONObject 6 | import org.kethereum.bip32.generateChildKey 7 | import org.kethereum.bip32.model.ExtendedKey 8 | import org.kethereum.extensions.toHexStringNoPrefix 9 | import org.komputing.kbip44.BIP44Element 10 | import org.komputing.khash.sha256.Sha256 11 | import org.komputing.khex.extensions.toNoPrefixHexString 12 | import java.net.URI 13 | 14 | data class BlockstackAccount(val username: String?, val keys: ExtendedKey, val salt: String, val metaData: MetaData = MetaData()) { 15 | 16 | fun getAppsNode(): AppsNode { 17 | return AppsNode(keys.generateChildKey(BIP44Element(true, APPS_NODE_INDEX)), salt) 18 | } 19 | 20 | val ownerAddress: String 21 | get() = keys.keyPair.toBtcAddress() 22 | 23 | companion object { 24 | const val APPS_NODE_INDEX = 0 25 | const val SIGNING_NODE_INDEX = 1 26 | const val ENCRYPTION_NODE_INDEX = 2 27 | 28 | data class MetaData( 29 | var permissions: List = emptyList(), 30 | var email: String? = null, 31 | var profileUrl: String? = null 32 | ) 33 | } 34 | } 35 | 36 | data class AppNode(val keys: ExtendedKey) { 37 | fun getPrivateKeyHex(): String { 38 | return keys.keyPair.privateKey.key.toHexStringNoPrefix() 39 | } 40 | 41 | fun toBtcAddress(): String { 42 | return keys.keyPair.toBtcAddress() 43 | } 44 | } 45 | 46 | data class AppsNode(val keys: ExtendedKey, val salt: String) { 47 | 48 | fun getAppNode(origin: String): AppNode { 49 | val normalizedOrigin = URI(origin).getOrigin() 50 | val hash = Sha256.digest("$normalizedOrigin$salt".toByteArray()).toNoPrefixHexString() 51 | val appIndex = hashCode(hash) 52 | return AppNode(keys.generateChildKey(BIP44Element(true, appIndex))) 53 | } 54 | } 55 | 56 | fun BlockstackAccount.toUserData(): UserData { 57 | val identityAddress = this.keys.keyPair.toBtcAddress() 58 | return UserData( 59 | JSONObject().put("identityAddress", identityAddress) 60 | .put("username", this.username) 61 | .put("decentralizedID", "did:btc-addr:${identityAddress}") 62 | ) 63 | } 64 | 65 | fun hashCode(string: String): Int { 66 | var hash = 0 67 | if (string.length == 0) return hash 68 | 69 | for (i in 0..string.length - 1) { 70 | val character = string.codePointAt(i) 71 | hash = (hash shl 5) - hash + character 72 | hash = hash and hash 73 | } 74 | return hash and 0x7fffffff 75 | } 76 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/BlockstackConfig.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | import org.blockstack.android.sdk.BaseScope 4 | import org.blockstack.android.sdk.Scope 5 | import java.net.URI 6 | 7 | /** 8 | * Configuration of the app using Blockstack 9 | * 10 | * @property appDomain the domain of the app, like `https://example.com` 11 | * @property redirectPath the redirect path relative to appDomain used after successful sign-in. This page should redirect 12 | * to the url defined in the AndroidManifest 13 | * @property manifestPath the manifest path relative to appDomain. This page is the same as for progressive web apps. 14 | * @property scopes the permissions that the app needs from the user 15 | * @property coreNode - override the default or user selected core node 16 | * @property authenticatorURL - the web-based fall back authenticator 17 | 18 | */ 19 | data class BlockstackConfig( 20 | val appDomain: URI, 21 | val redirectPath: String, 22 | val manifestPath: String, 23 | val scopes: Array, 24 | val coreNode: String? = null, 25 | val authenticatorUrl: String? = null 26 | ) 27 | 28 | 29 | /** 30 | * convenience method to build default configuration from the app domain 31 | * 32 | * @receiver the app domain without trailing slash 33 | * @param scopes the requested permissions 34 | * @param redirectPath the path of the redirection url that is appended to the app domain, needs leading slash, defaults to '/redirect' 35 | * @param manifestPath the path of the manifest url that is appended to the app domain, needs leading slash, defaults to `/manifest.json' 36 | */ 37 | fun String.toBlockstackConfig(scopes: Array = arrayOf(BaseScope.StoreWrite.scope), redirectPath: String = "/redirect", manifestPath: String = "/manifest.json"): BlockstackConfig = 38 | BlockstackConfig( 39 | URI(this), 40 | redirectPath, 41 | manifestPath, 42 | scopes) -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/BlockstackIdentity.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | import org.blockstack.android.sdk.extensions.toHexPublicKey64 4 | import org.kethereum.bip32.model.ExtendedKey 5 | import org.komputing.khash.sha256.Sha256 6 | import org.komputing.khex.extensions.toNoPrefixHexString 7 | 8 | data class BlockstackIdentity(val identityKeys: ExtendedKey) { 9 | 10 | var salt: String 11 | private set 12 | 13 | init { 14 | salt = Sha256.digest(identityKeys.keyPair.toHexPublicKey64().toByteArray()).toNoPrefixHexString() 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/CipherObject.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | import org.json.JSONObject 4 | 5 | /** 6 | * Object containing encrypted content. It is backed by the JSONObject. 7 | * 8 | * The json object can be stored or transferred as string or file using 9 | * `json.toString()`. This an then be used for decrypting the content in @see decryptContent 10 | */ 11 | class CipherObject(private val jsonObject: JSONObject) { 12 | 13 | constructor (iv: String, ephemeralPK: String, cipherText: String, mac: String, wasString: Boolean) : this(JSONObject() 14 | .put("iv", iv) 15 | .put("ephemeralPK", ephemeralPK) 16 | .put("cipherText", cipherText) 17 | .put("mac", mac) 18 | .put("wasString", wasString)) 19 | 20 | /** 21 | * json representation of the encrypted content 22 | * 23 | * json.toString should be used as parameter of decryptContent. 24 | * 25 | * @see BlockstackSession.decryptContent 26 | */ 27 | val json: JSONObject 28 | get() { 29 | return jsonObject 30 | } 31 | 32 | val iv: String 33 | get() { 34 | return jsonObject.getString("iv") 35 | } 36 | val ephemeralPK: String 37 | get() { 38 | return jsonObject.getString("ephemeralPK") 39 | } 40 | val cipherText: String 41 | get() { 42 | return jsonObject.getString("cipherText") 43 | } 44 | val mac: String 45 | get() { 46 | return jsonObject.getString("mac") 47 | } 48 | val wasString: Boolean 49 | get() { 50 | return jsonObject.getBoolean("wasString") 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/CryptoOptions.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | import org.json.JSONObject 4 | 5 | /** 6 | * An object to configure options for `encrypt` and `decrypt` operations. 7 | * 8 | * @property publicKey the hex string of the ECDSA public key to use for encryption. 9 | * @property privateKey the hex string of the ECDSA private key to use for decryption. 10 | */ 11 | class CryptoOptions(val publicKey: String? = null, val privateKey: String? = null) { 12 | 13 | /** 14 | * json representation of these options 15 | */ 16 | fun toJSON(): JSONObject { 17 | val optionsObject = JSONObject() 18 | optionsObject.put("privateKey", if (privateKey == null) JSONObject.NULL else privateKey) 19 | optionsObject.put("publicKey", if (publicKey == null) JSONObject.NULL else publicKey) 20 | return optionsObject 21 | } 22 | 23 | /** 24 | * string representation in json format used by blockstack.js 25 | */ 26 | override fun toString(): String { 27 | return toJSON().toString() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/DeleteFileOptions.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | import org.json.JSONObject 4 | 5 | /** 6 | * An object to configure options for `deleteFile` operations. 7 | * 8 | * @property wasSigned set to true if the file was originally signed 9 | * in order for the corresponding signature file to also be deleted. 10 | */ 11 | public class DeleteFileOptions(val wasSigned: Boolean = false, val gaiaHubConfig: GaiaHubConfig? = null) { 12 | 13 | /** 14 | * json representation of these options as used by blockstack.js 15 | */ 16 | fun toJSON(): JSONObject { 17 | val optionsObject = JSONObject() 18 | optionsObject.put("wasSigned", wasSigned) 19 | return optionsObject 20 | } 21 | 22 | /** 23 | * string representation in json format used by blockstack.js 24 | */ 25 | override fun toString(): String { 26 | return toJSON().toString() 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/Entity.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | import org.json.JSONObject 4 | 5 | /** 6 | * Object representing a person or organization 7 | */ 8 | class Entity(private val jsonObject: JSONObject) { 9 | 10 | 11 | /** 12 | * The public key representing this entity 13 | */ 14 | val publicKey: String = jsonObject.getString("publicKey") 15 | 16 | /** 17 | * The `JSONObject` that backs this object. You use this object to 18 | * access properties that are not yet exposed by this class. 19 | */ 20 | val json: JSONObject 21 | get() = jsonObject 22 | 23 | companion object { 24 | fun withKey(publicKey: String): Entity { 25 | val json = JSONObject() 26 | json.put("publicKey", publicKey) 27 | return Entity(json) 28 | } 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/GaiaHubConfig.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | data class GaiaHubConfig(val urlPrefix: String, val address: String, val token: String, val server: String) 4 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/GetFileOptions.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | import org.json.JSONObject 4 | import java.net.URL 5 | 6 | /** 7 | * An object to configure options for `getFile` operations. 8 | * 9 | * @property decrypt attempt to decrypt the file 10 | * @property verify indicate if the content should be verified. Verification also requires that 11 | * UserSession.putFile was set to sign=true. 12 | * @property username the username of the user from which you wish to read the file 13 | * @property app the app from which to read the file 14 | * @property zoneFileLookupURL The URL to use for zonefile lookup. 15 | * If `null`, this will use a default lookup URL. 16 | */ 17 | public class GetFileOptions(val decrypt: Boolean = true, 18 | val verify: Boolean = false, 19 | val username: String? = null, 20 | val app: String? = null, 21 | val zoneFileLookupURL: URL? = null) { 22 | 23 | /** 24 | * json representation of these options as used by blockstack.js 25 | */ 26 | fun toJSON(): JSONObject { 27 | val optionsObject = JSONObject() 28 | optionsObject.put("decrypt", decrypt) 29 | optionsObject.put("verify", verify) 30 | optionsObject.put("username", if (username.isNullOrBlank()) JSONObject.NULL else username) 31 | optionsObject.put("app", if (app.isNullOrBlank()) JSONObject.NULL else app) 32 | optionsObject.put("zoneFileLookupURL", if (zoneFileLookupURL == null) JSONObject.NULL else zoneFileLookupURL) 33 | return optionsObject 34 | } 35 | 36 | /** 37 | * string representation in json format used by blockstack.js 38 | */ 39 | override fun toString(): String { 40 | return toJSON().toString() 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/Profile.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | import org.json.JSONObject 4 | 5 | /** 6 | * Object containing user's profile. This object is backed by the original JSON representation. 7 | */ 8 | class Profile(private val jsonObject: JSONObject) { 9 | 10 | /** 11 | * The name of the person of profile or null if not defined 12 | */ 13 | val name: String? 14 | get() { 15 | return jsonObject.optString("name") 16 | } 17 | 18 | /** 19 | * The short bio for person or a tag line for organizations 20 | */ 21 | val description: String? 22 | get() = jsonObject.optString("description") 23 | 24 | /** 25 | * The url of the avatar image or null if not defined 26 | */ 27 | val avatarImage: String? 28 | get() { 29 | return jsonObject 30 | .optJSONArray("image") 31 | ?.optJSONObject(0) // TODO iterator through images for avatar type 32 | ?.optString("contentUrl") 33 | } 34 | 35 | /** 36 | * The email address of the account of null if not defined or not authorized 37 | */ 38 | val email: String? 39 | get() = jsonObject.optString("email") 40 | 41 | /** 42 | * The `JSONObject` that backs this object. You use this object to 43 | * access properties that are not yet exposed by this class. 44 | */ 45 | val json: JSONObject 46 | get() = jsonObject 47 | 48 | /** 49 | * returns true if the account belongs to a person. This is defined by the schema.org type 'Person' 50 | */ 51 | fun isPerson() = "Person".equals(jsonObject.opt("@type")) 52 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/ProfileToken.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | import org.json.JSONObject 4 | 5 | /** 6 | * Object containing a decoded profile token 7 | */ 8 | class ProfileToken(private val jsonObject: JSONObject) { 9 | 10 | /** 11 | * The signature if the token was signed 12 | */ 13 | val signature: String? = jsonObject.optString("signature") 14 | 15 | 16 | /** 17 | * The `JSONObject` that backs this object. You use this object to 18 | * access properties that are not yet exposed by this class. 19 | */ 20 | val json: JSONObject 21 | get() = jsonObject 22 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/ProfileTokenPair.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | import org.json.JSONObject 4 | 5 | /** 6 | * Object containing an encoded and corresponding decoded profile token 7 | */ 8 | class ProfileTokenPair(private val jsonObject: JSONObject) { 9 | 10 | /** 11 | * The encoded token. 12 | */ 13 | val token: String? = jsonObject.optString("token") 14 | 15 | /** 16 | * The decoded token. 17 | */ 18 | val decodedToken: ProfileToken? = if (jsonObject.has("decodedToken")) { 19 | ProfileToken(jsonObject.getJSONObject("decodedToken")) 20 | } else { 21 | null 22 | } 23 | 24 | /** 25 | * The `JSONObject` that backs this object. You use this object to 26 | * access properties that are not yet exposed by this class. 27 | */ 28 | val json: JSONObject 29 | get() = jsonObject 30 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/Proof.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | import org.json.JSONObject 4 | 5 | /** 6 | * Object containing a social proof usually created by `BlockstackSession.validateProofs`. 7 | * The proof is not valid, if the claim couldn't be verified for whatever reasons. 8 | * 9 | * This object is backed by the original JSON representation. 10 | */ 11 | class Proof(private val jsonObject: JSONObject) { 12 | 13 | /** 14 | * The name of the social service. 15 | */ 16 | val service: String 17 | get() = jsonObject.optString("service") 18 | 19 | /** 20 | * The url used to proof the claim. 21 | */ 22 | val proofUrl: String 23 | get() = jsonObject.optString("proof_url") 24 | 25 | /** 26 | * The identifier of the social service that is claimed. 27 | */ 28 | val identifier: String 29 | get() = jsonObject.optString("identifier") 30 | 31 | /** 32 | * The validity of the proof. 33 | */ 34 | val valid: Boolean 35 | get() = jsonObject.optBoolean("valid") 36 | 37 | /** 38 | * The `JSONObject` that backs this object. You use this object to 39 | * access properties that are not yet exposed by this class. 40 | */ 41 | val json: JSONObject 42 | get() = jsonObject 43 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/PutFileOptions.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | import org.json.JSONObject 4 | import java.security.InvalidParameterException 5 | 6 | /** 7 | * An object to configure options for `putFile` operations. 8 | * 9 | * @property encrypt encrypt the content with the public key of the current user before writing to storage 10 | * @property encryptionKey encrypt the content with the given key, if not null then `encrypt` is ignored 11 | * @property contentType contentType of file to be used only if not encrypted 12 | * @property sign (Boolean or String) If set to `true` the data is signed using ECDSA on SHA256 hashes 13 | * with the user's app private key. If a string is specified, it is used as the private key instead 14 | * of the user's app private key. The signature can be verified with `GetFileOptions.verify` set to 15 | * true when retrieving the data. 16 | */ 17 | public class PutFileOptions(val encrypt: Boolean = true, val encryptionKey:String? = null, 18 | val contentType: String? = null, val sign: Any = false) { 19 | 20 | /** 21 | * json representation of these options as used by blockstack.js 22 | */ 23 | fun toJSON(): JSONObject { 24 | val optionsObject = JSONObject() 25 | optionsObject.put("encrypt", encrypt) 26 | if (!encrypt && contentType != null) { 27 | optionsObject.put("contentType", contentType) 28 | } 29 | optionsObject.put("sign", sign) 30 | return optionsObject 31 | } 32 | 33 | /** 34 | * string representation in json format used by blockstack.js 35 | */ 36 | override fun toString(): String { 37 | return toJSON().toString() 38 | } 39 | 40 | fun shouldSign(): Boolean { 41 | if (sign is Boolean) { 42 | return sign 43 | } else if (sign is String) { 44 | return sign.isNotEmpty() 45 | } else { 46 | throw InvalidParameterException("sign must be a boolean or non-empty string") 47 | } 48 | } 49 | 50 | fun shouldEncrypt(): Boolean { 51 | return encrypt || encryptionKey != null 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/SessionData.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | import org.json.JSONObject 4 | 5 | class SessionData(private val jsonObject: JSONObject) { 6 | val json: JSONObject 7 | get() { 8 | return jsonObject 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/SignatureObject.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | import okio.ByteString 4 | import okio.ByteString.Companion.toByteString 5 | import org.json.JSONObject 6 | 7 | data class SignatureObject(val signature: String, val publicKey: String) { 8 | fun toJSONByteString(): ByteString { 9 | val jsonString = JSONObject() 10 | .put("signature", signature) 11 | .put("publicKey", publicKey).toString() 12 | val bytes = jsonString.toByteArray() 13 | return bytes.toByteString(0, bytes.size) 14 | } 15 | 16 | companion object { 17 | fun fromJSONString(jsonString: String): SignatureObject { 18 | val json = JSONObject(jsonString) 19 | return SignatureObject(json.getString("signature"), json.getString("publicKey")) 20 | } 21 | } 22 | } 23 | 24 | data class SignedCipherObject(val signature: String, val publicKey: String, val cipherText: String) { 25 | fun toJSONByteString(): ByteString { 26 | val jsonString = JSONObject() 27 | .put("signature", signature) 28 | .put("publicKey", publicKey) 29 | .put("cipherText", cipherText).toString() 30 | val bytes = jsonString.toByteArray() 31 | return bytes.toByteString(0, bytes.size) 32 | } 33 | 34 | companion object { 35 | fun fromJSONString(jsonString: String): SignedCipherObject { 36 | val json = JSONObject(jsonString) 37 | return SignedCipherObject(json.getString("signature"), json.getString("publicKey"), json.getString("cipherText")) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/UserData.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | import org.json.JSONObject 4 | 5 | private val PROFILE = "profile" 6 | 7 | /** 8 | * Object containing user data. This object is backed by the original JSON representation. 9 | * 10 | * This currently provides very minimal functionality. To access the full array of values, 11 | * you should parse the `json` property of this object or send a pull request to add additional 12 | * functionality to this class. 13 | */ 14 | class UserData(private val jsonObject: JSONObject) { 15 | 16 | /** 17 | * The profile of the user or null if not defined 18 | */ 19 | val profile: Profile? = if (jsonObject.has(PROFILE)) { 20 | Profile(jsonObject.getJSONObject(PROFILE)) 21 | } else { 22 | null 23 | } 24 | 25 | /** 26 | * The user's decentralized identifier - this is used to uniquely identify a user 27 | */ 28 | val decentralizedID: String 29 | get() { 30 | return jsonObject.getString("decentralizedID") 31 | } 32 | 33 | /** 34 | * The user's private key for the currently logged in app 35 | */ 36 | val appPrivateKey: String 37 | get() { 38 | return jsonObject.getString("appPrivateKey") 39 | } 40 | 41 | /** 42 | * The user's gaia storage location 43 | */ 44 | val hubUrl: String 45 | get() = jsonObject.getString("hubUrl") 46 | 47 | /** 48 | * The `JSONObject` that backs this object. You use this object to 49 | * access properties that are not yet exposed by this class. 50 | */ 51 | val json: JSONObject 52 | get() { 53 | return jsonObject 54 | } 55 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/ZoneFile.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model 2 | 3 | import org.blockstack.android.sdk.URIType 4 | import org.json.JSONArray 5 | import org.json.JSONObject 6 | 7 | /** 8 | * Object representing a zone file 9 | */ 10 | class ZoneFile(private val jsonObject: JSONObject) { 11 | 12 | /** 13 | * The `JSONObject` that backs this object. You use this object to 14 | * access properties that are not yet exposed by this class. 15 | */ 16 | val json: JSONObject 17 | get() = jsonObject 18 | 19 | 20 | /** 21 | * The target of the first uri entry or null if not defined 22 | */ 23 | val tokenFileUri: String? 24 | get() { 25 | if (!jsonObject.has("uri")) { 26 | return null 27 | } 28 | val uris: JSONArray? = jsonObject.optJSONArray("uri") 29 | if (uris == null) { 30 | return null 31 | } 32 | if (uris.length() < 1) { 33 | return null 34 | } 35 | val firstUriRecord = uris.get(0) 36 | 37 | if (firstUriRecord !is URIType) { 38 | return null 39 | } 40 | if (firstUriRecord.target.isEmpty()) { 41 | return null 42 | } 43 | var tokenFileUrl = firstUriRecord.target 44 | 45 | if (tokenFileUrl.startsWith("https")) { 46 | // pass 47 | } else if (tokenFileUrl.startsWith("http")) { 48 | // pass 49 | } else { 50 | tokenFileUrl = "https://$tokenFileUrl" 51 | } 52 | return tokenFileUrl 53 | } 54 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/network/AccountStatus.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model.network 2 | 3 | import org.blockstack.android.sdk.optStringOrNull 4 | import org.json.JSONObject 5 | import java.math.BigInteger 6 | 7 | /** 8 | * Object containing information about an account for a particular token type. 9 | * This includes its total number of expenditures and credits, lockup times, last txid, and so on. 10 | */ 11 | class AccountStatus(private val jsonObject: JSONObject) { 12 | /** 13 | * The account's address. 14 | */ 15 | val address: String? 16 | get() { 17 | return jsonObject.optString("address") 18 | } 19 | 20 | /** 21 | * The block this status belongs to. 22 | */ 23 | val blockId: Int? 24 | get() { 25 | return jsonObject.optInt("block_id") 26 | } 27 | 28 | /** 29 | * The unit of the token this status belongs to, e.g. BTC, STACKS. 30 | */ 31 | val type: String? 32 | get() { 33 | return jsonObject.optString("type") 34 | } 35 | 36 | /** 37 | * The credit value for this account for the token. 38 | */ 39 | val creditValue: BigInteger? 40 | get() { 41 | return jsonObject.optStringOrNull("credit_value")?.let { BigInteger(it) } 42 | } 43 | 44 | /** 45 | * The debit value for this account for the token. 46 | */ 47 | val debitValue: BigInteger? 48 | get() { 49 | return jsonObject.optStringOrNull("debit_value")?.let { BigInteger(it) } 50 | } 51 | 52 | /** 53 | * The `JSONObject` that backs this object. You use this object to 54 | * access properties that are not yet exposed by this class. 55 | */ 56 | val json: JSONObject 57 | get() { 58 | return jsonObject 59 | } 60 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/network/Denomination.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model.network 2 | 3 | import org.blockstack.android.sdk.optStringOrNull 4 | import org.json.JSONObject 5 | import java.math.BigInteger 6 | 7 | /** 8 | * Object describing unit and amount of a price request. 9 | */ 10 | class Denomination(private val jsonObject: JSONObject) { 11 | /** 12 | * The unit of the cryptocurrency, e.g. BTC, STACKS. 13 | */ 14 | val units: String? 15 | get() { 16 | return jsonObject.optString("units") 17 | } 18 | 19 | /** 20 | * The amount of the price information in the smallest denomination 21 | * e.g. if `units` is BTC, `amount` will be in satoshis; if `units` is STACKS, `amount` will be in microStacks. 22 | */ 23 | val amount: BigInteger? 24 | get() = jsonObject.optStringOrNull("amount")?.let { BigInteger(it, 10) } 25 | 26 | /** 27 | * The `JSONObject` that backs this object. You use this object to 28 | * access properties that are not yet exposed by this class. 29 | */ 30 | val json: JSONObject 31 | get() { 32 | return jsonObject 33 | } 34 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/network/NameInfo.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model.network 2 | 3 | import org.json.JSONObject 4 | 5 | /** 6 | * Object containing information about a name including 7 | * address that owns it, the block at which it expires, and the zone file anchored to it (if available). 8 | */ 9 | class NameInfo(private val jsonObject: JSONObject) { 10 | /** 11 | * The owning address. 12 | */ 13 | val address: String? 14 | get() { 15 | return jsonObject.optString("address") 16 | } 17 | 18 | /** 19 | * The underlying blockchain. 20 | */ 21 | val blockchain: String? 22 | get() { 23 | return jsonObject.optString("blockchain") 24 | } 25 | 26 | /** 27 | * The default decentralized identifier. 28 | */ 29 | val did: String? 30 | get() { 31 | return jsonObject.optString("did") 32 | } 33 | 34 | /** 35 | * The id of the last transaction. 36 | */ 37 | val lastTxid: String? 38 | get() { 39 | return jsonObject.optString("last_txid") 40 | } 41 | 42 | /** 43 | * The status of the name, one of `revoked`, `registered`, `registered_subdomain`, `available`. 44 | */ 45 | val status: String? 46 | get() { 47 | return jsonObject.optString("status") 48 | } 49 | 50 | /** 51 | * The zone file's text content if available. 52 | */ 53 | val zonefile: String? 54 | get() { 55 | return jsonObject.optString("zonefile") 56 | } 57 | 58 | /** 59 | * The hash of the associated zone file. 60 | */ 61 | val zonefileHash: String? 62 | get() { 63 | return jsonObject.optString("zonefile_hash") 64 | } 65 | 66 | /** 67 | * When the name expires. Not available for subdomains. 68 | */ 69 | val expireBlock: String? 70 | get() { 71 | return jsonObject.optString("expire_block") 72 | } 73 | 74 | /** 75 | * When the name needs to be renewed. Not available for subdomains. 76 | */ 77 | val renewalDeadline: String? 78 | get() { 79 | return jsonObject.optString("renewal_deadline") 80 | } 81 | 82 | 83 | /** 84 | * Number of blocks before an expired name can be owned by somebody else. Not available for subdomains. 85 | */ 86 | val gracePeriod: String? 87 | get() { 88 | return jsonObject.optString("grace_period") 89 | } 90 | 91 | /** 92 | * How the resolve subdomains of this name. Not available for subdomains. 93 | */ 94 | val resolver: String? 95 | get() { 96 | return jsonObject.optString("resolver") 97 | } 98 | 99 | /** 100 | * The `JSONObject` that backs this object. You use this object to 101 | * access properties that are not yet exposed by this class. 102 | */ 103 | val json: JSONObject 104 | get() { 105 | return jsonObject 106 | } 107 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/model/network/NamespaceInfo.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.model.network 2 | 3 | import org.json.JSONObject 4 | 5 | /** 6 | * Object containing information about a namespace including the pricing parameters and creation history. 7 | */ 8 | class NamespaceInfo(private val jsonObject: JSONObject) { 9 | 10 | /** 11 | * The `JSONObject` that backs this object. You use this object to 12 | * access properties that are not yet exposed by this class. 13 | */ 14 | val json: JSONObject 15 | get() { 16 | return jsonObject 17 | } 18 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/main/java/org/blockstack/android/sdk/ui/ConnectHowItWorksActivity.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.ui 2 | 3 | import android.content.Intent 4 | import android.graphics.BitmapFactory 5 | import android.net.Uri 6 | import android.os.Bundle 7 | import androidx.appcompat.app.AppCompatActivity 8 | import androidx.browser.customtabs.CustomTabsIntent 9 | import androidx.core.content.ContextCompat 10 | import kotlinx.android.synthetic.main.activity_connect_how_it_works.* 11 | import org.blockstack.android.sdk.BlockstackSignIn 12 | import org.blockstack.android.sdk.R 13 | 14 | class ConnectHowItWorksActivity : AppCompatActivity() { 15 | 16 | override fun onCreate(savedInstanceState: Bundle?) { 17 | super.onCreate(savedInstanceState) 18 | setContentView(R.layout.activity_connect_how_it_works) 19 | 20 | button_get_started.setOnClickListener { 21 | setResult(RESULT_OK) 22 | this.finish() 23 | } 24 | 25 | power_by_blockstack.setOnClickListener { 26 | val locationUri = Uri.parse("https://www.blockstack.org/") 27 | if (BlockstackSignIn.shouldLaunchInCustomTabs) { 28 | val builder = CustomTabsIntent.Builder() 29 | val options = BitmapFactory.Options() 30 | options.outWidth = 24 31 | options.outHeight = 24 32 | options.inScaled = true 33 | val backButton = BitmapFactory.decodeResource(resources, R.drawable.ic_arrow_back, options) 34 | builder.setCloseButtonIcon(backButton) 35 | builder.setToolbarColor(ContextCompat.getColor(this, R.color.org_blockstack_purple_50_logos_types)) 36 | builder.setToolbarColor(ContextCompat.getColor(this, R.color.org_blockstack_purple_85_lines)) 37 | builder.setShowTitle(true) 38 | val customTabsIntent = builder.build() 39 | customTabsIntent.launchUrl(this, locationUri) 40 | } else { 41 | startActivity(Intent(Intent.ACTION_VIEW, locationUri).addCategory(Intent.CATEGORY_BROWSABLE)) 42 | } 43 | 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/color/org_blockstack_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/color/org_blockstack_tint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/drawable-hdpi/ic_arrow_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/blockstack-sdk/src/main/res/drawable-hdpi/ic_arrow_back.png -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/drawable-mdpi/ic_arrow_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/blockstack-sdk/src/main/res/drawable-mdpi/ic_arrow_back.png -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/drawable-xhdpi/ic_arrow_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/blockstack-sdk/src/main/res/drawable-xhdpi/ic_arrow_back.png -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/drawable-xxhdpi/ic_arrow_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/blockstack-sdk/src/main/res/drawable-xxhdpi/ic_arrow_back.png -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/drawable-xxxhdpi/ic_arrow_back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/blockstack-sdk/src/main/res/drawable-xxxhdpi/ic_arrow_back.png -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/drawable/ic_back.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/drawable/ic_close.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/drawable/ic_link.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/drawable/ic_lock.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/drawable/ic_secret_key.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/drawable/ic_track.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/drawable/org_blockstack_black_logo.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/font/inter.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 20 | 21 | 28 | 29 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/font/inter_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/blockstack-sdk/src/main/res/font/inter_bold.ttf -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/font/inter_medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/blockstack-sdk/src/main/res/font/inter_medium.ttf -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/font/inter_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/blockstack-sdk/src/main/res/font/inter_regular.ttf -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/layout/activity_connect_how_it_works.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 20 | 21 | 22 | 23 | 31 | 32 | 39 | 40 | 45 | 46 | 53 | 54 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/values-de/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Los geht\'s 4 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/values-fr/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | On y va 4 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/values-zh/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 开始吧 4 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #270f34 4 | #4b3457 5 | #564061 6 | #95889c 7 | #cac3cd 8 | #3700ff 9 | #0000C4 10 | #00008C 11 | #2D2D2D 12 | #808080 13 | 14 | 15 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Blockstack 3 | Get your Secret Key 4 | Get Started 5 | %1$s guarantees your privacy by encrypting everything 6 | Sign in 7 | How it works 8 | Blockstack Connect 9 | How it works 10 | The app keeps everything you do private with Blockstack\'s Secret Key technology\nNormally, apps keep your data for them to use. When you have a Secret Key, you no longer have to trust the app with your data because the app won\'t have access. 11 | \n\nWhat is Blockstack?\nBlockstack is the open-source technology that generates your Secret Key. There\'s no company that owns or controls Blockstack, it is independent. Go to blockstack.org 12 | to learn more. 13 | \n\nEncryption\nEncryption is always on. It locks everything you do in the app into useless codes. Because of this, the app can\'t see or track your activity. Your data can only be unlocked with the key that you own. No one else has this key, not even the app, so no one else can unlock your data 14 | \n\nWhat is a Secret Key?\nYour Secret Key unlocks your data. It\'s created independently from the app to make sure that the app doesn\'t have it. An open-source protocol called Blockstack generates your Secret Key when you sign up. Nobody but you will have your Secret Key, to make sure that only you have access to your data. 15 | \n\nWhen will I need my Secret Key?\nYou\'ll need your Secret Key to prove it\'s you when you use the app on a new device, such as a new phone or laptop. After that, your Secret Key will stay active to keep you safe and private in the apps you use on that device. 16 | 17 | You\'ll get a Secret Key that automatically encrypts everything you do 18 | %1$s won\'t be able to see, access, or track your activity 19 | Powered By 20 | 21 | -------------------------------------------------------------------------------- /blockstack-sdk/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 21 | 22 | 27 | 28 | 31 | 32 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /blockstack-sdk/src/test/java/com/colendi/ecies/EncryptionApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.colendi.ecies; 2 | 3 | import org.junit.Test; 4 | 5 | public class EncryptionApplicationTests { 6 | 7 | @Test 8 | public void contextLoads() { 9 | Encryption encryption = new Encryption(); 10 | 11 | String privateKey = "7a7480972a756b1f117faadd23f9af00bdb309d3553e47b3b5d7f2756df620b3"; 12 | String publicKey = "04327453891187123d8a122c47ac5a98ff9a1cbc0dd28ce6fae2183a51a7b8aeaaea8b75c7ac46fbc2434c0fe8b8fecb5fee1be8b52bff3072046fe26ca3652279"; 13 | String message = "Colendi"; 14 | 15 | 16 | EncryptedResult encryptedResult = encryption.encryptWithPublicKey(message.getBytes(), publicKey); 17 | 18 | EncryptedResultForm formData = new EncryptedResultForm(); 19 | 20 | formData.setPrivateKey(privateKey); 21 | formData.setCiphertext(encryptedResult.getCiphertext()); 22 | formData.setEphemPublicKey(encryptedResult.getEphemPublicKey()); 23 | formData.setIv(encryptedResult.getIv()); 24 | formData.setMac(encryptedResult.getMac()); 25 | 26 | String result = new String(encryption.decryptWithPrivateKey(formData)); 27 | assert (result.equals(message)); 28 | } 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /blockstack-sdk/src/test/java/org/blockstack/android/sdk/Crockford32Test.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk 2 | 3 | import org.blockstack.android.sdk.extensions.decodeCrockford32 4 | import org.blockstack.android.sdk.extensions.encodeCrockford32 5 | import org.junit.Assert 6 | import org.junit.Test 7 | 8 | class Crockford32Test { 9 | 10 | val strings = listOf( 11 | "a46ff88886c2ef9762d970b4d2c63678835bd39d", 12 | "", 13 | "0000000000000000000000000000000000000000", 14 | "0000000000000000000000000000000000000001", 15 | "1000000000000000000000000000000000000001", 16 | "1000000000000000000000000000000000000000", 17 | "1", 18 | "22", 19 | "001", 20 | "0001", 21 | "00001", 22 | "000001", 23 | "0000001", 24 | "00000001", 25 | "10", 26 | "100", 27 | "1000", 28 | "10000", 29 | "100000", 30 | "1000000", 31 | "10000000", 32 | "100000000" 33 | ) 34 | 35 | val c32Strings = listOf( 36 | "C4T3CSK670W3GE1PCCS6ASHS6WV34S1S6WR64D3469HKCCSP6WW3GCSNC9J36EB4", 37 | "", 38 | "60R30C1G60R30C1G60R30C1G60R30C1G60R30C1G60R30C1G60R30C1G60R30C1G", 39 | "60R30C1G60R30C1G60R30C1G60R30C1G60R30C1G60R30C1G60R30C1G60R30C1H", 40 | "64R30C1G60R30C1G60R30C1G60R30C1G60R30C1G60R30C1G60R30C1G60R30C1H", 41 | "64R30C1G60R30C1G60R30C1G60R30C1G60R30C1G60R30C1G60R30C1G60R30C1G", 42 | "64", 43 | "68S0", 44 | "60R32", 45 | "60R30C8", 46 | "60R30C1H", 47 | "60R30C1G64", 48 | "60R30C1G60RG", 49 | "60R30C1G60R32", 50 | "64R0", 51 | "64R30", 52 | "64R30C0", 53 | "64R30C1G", 54 | "64R30C1G60", 55 | "64R30C1G60R0", 56 | "64R30C1G60R30", 57 | "64R30C1G60R30C0" 58 | ) 59 | 60 | @Test 61 | fun encodeTest() { 62 | strings.forEachIndexed { index, string -> 63 | Assert.assertEquals(c32Strings[index], string.encodeCrockford32()) 64 | } 65 | } 66 | 67 | @Test 68 | fun decodeTest() { 69 | c32Strings.forEachIndexed { index, string -> 70 | Assert.assertEquals(strings[index], string.decodeCrockford32()) 71 | } 72 | } 73 | 74 | @Test 75 | fun crockford32Test() { 76 | val encoded = "something very very big and complex".encodeCrockford32() 77 | Assert.assertEquals("something very very big and complex", encoded.decodeCrockford32()) 78 | } 79 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/test/java/org/blockstack/android/sdk/ScopesUnitTest.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk; 2 | 3 | import org.hamcrest.CoreMatchers.`is` 4 | import org.junit.Assert.assertEquals 5 | import org.junit.Assert.assertThat 6 | import org.junit.Test 7 | 8 | class ScopesUnitTest { 9 | @Test 10 | fun testScopesToString() { 11 | val scopes: Array = arrayOf(BaseScope.Email.scope, BaseScope.PublishData.scope, BaseScope.StoreWrite.scope) 12 | val result = Scope.scopesArrayToJSONString(scopes) 13 | assertEquals("[\"email\", \"publish_data\", \"store_write\"]", result) 14 | } 15 | 16 | @Test 17 | fun testFromJSName() { 18 | assertThat(BaseScope.StoreWrite, `is`(Scope.fromJSName("store_write"))) 19 | } 20 | 21 | @Test 22 | fun testBadJSName() { 23 | try { 24 | Scope.fromJSName("xyz") 25 | } catch (e: Exception) { 26 | assertThat(e.message, `is`("scope 'xyz' not defined, available scopes: store_write, publish_data, email")) 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /blockstack-sdk/src/test/java/org/blockstack/android/sdk/extensions/AddressesTest.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdk.extensions 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.runBlocking 5 | import kotlinx.coroutines.withContext 6 | import org.blockstack.android.sdk.model.BlockstackIdentity 7 | import org.junit.Assert 8 | import org.junit.Test 9 | import org.kethereum.bip32.generateChildKey 10 | import org.kethereum.bip32.toKey 11 | import org.kethereum.bip39.model.MnemonicWords 12 | import org.kethereum.bip39.toSeed 13 | import org.kethereum.extensions.toHexStringNoPrefix 14 | import org.komputing.kbip44.BIP44Element 15 | 16 | class AddressesTest { 17 | 18 | private val SEED_PHRASE = 19 | "float myth tuna chuckle estate recipe canoe equal sport matter zebra vanish pyramid this veteran oppose festival lava economy uniform open zoo shrug fade" 20 | private val PRIVATE_KEY = 21 | "9f6da87aa7a214d484517394ca0689a38faa8b3497bb9bf491bd82c31b5af796" //01 22 | private val PUBLIC_KEY = 23 | "023064b1fa3c279cd7c8eca2f41c3aa33dc48741819f38b740975af1e8fef61fe4" 24 | private val BTC_ADDRESS_MAINNET = "1Hu5PUAGWqaokbusF7ZUTpfnejwKbAeGUd" 25 | private val STX_ADDRESS_MAINNET = "SP2WNPKGHNM1PKE1D95KGADR1X5MWXTJHD8EJ1HHK" 26 | 27 | // Test environment 28 | private val STX_ADDRESS_TESTNET = "ST2WNPKGHNM1PKE1D95KGADR1X5MWXTJHDAYBBZPG" 29 | 30 | @Test 31 | fun stxAddressMainnetTest() = runBlocking { 32 | // Arrange 33 | val keys = generateWalletKeysFromMnemonicWords(SEED_PHRASE) 34 | 35 | // Act / Assert 36 | Assert.assertEquals(PUBLIC_KEY, keys.keyPair.toHexPublicKey64()) 37 | Assert.assertEquals(PRIVATE_KEY, keys.keyPair.privateKey.key.toHexStringNoPrefix()) 38 | Assert.assertEquals(BTC_ADDRESS_MAINNET, keys.keyPair.toBtcAddress()) 39 | Assert.assertEquals(STX_ADDRESS_MAINNET, "S${keys.keyPair.toStxAddress()}") 40 | } 41 | 42 | @Test 43 | fun stxAddressTestnetTest() = runBlocking { 44 | // Arrange 45 | val keys = generateWalletKeysFromMnemonicWords(SEED_PHRASE) 46 | 47 | // Act Assert 48 | Assert.assertEquals(STX_ADDRESS_TESTNET, "S${keys.keyPair.toTestNetStxAddress()}") 49 | } 50 | 51 | 52 | private suspend fun generateWalletKeysFromMnemonicWords(seedPhrase: String) = withContext( 53 | Dispatchers.IO 54 | ) { 55 | val words = MnemonicWords(seedPhrase) 56 | val stxKeys = BlockstackIdentity(words.toSeed().toKey("m/44'/5757'/0'/0")) 57 | return@withContext stxKeys.identityKeys.generateChildKey(BIP44Element(false, 0)) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /blockstack-sdktest/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /blockstack-sdktest/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | android { 6 | compileSdkVersion 30 7 | 8 | 9 | defaultConfig { 10 | minSdkVersion 21 11 | targetSdkVersion 30 12 | versionCode 1 13 | versionName "1.0" 14 | multiDexEnabled true 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | 26 | packagingOptions { 27 | exclude 'META-INF/*' 28 | } 29 | 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | } 37 | 38 | dependencies { 39 | implementation project(':blockstack-sdk') 40 | implementation 'com.eclipsesource.j2v8:j2v8:4.8.4@aar' 41 | implementation 'androidx.appcompat:appcompat:1.2.0' 42 | implementation 'androidx.browser:browser:1.2.0' 43 | implementation "androidx.core:core-ktx:1.3.2" 44 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9' 45 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 46 | testImplementation 'junit:junit:4.13.1' 47 | androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 48 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 49 | androidTestImplementation 'androidx.test:rules:1.3.0' 50 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 51 | androidTestImplementation 'org.mockito:mockito-android:3.6.0' 52 | androidTestImplementation 'androidx.test.espresso:espresso-intents:3.3.0' 53 | } 54 | repositories { 55 | mavenCentral() 56 | } 57 | -------------------------------------------------------------------------------- /blockstack-sdktest/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /blockstack-sdktest/src/androidTest/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /blockstack-sdktest/src/androidTest/java/org/blockstack/android/sdktest/BlockstackSession2EncryptionTest.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdktest 2 | 3 | import android.util.Log 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | import androidx.test.rule.ActivityTestRule 6 | import okhttp3.OkHttpClient 7 | import org.blockstack.android.sdk.Blockstack 8 | import org.blockstack.android.sdk.BlockstackSession 9 | import org.blockstack.android.sdk.model.CryptoOptions 10 | import org.blockstack.android.sdk.model.toBlockstackConfig 11 | import org.blockstack.android.sdktest.j2v8.BlockstackSessionJ2V8 12 | import org.blockstack.android.sdktest.test.TestActivity 13 | import org.hamcrest.CoreMatchers.`is` 14 | import org.hamcrest.MatcherAssert.assertThat 15 | import org.junit.Before 16 | import org.junit.Rule 17 | import org.junit.Test 18 | import org.junit.runner.RunWith 19 | 20 | 21 | private val PRIVATE_KEY = "a5c61c6ca7b3e7e55edee68566aeab22e4da26baa285c7bd10e8d2218aa3b229" 22 | private val PUBLIC_KEY = "027d28f9951ce46538951e3697c62588a87f1f1f295de4a14fdd4c780fc52cfe69" 23 | 24 | 25 | @RunWith(AndroidJUnit4::class) 26 | class BlockstackSession2EncryptionTest { 27 | 28 | @get:Rule 29 | val rule = ActivityTestRule(TestActivity::class.java) 30 | 31 | private lateinit var sessionJ2V8: BlockstackSessionJ2V8 32 | private lateinit var blockstack: Blockstack 33 | private lateinit var session: BlockstackSession 34 | 35 | @Before 36 | fun setup() { 37 | val sessionStore = sessionStoreforIntegrationTests(rule) 38 | val executor = IntegrationTestExecutor(rule) 39 | val callFactory = OkHttpClient() 40 | sessionJ2V8 = BlockstackSessionJ2V8(rule.activity, 41 | "https://flamboyant-darwin-d11c17.netlify.app".toBlockstackConfig(emptyArray()), 42 | sessionStore = sessionStore, 43 | executor = executor, 44 | callFactory = callFactory) 45 | 46 | // get a gaiaHubConfig by using a j2v8 call to gaia 47 | sessionJ2V8.listFiles({ false }, { 48 | val gaiaHubConfig = sessionStore.sessionData.json.getJSONObject("userData").getJSONObject("gaiaHubConfig") 49 | Log.d(TAG, gaiaHubConfig.toString()) 50 | }) 51 | 52 | blockstack = Blockstack() 53 | session = BlockstackSession(sessionStore, callFactory = callFactory, blockstack = blockstack) 54 | 55 | val appPrivateKey = sessionStore.sessionData.json.getJSONObject("userData").getString("appPrivateKey") 56 | val hubUrl = sessionStore.sessionData.json.getJSONObject("userData").getString("hubUrl") 57 | val associationToken = sessionStore.sessionData.json.getJSONObject("userData").optString("gaiaAssociationToken") 58 | } 59 | 60 | @Test 61 | fun testEncryptJ2V8ThenDecrypt() { 62 | val message = "Hello Test" 63 | val result = sessionJ2V8.encryptContent(message, CryptoOptions(publicKey = PUBLIC_KEY)) 64 | val plainText = blockstack.decryptContent(result.value!!.json.toString(), false, CryptoOptions(privateKey = PRIVATE_KEY)) 65 | assertThat(plainText.value as String, `is`(message)) 66 | } 67 | 68 | 69 | @Test 70 | fun testEncryptThenDecryptJ2V8() { 71 | val message = "Hello Test" 72 | val result = blockstack.encryptContent(message, CryptoOptions(publicKey = PUBLIC_KEY)) 73 | val plainText = sessionJ2V8.decryptContent(result.value!!.json.toString(), false, CryptoOptions(privateKey = PRIVATE_KEY)) 74 | assertThat(plainText.value as String, `is`(message)) 75 | } 76 | 77 | companion object { 78 | val TAG = BlockstackSession2EncryptionTest::class.java.simpleName 79 | } 80 | } -------------------------------------------------------------------------------- /blockstack-sdktest/src/androidTest/java/org/blockstack/android/sdktest/BlockstackSignInWithBrowserTest.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdktest 2 | 3 | import android.accessibilityservice.AccessibilityService 4 | import android.content.Intent 5 | import androidx.test.espresso.intent.Intents.intended 6 | import androidx.test.espresso.intent.matcher.IntentMatchers.* 7 | import androidx.test.espresso.intent.matcher.UriMatchers.hasHost 8 | import androidx.test.espresso.intent.rule.IntentsTestRule 9 | import androidx.test.platform.app.InstrumentationRegistry 10 | import kotlinx.coroutines.delay 11 | import kotlinx.coroutines.runBlocking 12 | import org.blockstack.android.sdk.BlockstackSignIn 13 | import org.blockstack.android.sdk.SessionStore 14 | import org.blockstack.android.sdk.model.toBlockstackConfig 15 | import org.blockstack.android.sdktest.test.TestActivity 16 | import org.hamcrest.Matchers.allOf 17 | import org.hamcrest.Matchers.containsString 18 | import org.junit.Before 19 | import org.junit.Rule 20 | import org.junit.Test 21 | 22 | class BlockstackSessionLoginWithBrowserTest { 23 | private val TIMEOUT = 3000L 24 | 25 | @get:Rule 26 | val rule = IntentsTestRule(TestActivity::class.java) 27 | 28 | private lateinit var signIn: BlockstackSignIn 29 | private lateinit var sessionStore: SessionStore 30 | 31 | @Before 32 | fun setup() { 33 | sessionStore = sessionStoreforIntegrationTests(rule) 34 | signIn = BlockstackSignIn(sessionStore, "https://example.com".toBlockstackConfig(kotlin.emptyArray())) 35 | } 36 | 37 | @Test 38 | fun testRedirect() { 39 | runBlocking { 40 | signIn.redirectUserToSignIn(rule.activity) 41 | delay(500) 42 | InstrumentationRegistry.getInstrumentation().uiAutomation 43 | .performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK) 44 | } 45 | intended(allOf(hasAction(Intent.ACTION_VIEW), 46 | hasData( 47 | hasHost("app.blockstack.org")), 48 | hasDataString(containsString("authResponse=")) 49 | )) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /blockstack-sdktest/src/androidTest/java/org/blockstack/android/sdktest/j2v8/Console.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdktest.j2v8 2 | 3 | import android.util.Log 4 | import com.eclipsesource.v8.V8Object 5 | 6 | 7 | interface Console { 8 | fun error(msg: Any) 9 | fun warn(msg: String) 10 | fun debug(msg: String) 11 | fun log(msg: String) 12 | } 13 | 14 | class LogConsole : Console { 15 | private val TAG = LogConsole::class.simpleName 16 | 17 | override fun error(msg: Any) { 18 | if (msg is V8Object) { 19 | Log.e(TAG, msg.toString()) 20 | } else if (msg is String) { 21 | Log.e(TAG, msg) 22 | } else { 23 | Log.e(TAG, msg.toString()) 24 | } 25 | 26 | } 27 | 28 | override fun warn(msg: String) { 29 | Log.w(TAG, msg) 30 | } 31 | 32 | override fun log(msg: String) { 33 | Log.i(TAG, msg) 34 | } 35 | 36 | override fun debug(msg: String) { 37 | Log.d(TAG, msg) 38 | } 39 | } -------------------------------------------------------------------------------- /blockstack-sdktest/src/androidTest/java/org/blockstack/android/sdktest/test/TestActivity.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android.sdktest.test 2 | 3 | import android.os.Bundle 4 | import android.preference.PreferenceManager 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.test.rule.ActivityTestRule 7 | import org.blockstack.android.sdk.BLOCKSTACK_SESSION 8 | import org.blockstack.android.sdk.SessionStore 9 | 10 | class TestActivity : AppCompatActivity() { 11 | 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | setContentView(R.layout.activity_test) 15 | } 16 | } 17 | 18 | 19 | val A_VALID_BLOCKSTACK_SESSION_JSON = "{\"transitKey\":\"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\",\"userData\":{\"username\":null,\"profile\":{\"@type\":\"Person\",\"@context\":\"http:\\/\\/schema.org\",\"apps\":{\"https:\\/\\/app.graphitedocs.com\":\"https:\\/\\/gaia.blockstack.org\\/hub\\/1Fuzd6sJXqtXhgoFpb8W19Ao4BCG3EHQGf\\/\",\"https:\\/\\/app.misthos.io\":\"https:\\/\\/gaia.blockstack.org\\/hub\\/1CkGhNN71a1dpgXQjncunYZpXakJvwrpmc\\/\"},\"image\":[{\"@type\":\"ImageObject\",\"name\":\"avatar\",\"contentUrl\":\"https:\\/\\/gaia.blockstack.org\\/hub\\/12Gpr9kYXJJn4fWec4nRVwHLSrrTieeywt\\/\\/avatar-0\"}],\"name\":\"fm\"},\"decentralizedID\":\"did:btc-addr:12Gpr9kYXJJn4fWec4nRVwHLSrrTieeywt\",\"identityAddress\":\"12Gpr9kYXJJn4fWec4nRVwHLSrrTieeywt\",\"appPrivateKey\":\"89f92476f13f5b173e53926ad7d6e22baf78c6b1dcdf200c38dc73d2bf47d43b\",\"coreSessionToken\":null,\"authResponseToken\":\"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJqdGkiOiI3MWI3NWY1Yi0xNDJkLTQxMzQtOGZkYy0zYWUyMWRmZTkzNjAiLCJpYXQiOjE1MzcxNjkxODYsImV4cCI6MTUzOTc2MTE4NSwiaXNzIjoiZGlkOmJ0Yy1hZGRyOjEyR3ByOWtZWEpKbjRmV2VjNG5SVndITFNyclRpZWV5d3QiLCJwcml2YXRlX2tleSI6IjdiMjI2OTc2MjIzYTIyMzEzOTMzMzM2NTM5MzYzNTY2NjMzNzM5NjM2MjYyMzAzNjY0NjMzMjM0MzIzNzY2NjY2MjYzMzczMjMyMzgzOTIyMmMyMjY1NzA2ODY1NmQ2NTcyNjE2YzUwNGIyMjNhMjIzMDMyMzQzMTY1MzY2NjYzMzEzODM0MzAzMDMyMzQzMTY0NjYzNzMzMzMzNTY1NjM2MjY2MzQzMzM0NjY2NDM5MzU2MjY0NjUzMjY2MzIzNTMxNjU2MzM3MzczNjMxMzQzNDM0MzAzMjY2NjUzNTM3NjE2MzYzMzA2MjY2MzQzNjYxMzQyMjJjMjI2MzY5NzA2ODY1NzI1NDY1Nzg3NDIyM2EyMjM5MzkzMTYxMzM2MzM1NjYzOTY2NjI2MzM2MzYzMzY2MzMzMDY0MzgzNzMxMzgzNzMyNjIzODM5MzMzMTYxMzMzMTMyNjUzMjM0NjM2MjM4MzAzNzM1NjQzODY0MzkzNzM1Mzg2MjM3NjEzMDY1NjMzMTM0Mzg2MTM2MzEzMjM2MzIzMjY1MzYzMjYyMzQ2NjY1NjYzNjMzNjMzMTYzNjYzMjMzNjEzMTM3MzM2NTM0NjUzOTMwMzIzMzM5MzQ2NDY1NjEzMTMxMzQzOTYzNjE2NTYxMzMzMzY2MzMzODY2NjIzMTMwMzczNTM0MzU2NDM1MzUzNDM3NjYzNjY2NjMzODMwMzg2MTM2MzIzNjM2MzAzMDMzNjMzNzM5MzMzNzM4MzYzMzM4MzMzNDMyNjEzNjYzMzMzOTM4MzM2MjM5MjIyYzIyNmQ2MTYzMjIzYTIyNjYzODMwMzQ2MjMzMzY2MjY0MzgzMjM2NjQzNTM3MzIzMzYxMzQzOTM4MzczOTYzMzAzMDYzNjQ2MzMzNjYzMDYxNjQzNzY0NjE2MTYyNjE2NDM0MzY2NDMzMzczNjY0MzUzMTYxNjI2NjYzNjYzMTM4MzM2MTM4MzAzNDMwMzgyMjJjMjI3NzYxNzM1Mzc0NzI2OTZlNjcyMjNhNzQ3Mjc1NjU3ZCIsInB1YmxpY19rZXlzIjpbIjAyODcyNmQyMGZhMTI4Yjc1OWViOGIwOWZjY2Y2ODU2ZTQzMjM1YmI5OTkxNjI0OWNmNjNhM2NiMjA0MTY0Y2I4ZSJdLCJwcm9maWxlIjpudWxsLCJ1c2VybmFtZSI6bnVsbCwiY29yZV90b2tlbiI6bnVsbCwiZW1haWwiOiJmcmllZGdlckBnbWFpbC5jb20iLCJwcm9maWxlX3VybCI6Imh0dHBzOi8vZ2FpYS5ibG9ja3N0YWNrLm9yZy9odWIvMTJHcHI5a1lYSkpuNGZXZWM0blJWd0hMU3JyVGllZXl3dC9wcm9maWxlLmpzb24iLCJodWJVcmwiOiJodHRwczovL2h1Yi5ibG9ja3N0YWNrLm9yZyIsInZlcnNpb24iOiIxLjIuMCJ9.QM8wx-EDjZrHk1ubK99divaejN5XAcCn_OkYAiPXQUNtzENUy8ogyrHzB4xC7mwfMckzFsMxEOdzwVW_Xcjqqg\",\"hubUrl\":\"https:\\/\\/hub.blockstack.org\"}}" 20 | 21 | fun sessionStoreforIntegrationTests(rule: ActivityTestRule<*>): SessionStore { 22 | val prefs = PreferenceManager.getDefaultSharedPreferences(rule.activity) 23 | prefs.edit().putString(BLOCKSTACK_SESSION, A_VALID_BLOCKSTACK_SESSION_JSON) 24 | .apply() 25 | return SessionStore(prefs) 26 | } 27 | -------------------------------------------------------------------------------- /blockstack-sdktest/src/androidTest/res/layout/activity_test.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /blockstack-sdktest/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /blockstack-sdktest/src/main/res/raw/org_blockstack_base64.js: -------------------------------------------------------------------------------- 1 | /* 2 | * base64-arraybuffer 3 | * https://github.com/niklasvh/base64-arraybuffer 4 | * 5 | * Copyright (c) 2012 Niklas von Hertzen 6 | * Licensed under the MIT license. 7 | */ 8 | "use strict"; 9 | var Base64 = {} 10 | var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 11 | 12 | // Use a lookup table to find the index. 13 | var lookup = new Uint8Array(256); 14 | for (var i = 0; i < chars.length; i++) { 15 | lookup[chars.charCodeAt(i)] = i; 16 | } 17 | 18 | Base64.encode = function(arraybuffer) { 19 | var bytes = new Uint8Array(arraybuffer), 20 | i, len = bytes.length, base64 = ""; 21 | 22 | for (i = 0; i < len; i+=3) { 23 | base64 += chars[bytes[i] >> 2]; 24 | base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; 25 | base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; 26 | base64 += chars[bytes[i + 2] & 63]; 27 | } 28 | 29 | if ((len % 3) === 2) { 30 | base64 = base64.substring(0, base64.length - 1) + "="; 31 | } else if (len % 3 === 1) { 32 | base64 = base64.substring(0, base64.length - 2) + "=="; 33 | } 34 | 35 | return base64; 36 | }; 37 | 38 | Base64.decode = function(base64) { 39 | var bufferLength = base64.length * 0.75, 40 | len = base64.length, i, p = 0, 41 | encoded1, encoded2, encoded3, encoded4; 42 | 43 | if (base64[base64.length - 1] === "=") { 44 | bufferLength--; 45 | if (base64[base64.length - 2] === "=") { 46 | bufferLength--; 47 | } 48 | } 49 | 50 | var arraybuffer = new ArrayBuffer(bufferLength), 51 | bytes = new Uint8Array(arraybuffer); 52 | 53 | for (i = 0; i < len; i+=4) { 54 | encoded1 = lookup[base64.charCodeAt(i)]; 55 | encoded2 = lookup[base64.charCodeAt(i+1)]; 56 | encoded3 = lookup[base64.charCodeAt(i+2)]; 57 | encoded4 = lookup[base64.charCodeAt(i+3)]; 58 | 59 | bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); 60 | bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); 61 | bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); 62 | } 63 | 64 | return arraybuffer; 65 | }; 66 | -------------------------------------------------------------------------------- /blockstack-sdktest/src/main/res/raw/org_blockstack_globals.js: -------------------------------------------------------------------------------- 1 | var blockstack={}; 2 | var navigator={userAgent:"Android"}; 3 | var setLocation = (location) => { 4 | android.setLocation(location) 5 | }; 6 | 7 | var crypto = { 8 | // overwritten in blockstack_android.js 9 | getRandomValues : function(array) { 10 | throw Error("not secure getRandomValues") 11 | } 12 | } 13 | 14 | 15 | var global = typeof(global) == 'undefined' ? {Uint8Array:Uint8Array} : global; 16 | if (!global.crypto) { 17 | global.crypto = crypto 18 | }; 19 | 20 | 21 | global.navigator = navigator; 22 | 23 | // window location is needed for redirecting 24 | global.location = { 25 | set href(loc) { 26 | setLocation(loc); 27 | } 28 | }; 29 | 30 | var window = global; 31 | var self = global; 32 | 33 | var module = {}; 34 | var exports = {}; 35 | -------------------------------------------------------------------------------- /blockstack-sdktest/src/main/res/raw/webview.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/blockstack-sdktest/src/main/res/raw/webview.html -------------------------------------------------------------------------------- /blockstack-sdktest/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | blockstacksdktest 3 | 4 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | kotlin_version = '1.4.10' 6 | khex_version = '1.0.0' 7 | khash_version = '1.1.1' 8 | kethereum_version = '0.83.0' 9 | did_jwt_version = '0.4.0' 10 | kbase58_version = '0.1' 11 | kbip44_version = '0.1' 12 | } 13 | 14 | repositories { 15 | google() 16 | mavenCentral() 17 | } 18 | dependencies { 19 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 20 | classpath 'com.android.tools.build:gradle:4.1.0' 21 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' 22 | // NOTE: Do not place your application dependencies here; they belong 23 | // in the individual module build.gradle files 24 | } 25 | } 26 | 27 | plugins { 28 | id "org.jetbrains.dokka-android" version "0.9.17" 29 | id 'de.undercouch.download' version '3.4.3' 30 | } 31 | 32 | 33 | allprojects { 34 | repositories { 35 | google() 36 | mavenCentral() 37 | maven { url 'https://jitpack.io' } 38 | } 39 | } 40 | 41 | ext { 42 | mySigningConfigs = [debug: [ 43 | storeFile : file("config/debug.keystore"), 44 | storePassword: "android", 45 | keyAlias : "androiddebugkey", 46 | keyPassword : "android" 47 | ]] 48 | } 49 | 50 | task clean(type: Delete) { 51 | delete rootProject.buildDir 52 | } 53 | -------------------------------------------------------------------------------- /config/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/config/debug.keystore -------------------------------------------------------------------------------- /docs/images/app-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/docs/images/app-flow.png -------------------------------------------------------------------------------- /docs/images/blockstack-install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/docs/images/blockstack-install.png -------------------------------------------------------------------------------- /docs/images/blockstack-signin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/docs/images/blockstack-signin.png -------------------------------------------------------------------------------- /docs/images/chrome-prompt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/docs/images/chrome-prompt.png -------------------------------------------------------------------------------- /docs/images/configure-activity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/docs/images/configure-activity.png -------------------------------------------------------------------------------- /docs/images/create-restore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/docs/images/create-restore.png -------------------------------------------------------------------------------- /docs/images/final-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/docs/images/final-app.png -------------------------------------------------------------------------------- /docs/images/hello-andriod-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/docs/images/hello-andriod-1.png -------------------------------------------------------------------------------- /docs/images/initial-build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/docs/images/initial-build.png -------------------------------------------------------------------------------- /docs/images/new-interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/docs/images/new-interface.png -------------------------------------------------------------------------------- /docs/images/oreo-api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/docs/images/oreo-api.png -------------------------------------------------------------------------------- /docs/images/running-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/docs/images/running-app.png -------------------------------------------------------------------------------- /docs/images/select-hdw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/docs/images/select-hdw.png -------------------------------------------------------------------------------- /docs/images/studio-download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/docs/images/studio-download.png -------------------------------------------------------------------------------- /docs/images/sync-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/docs/images/sync-project.png -------------------------------------------------------------------------------- /docs/images/sync-success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/docs/images/sync-success.png -------------------------------------------------------------------------------- /example-multi-activities/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /example-multi-activities/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 30 9 | defaultConfig { 10 | applicationId "org.blockstack.android.sample" 11 | minSdkVersion 21 12 | targetSdkVersion 30 13 | versionCode 1 14 | versionName "1.0" 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | } 17 | 18 | signingConfigs { 19 | debug { 20 | storeFile mySigningConfigs.debug.storeFile 21 | storePassword mySigningConfigs.debug.storePassword 22 | keyAlias mySigningConfigs.debug.keyAlias 23 | keyPassword mySigningConfigs.debug.keyPassword 24 | } 25 | } 26 | 27 | buildTypes { 28 | debug { 29 | signingConfig signingConfigs.debug 30 | } 31 | release { 32 | minifyEnabled false 33 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 34 | } 35 | } 36 | 37 | packagingOptions { 38 | exclude 'META-INF/*' 39 | } 40 | 41 | compileOptions { 42 | sourceCompatibility JavaVersion.VERSION_1_8 43 | targetCompatibility JavaVersion.VERSION_1_8 44 | } 45 | } 46 | 47 | dependencies { 48 | implementation fileTree(include: ['*.jar'], dir: 'libs') 49 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 50 | implementation 'androidx.appcompat:appcompat:1.2.0' 51 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4' 52 | implementation 'androidx.preference:preference-ktx:1.1.1' 53 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-beta01" 54 | implementation 'com.google.android.material:material:1.2.1' 55 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9' 56 | testImplementation 'junit:junit:4.13.1' 57 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 58 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 59 | implementation project(':blockstack-sdk') 60 | } 61 | -------------------------------------------------------------------------------- /example-multi-activities/gradle.properties: -------------------------------------------------------------------------------- 1 | kotlin.coroutines=enabled -------------------------------------------------------------------------------- /example-multi-activities/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /example-multi-activities/src/androidTest/java/org/blockstack/android/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android 2 | 3 | import android.app.Application 4 | import androidx.test.core.app.ApplicationProvider 5 | import androidx.test.ext.junit.runners.AndroidJUnit4 6 | import org.junit.Assert.assertEquals 7 | import org.junit.Test 8 | import org.junit.runner.RunWith 9 | 10 | /** 11 | * Instrumented test, which will execute on an Android device. 12 | * 13 | * See [testing documentation](http://d.android.com/tools/testing). 14 | */ 15 | @RunWith(AndroidJUnit4::class) 16 | class ExampleInstrumentedTest { 17 | @Test 18 | fun useAppContext() { 19 | // Context of the app under test. 20 | val appContext = ApplicationProvider.getApplicationContext() 21 | assertEquals("org.blockstack.android.sample", appContext.packageName) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example-multi-activities/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example-multi-activities/src/main/java/org/blockstack/android/DefaultConfig.kt: -------------------------------------------------------------------------------- 1 | package org.blockstack.android 2 | 3 | import android.content.Context 4 | import org.blockstack.android.sdk.AppDetails 5 | import org.blockstack.android.sdk.BaseScope 6 | import org.blockstack.android.sdk.SessionStore 7 | import org.blockstack.android.sdk.getBlockstackSharedPreferences 8 | import org.blockstack.android.sdk.model.toBlockstackConfig 9 | 10 | val defaultConfig = "https://flamboyant-darwin-d11c17.netlify.app".toBlockstackConfig( 11 | arrayOf(BaseScope.StoreWrite.scope, BaseScope.Email.scope)) 12 | val defaultAppDetails = AppDetails("Blockstack Demo Multi Activities", "https://helloblockstack.com/icon-192x192.png") 13 | 14 | class SessionStoreProvider { 15 | companion object { 16 | private var instance: SessionStore? = null 17 | fun getInstance(context: Context): SessionStore { 18 | var sessionStore = instance 19 | if (sessionStore == null) { 20 | sessionStore = SessionStore(context.getBlockstackSharedPreferences()) 21 | instance = sessionStore 22 | } 23 | return sessionStore 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example-multi-activities/src/main/res/drawable-mdpi/default_avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacks-archive/blockstack-android/415df4944d3e73540aab0ed3e9d7a4c93b9aa876/example-multi-activities/src/main/res/drawable-mdpi/default_avatar.png -------------------------------------------------------------------------------- /example-multi-activities/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /example-multi-activities/src/main/res/layout/activity_account.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 21 | 22 | 23 | 24 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /example-multi-activities/src/main/res/layout/activity_cipher.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 21 | 22 | 23 | 24 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /example-multi-activities/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 21 | 22 | 23 | 24 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /example-multi-activities/src/main/res/layout/content_account.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 |