├── .github └── workflows │ ├── android.yml │ └── publish.yml ├── .gitignore ├── README.md ├── build.gradle ├── demo-app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── co │ │ └── thepeer │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── co │ │ │ └── thepeer │ │ │ └── MainActivity.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── co │ └── thepeer │ └── ExampleUnitTest.kt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── java-demo ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── java_demo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── java_demo │ │ │ └── MainActivity.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── com │ └── example │ └── java_demo │ └── ExampleUnitTest.java ├── scripts ├── publish-module.gradle └── publish-root.gradle ├── settings.gradle └── thepeer-android ├── .gitignore ├── build.gradle ├── config └── ktlint.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src ├── androidTest └── java │ └── co │ └── thepeer │ └── sdk │ └── ExampleInstrumentedTest.kt ├── main ├── AndroidManifest.xml ├── java │ └── co │ │ └── thepeer │ │ └── sdk │ │ ├── Thepeer.kt │ │ ├── model │ │ ├── ThepeerEvent.kt │ │ ├── ThepeerParam.kt │ │ └── ThepeerResult.kt │ │ ├── ui │ │ ├── ThepeerResultContract.kt │ │ ├── ThepeerResultListener.kt │ │ └── activity │ │ │ └── ThepeerSdkActivity.kt │ │ └── utils │ │ ├── Environment.kt │ │ ├── Extensions.kt │ │ ├── Logger.kt │ │ ├── ThepeerConstants.kt │ │ ├── ThepeerCurrency.kt │ │ ├── Urls.kt │ │ └── WebInterface.kt └── res │ ├── drawable │ ├── close_icon.xml │ ├── dialog_background.xml │ └── warning.xml │ ├── layout │ ├── fragment_thepeer.xml │ ├── retry_view.xml │ └── sdk_activity.xml │ └── values │ ├── attr.xml │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── test └── java └── co └── thepeer └── sdk ├── MockData.kt └── WebInterfaceTest.kt /.github/workflows/android.yml: -------------------------------------------------------------------------------- 1 | name: Android CI 2 | 3 | on: 4 | 5 | pull_request: 6 | push: 7 | branches: [ "master" ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: set up JDK 11 17 | uses: actions/setup-java@v3 18 | with: 19 | java-version: '11' 20 | distribution: 'temurin' 21 | cache: gradle 22 | 23 | - name: Grant execute permission for gradlew 24 | run: chmod +x gradlew 25 | 26 | - name: Check code quality with Ktlint 27 | run: ./gradlew thepeer-android:ktlint --stacktrace 28 | 29 | - name: Build thepeer-android library 30 | run: ./gradlew thepeer-android:build 31 | 32 | - name: Build Kotlin demo 33 | run: ./gradlew demo-app:build 34 | 35 | - name: Build Java demo 36 | run: ./gradlew java-demo:build 37 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | 3 | on: 4 | release: 5 | # We'll run this workflow when a new GitHub release is created 6 | types: [released] 7 | 8 | jobs: 9 | publish: 10 | name: Release build and publish 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Check out code 14 | uses: actions/checkout@v2 15 | - name: Set up JDK 11 16 | uses: actions/setup-java@v2 17 | with: 18 | distribution: adopt 19 | java-version: 11 20 | 21 | # Get publish version from tag 22 | - id: get_version 23 | uses: battila7/get-version-action@v2 24 | - name: Get version from tag 25 | run: echo ${{ steps.get_version.outputs.version-without-v }} 26 | 27 | 28 | # Builds the release artifacts of the library 29 | - name: Release build 30 | run: ./gradlew :thepeer-android:assembleRelease 31 | 32 | # Generates other artifacts (javadocJar is optional) 33 | # - name: Source jar and dokka 34 | # run: ./gradlew androidSourcesJar javadocJar 35 | 36 | # Runs upload, and then closes & releases the repository 37 | - name: Publish to MavenCentral 38 | run: ./gradlew publishReleasePublicationToSonatypeRepository --max-workers 1 closeAndReleaseSonatypeStagingRepository 39 | env: 40 | OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} 41 | OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} 42 | SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} 43 | SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} 44 | SIGNING_KEY: ${{ secrets.SIGNING_KEY }} 45 | SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }} 46 | PUBLISH_VERSION: ${{ steps.get_version.outputs.version-without-v }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | /.idea/* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Thepeer official Android SDK 2 | 3 | ![CI](https://github.com/thepeerstack/thepeer-android/actions/workflows/android.yml/badge.svg) 4 | 5 | Thepeer Android SDK gives one integration access to all fintech businesses on your Android App 6 | 7 | 8 | 1. Send 9 | 3. Checkout 10 | 4. Direct Charge 11 | 12 | ## Demo 13 | 14 | | Send | Checkout | Direct Charge | 15 | |------------------------ | --------------------------------------|--------------------------------------| 16 | |![Send-shot](https://user-images.githubusercontent.com/16048595/221840767-aafc2abd-475f-4b2b-9c82-a7c2373cfd0e.png) | ![checkout-shot](https://user-images.githubusercontent.com/16048595/221840744-9efad3ca-8d5a-4186-91d4-7e9a3f408bf7.png) | ![direct-charge-shot](https://user-images.githubusercontent.com/16048595/221840760-103db030-471d-4208-aa15-b2d97e2de96b.png)| 17 | 18 | 19 | 20 | ## Setup Implementation 21 | 22 | #### Step 1: Add Dependency 23 | 24 | To your root build.gradle file add: 25 | 26 | ``` 27 | allprojects { 28 | repositories { 29 | mavenCentral() 30 | } 31 | } 32 | ``` 33 | 34 | To your app-level build.gradle file add: 35 | 36 | ```groovy 37 | dependencies { 38 | // ... 39 | implementation "co.thepeer:thepeer-android:[version]" 40 | } 41 | ``` 42 | 43 | #### Step 2: Add Public Key to Manifest 44 | 45 | ``` 46 | 49 | ``` 50 | 51 | #### Step 3: Initialization 52 | 53 | KOTLIN 54 | 55 | ```kotlin 56 | 57 | // initialize Thepeer SDK 58 | override fun onCreate(savedInstanceState: Bundle?) { 59 | super.onCreate(savedInstanceState) 60 | 61 | 62 | // initialize Thepeer SDK 63 | val thepeer = Thepeer.Initiate( 64 | activity = this, 65 | userReference = "YOUR _USER_REFERENCE", 66 | resultListener = resultListener).build() 67 | } 68 | 69 | ``` 70 | 71 | JAVA 72 | 73 | ```java 74 | 75 | // initialize Thepeer SDK 76 | @Override 77 | protected void onCreate(Bundle savedInstanceState) { 78 | super.onCreate(savedInstanceState); 79 | setContentView(R.layout.activity_main); 80 | 81 | // initialize Thepeer SDK 82 | Thepeer thepeer = new Thepeer.Initiate( 83 | getResources().getString(R.string.user_reference), 84 | activity, 85 | new ThepeerResultListener()) 86 | 87 | } 88 | 89 | ``` 90 | | Parameter name | Description | Required | 91 | |------------------------ | --------------------------------------|--------------------------------------| 92 | | `userReference` | The user reference returned by Thepeer API when a user has been indexed |`true`| 93 | 94 | ## Configuration 95 | Every request will require this configuration to initiate a transaction. 96 | 97 | ``` 98 | val config = ThepeerConfig(amount = BigDecimal(100000), currency = "NGN", meta = mapOf()) 99 | 100 | ``` 101 | | Parameter name | Description | Required | 102 | |------------------------ | --------------------------------------|--------------------------------------| 103 | | `amount` | The amount you intend to send and must be pass as an integer in kobo |`true`| 104 | | `currency ` | Currency which can be `"NGN"` or `"USD"` |`true`| 105 | | `meta` | This object should contain additional/optional attributes you would like to have on your transaction response |`false`| 106 | 107 | ## Send 108 | 109 | Initiate the send request by calling the below function 110 | 111 | KOTLIN 112 | 113 | ```kotlin 114 | 115 | val config = ThepeerConfig(amount = BigDecimal(100000), currency = "NGN", meta = mapOf()) 116 | thepeer.send(config = config) 117 | 118 | ``` 119 | 120 | JAVA 121 | 122 | ```java 123 | 124 | thepeer.send(config); 125 | 126 | ``` 127 | 128 | ## Checkout 129 | 130 | Initiate the checkout request by calling the below function 131 | 132 | KOTLIN 133 | 134 | ```kotlin 135 | 136 | val config = ThepeerConfig(amount = BigDecimal(100000), currency = "NGN", meta = mapOf()) 137 | thepeer.checkout("email@gmail.com", config = config) 138 | 139 | ``` 140 | 141 | JAVA 142 | 143 | ```java 144 | 145 | thepeer.checkout("email@gmail.com", config); 146 | 147 | ``` 148 | 149 | ## Direct Charge 150 | 151 | Initiate the Direct Charge request by calling the below function 152 | 153 | KOTLIN 154 | 155 | ```kotlin 156 | val config = ThepeerConfig(amount = BigDecimal(100000), currency = "NGN", meta = mapOf()) 157 | thepeer.directCharge(config) 158 | 159 | ``` 160 | 161 | JAVA 162 | 163 | ```java 164 | 165 | thepeer.directCharge(config); 166 | 167 | ``` 168 | 169 | ## Listener 170 | 171 | Once the request is initiated the SDK will wait from response from the service and notify the App 172 | via `ThepeerResultListener` 173 | KOTLIN 174 | 175 | ```Kotlin 176 | private val resultListener = object : ThepeerResultListener { 177 | override fun onSuccess(response: String) { 178 | // Transaction Successful 179 | Log.v(TAG,response) 180 | 181 | } 182 | 183 | override fun onError(error: Throwable) { 184 | // Transaction Error occured 185 | Log.e(TAG, error.message) 186 | } 187 | 188 | override fun onCancelled() { 189 | // Transaction was cancelled 190 | Log.e(TAG, " Cancelled") 191 | } 192 | 193 | } 194 | 195 | ``` 196 | 197 | JAVA 198 | 199 | ```java 200 | 201 | new ThepeerResultListener() { 202 | 203 | @Override 204 | public void onSuccess(@NonNull String transaction) { 205 | ((TextView) findViewById(R.id.resultText)).setText(transaction.toString()); 206 | } 207 | 208 | @Override 209 | public void onCancelled() { 210 | 211 | } 212 | 213 | @Override 214 | public void onError(@NonNull Throwable error) { 215 | 216 | } 217 | 218 | } 219 | 220 | 221 | ``` 222 | ## Response structure 223 | ```JSON 224 | { 225 | "event": "send.success", 226 | "type": "send.success", 227 | "data": { 228 | "id": "744bdf8f-17a6-46ae-bda1-b348c6d22f89", 229 | "amount": 100000, 230 | "channel": "send", 231 | "refund": false, 232 | "checkout": null, 233 | "user": { 234 | "reference": "73f03de5-1043-4ad1-bc2e-aa4d94ebee4f", 235 | "name": "Doreen Okoh", 236 | "identifier": "doreen", 237 | "identifier_type": "username", 238 | "email": "doreen@okoh.co.uk", 239 | "created_at": "2021-04-19T19:50:26.000000Z", 240 | "updated_at": "2022-02-14T22:58:25.000000Z" 241 | }, 242 | "charge": 1000, 243 | "currency": "NGN", 244 | "mode": "debit", 245 | "reference": "d34dfaebd727e40a8f436a4b43acbf73", 246 | "remark": "food", 247 | "status": "success", 248 | "type": "peer", 249 | "meta": null, 250 | "peer": { 251 | "business": { 252 | "name": "Cash App", 253 | "logo": "https://palaciodepeer.s3.us-east-2.amazonaws.com/business_logos/UJimBqYOu7KQIM3DwCWOuKjkDbBbVLYRuYRTgxKh.png", 254 | "logo_colour": "#77cc33" 255 | }, 256 | "user": { 257 | "name": "Trojan Okoh", 258 | "identifier": "trojan", 259 | "identifier_type": "username" 260 | } 261 | }, 262 | "updated_at": "2023-05-25T12:32:03.000000Z", 263 | "created_at": "2023-05-25T12:32:03.000000Z" 264 | } 265 | } 266 | ``` 267 | ## Support 268 | 269 | If you're having trouble with Thepeer React or your integration, please reach out to us at . We're more than happy to help you out. 270 | 271 | ## Thepeer API References 272 | 273 | - [Thepeer API Docs](https://docs.thepeer.co) 274 | - [Thepeer Dashboard](https://dashboard.thepeer.co) 275 | 276 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | 2 | plugins{ 3 | id 'com.android.application' version '7.4.0' apply false 4 | id 'com.android.library' version '7.4.0' apply false 5 | id 'org.jetbrains.kotlin.android' version '1.8.0' apply false 6 | id 'io.github.gradle-nexus.publish-plugin' version '1.1.0' 7 | } 8 | 9 | 10 | ext { 11 | kotlin_version = '1.7.21' 12 | 13 | //ktlint 14 | ktlintVersion = '0.46.1' 15 | 16 | // Coroutine 17 | coroutineVersion = '1.5.2' 18 | coroutineTestVersion = '1.5.2' 19 | 20 | // OkHTTP 21 | okHttpVersion = '4.9.2' 22 | 23 | // Moshi 24 | moshiVersion = '1.12.0' 25 | moshiAdaptersVersion = '1.12.0' 26 | moshiKotlinVersion = '1.12.0' 27 | 28 | // Mockk 29 | mockkVersion = '1.12.0' 30 | 31 | // Junit 32 | junitVersion = '4.13.2' 33 | 34 | //Navigation graph 35 | navGraph = '2.4.1' 36 | 37 | //lifecycle 38 | lifecycleVersion = '2.5.0-alpha03' 39 | 40 | // Robolectric 41 | robolectricVersion = '4.7.2' 42 | } 43 | 44 | apply from: "${rootDir}/scripts/publish-root.gradle" -------------------------------------------------------------------------------- /demo-app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /demo-app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | id 'kotlin-kapt' 5 | } 6 | 7 | 8 | android { 9 | compileSdkVersion 33 10 | 11 | 12 | defaultConfig { 13 | applicationId "co.thepeer" 14 | minSdkVersion 23 15 | targetSdkVersion 33 16 | versionCode 1 17 | versionName "1.0" 18 | 19 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 20 | } 21 | 22 | buildFeatures { 23 | viewBinding true 24 | } 25 | 26 | buildTypes { 27 | release { 28 | minifyEnabled false 29 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 30 | } 31 | } 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | } 40 | 41 | dependencies { 42 | 43 | implementation project(":thepeer-android") 44 | implementation 'androidx.core:core-ktx:1.7.0' 45 | implementation 'androidx.appcompat:appcompat:1.4.1' 46 | implementation 'com.google.android.material:material:1.5.0' 47 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3' 48 | implementation 'androidx.core:core-ktx:1.10.1' 49 | testImplementation 'junit:junit:4.+' 50 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 51 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 52 | } -------------------------------------------------------------------------------- /demo-app/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 -------------------------------------------------------------------------------- /demo-app/src/androidTest/java/co/thepeer/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package co.thepeer 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("co.thepeer", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /demo-app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /demo-app/src/main/java/co/thepeer/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package co.thepeer 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import co.thepeer.databinding.ActivityMainBinding 6 | import co.thepeer.sdk.Thepeer 7 | import co.thepeer.sdk.model.ThepeerConfig 8 | import co.thepeer.sdk.ui.ThepeerResultListener 9 | import co.thepeer.sdk.utils.ThepeerCurrency 10 | import java.math.BigDecimal 11 | 12 | class MainActivity : AppCompatActivity() { 13 | 14 | private lateinit var binding: ActivityMainBinding 15 | 16 | lateinit var thepeer: Thepeer 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | binding = ActivityMainBinding.inflate(layoutInflater) 20 | setContentView(binding.root) 21 | 22 | // initialize Thepeer SDK 23 | val thepeer = Thepeer.Initiate( 24 | activity = this, 25 | userReference = getString(R.string.user_reference), 26 | resultListener = resultListener, 27 | ).build() 28 | 29 | binding.btnSendMoney.setOnClickListener { 30 | // calling ThePeer SDK 31 | val config = ThepeerConfig(amount = BigDecimal(100000), currency = ThepeerCurrency.NGN) 32 | thepeer.send(config = config) 33 | } 34 | binding.btnCheckout.setOnClickListener { 35 | val config = ThepeerConfig(amount = BigDecimal(100000), currency = ThepeerCurrency.NGN) 36 | thepeer.checkout("email@gmail.com", config = config) 37 | } 38 | binding.btnDirectDebit.setOnClickListener { 39 | val config = ThepeerConfig(amount = BigDecimal(100000), currency = ThepeerCurrency.NGN) 40 | thepeer.directCharge(config) 41 | } 42 | } 43 | 44 | private val resultListener = object : ThepeerResultListener { 45 | override fun onSuccess(response: String) { 46 | binding.resultText.text = response 47 | } 48 | 49 | override fun onError(error: Throwable) { 50 | binding.resultText.text = error.message 51 | } 52 | 53 | override fun onCancelled() { 54 | binding.resultText.text = " Cancelled" 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /demo-app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /demo-app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /demo-app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 |