├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── minijoy
│ │ └── particle_connect_android
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── connect
│ │ │ └── demo
│ │ │ ├── App.kt
│ │ │ ├── base
│ │ │ └── BaseActivity.kt
│ │ │ ├── controller
│ │ │ ├── main
│ │ │ │ ├── AccountAdapter.kt
│ │ │ │ └── MainActivity.kt
│ │ │ ├── manage
│ │ │ │ ├── ConnectAdapter.kt
│ │ │ │ ├── ManageActivity.kt
│ │ │ │ └── WalletConnectFragment.kt
│ │ │ ├── reference
│ │ │ │ └── ReferenceActivity.kt
│ │ │ └── secret
│ │ │ │ └── ImportWalletActivity.kt
│ │ │ ├── custom_connectadapter
│ │ │ └── Coin98ConnectAdapter.kt
│ │ │ ├── model
│ │ │ ├── RpcRequest.kt
│ │ │ ├── RpcResponse.kt
│ │ │ ├── SerializeTransaction.kt
│ │ │ ├── TransactionAddressData.kt
│ │ │ └── WalletAccount.kt
│ │ │ ├── transaction
│ │ │ ├── SolanaRpcRepository.kt
│ │ │ └── SolanaTransactionManager.kt
│ │ │ └── utils
│ │ │ ├── BarcodeEncoder.kt
│ │ │ ├── ChainUtils.kt
│ │ │ ├── CoilLoader.kt
│ │ │ ├── ContextExt.kt
│ │ │ ├── MockManger.kt
│ │ │ ├── QrParams.kt
│ │ │ ├── SolanaRpcApi.kt
│ │ │ └── StreamUtils.kt
│ └── res
│ │ ├── drawable-xxhdpi
│ │ ├── arbitrum.png
│ │ ├── aurora.png
│ │ ├── avalanche.png
│ │ ├── bsc.png
│ │ ├── ethereum.png
│ │ ├── fantom.png
│ │ ├── harmony.png
│ │ ├── heco.png
│ │ ├── ic_logo.png
│ │ ├── kcc.png
│ │ ├── moonbeam.png
│ │ ├── moonriver.png
│ │ ├── optimism.png
│ │ ├── polygon.png
│ │ └── solana.png
│ │ ├── drawable
│ │ ├── edit_bg.xml
│ │ ├── ic_create_wallet.xml
│ │ ├── ic_delete.xml
│ │ ├── ic_edit.xml
│ │ ├── ic_left.xml
│ │ ├── ic_more.xml
│ │ ├── ic_right.xml
│ │ ├── ic_wallet.xml
│ │ ├── list_item_divider.xml
│ │ └── popup_window_transparent.xml
│ │ ├── layout
│ │ ├── activity_import_wallet.xml
│ │ ├── activity_main.xml
│ │ ├── activity_manage.xml
│ │ ├── activity_reference.xml
│ │ ├── edit_wallet_name_layout.xml
│ │ ├── fragment_wallet_connect.xml
│ │ ├── item_account.xml
│ │ └── item_adapter.xml
│ │ ├── menu
│ │ ├── delete_action.xml
│ │ └── toolbar_action.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_foreground.png
│ │ └── ic_launcher_round.png
│ │ ├── raw
│ │ └── typed_data.json
│ │ └── values
│ │ ├── colors.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── com
│ └── minijoy
│ └── particle_connect_android
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.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 | local.properties
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://search.maven.org/artifact/network.particle/connect)
2 |
3 | # Particle Connect Android
4 | The best way to connect a wallet. Support multi chains and multi wallet. Learn more visit [Particle Network](https://docs.particle.network/).
5 |
6 |
7 |
8 |
9 | ## Summary
10 |
11 | Modular Kotlin wallet adapters and components for EVM & Solana chains. Manage wallet and custom RPC request.
12 |
13 | 
14 |
15 | ## Quick Start
16 |
17 | ```
18 | dependencies {
19 | //required dependencies
20 | implementation("network.particle:auth-service:{latest-version}")
21 | implementation("network.particle:connect-common:${latest-version}")
22 | implementation 'network.particle:connect:{latest-version}'
23 |
24 | //Optional: support evm chain Generate & Import wallet
25 | implementation 'network.particle:connect-evm-adapter:{latest-version}'
26 |
27 | //Optional: support solana chain Generate & Import wallet
28 | implementation 'network.particle:connect-solana-adapter:{latest-version}'
29 |
30 | //Optional: support connect Phantom wallet
31 | implementation 'network.particle:connect-phantom-adapter:{latest-version}'
32 |
33 | //Optional: support WalletConnect Protocol, include MetaMask, Rainbow, Trust, imToken etc.
34 | implementation 'network.particle:connect-wallet-connect-adapter:{latest-version}'
35 | }
36 | ```
37 |
38 | Add below config to AndroidManifest.xml
39 |
40 | ```
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
78 |
82 |
86 |
87 |
88 | ```
89 |
90 | Init Particle Connect in `Application#Create()`
91 |
92 | ```
93 | ParticleConnect.init(
94 | this,
95 | Env.DEV, //debug mode
96 | EthereumChain(EthereumChainId.Kovan), //chain info
97 | DAppMetadata(
98 | "Particle Connect",
99 | "https://static.particle.network/wallet-icons/Particle.png",
100 | "https://particle.network"
101 | ) //DApp or Wallet info
102 | ) {
103 | listOf(
104 | ParticleConnectAdapter(),
105 | MetaMaskConnectAdapter(),
106 | RainbowConnectAdapter(),
107 | TrustConnectAdapter(),
108 | ImTokenConnectAdapter(),
109 | BitKeepConnectAdapter(),
110 | WalletConnectAdapter(),
111 | PhantomConnectAdapter(),
112 | EVMConnectAdapter(),
113 | SolanaConnectAdapter(),
114 | ) //list all support adapters, lazy create.
115 | }
116 | ```
117 |
118 | Switch chain.
119 |
120 | ```
121 | ParticleConnect.setChain(chain)
122 | ```
123 |
124 | Get all wallet adapters.
125 |
126 | ```
127 | var adapters = ParticleConnect.getAdapters(chainTypes)
128 | //or
129 | var adapters = ParticleConnect.getAdapterByAddress(address)
130 | ```
131 |
132 | Get all connected accounts.
133 |
134 | ```
135 | val accounts = ParticleConnect.getAccounts(chainTypes)
136 | ```
137 |
138 | Connect wallet. (For `EVMConnectAdapter` or `SolanaConnectAdapter` will generate new wallet)
139 |
140 | ```
141 | connectAdapter.connect(callback)
142 | ```
143 |
144 | Disconnect wallet.
145 |
146 | ```
147 | connectAdapter.disconnect(address, callback)
148 | ```
149 |
150 | Check whether the account is connected.
151 |
152 | ```
153 | val result = connectAdapter.connected(address)
154 | ```
155 |
156 | Import wallet. (Only `EVMConnectAdapter` and `SolanaConnectAdapter` support this method)
157 |
158 | ```
159 | // import wallet with private key
160 | val account = connectAdapter.importWalletFromPrivateKey(privateKey)
161 |
162 | // import wallet with mnemonic(Split with space).
163 | val account = connectAdapter.importWalletFromMnemonic(mnemonic)
164 | ```
165 |
166 | Export wallet. (Only `EVMConnectAdapter` and `SolanaConnectAdapter` support this method)
167 |
168 | ```
169 | val privateKey = connectAdapter.exportWalletPrivateKey(address)
170 | ```
171 |
172 | Sign and send transaction.
173 |
174 | ```
175 | // todo: check connected before sign
176 | connectAdapter.signAndSendTransaction(address, transaction, callback)
177 | ```
178 |
179 | Sign transaction. (Only Solana chain support this method)
180 |
181 | ```
182 | connectAdapter.signTransaction(address, transaction, callback)
183 | ```
184 |
185 | Sign all transactions. (Only Solana chain support this method)
186 |
187 | ```
188 | connectAdapter.signAllTransactions(address, transactions, callback)
189 | ```
190 |
191 | Sign message. (EVM call `personal_sign`)
192 |
193 | ```
194 | connectAdapter.signMessage(address, message, callback)
195 | ```
196 |
197 | Sign typed data. (Only EVM chains support this method)
198 |
199 | ```
200 | connectAdapter.signTypedData(address, data, callback)
201 | ```
202 |
203 | ## Give Feedback
204 |
205 | You can join our [Discord](https://discord.gg/2y44qr6CR2).
206 |
207 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("com.android.application")
3 | id("kotlin-android")
4 | id("kotlin-kapt")
5 | id("kotlin-parcelize")
6 | id("org.jetbrains.kotlin.android")
7 | }
8 |
9 | android {
10 | compileSdk = libs.versions.compileSdk.get().toInt()
11 |
12 | defaultConfig {
13 | applicationId = "com.connect.demo"
14 | minSdk = libs.versions.minSdk.get().toInt()
15 | targetSdk = libs.versions.targetSdk.get().toInt()
16 | versionCode = 1
17 | versionName = "1.0"
18 |
19 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
20 | manifestPlaceholders["PN_PROJECT_ID"] = "34c6b829-5b89-44e8-90a9-6d982787b9c9"
21 | manifestPlaceholders["PN_PROJECT_CLIENT_KEY"] = "c6Z44Ml4TQeNhctvwYgdSv6DBzfjf6t6CB0JDscR"
22 | manifestPlaceholders["PN_APP_ID"] = "e35f880b-5d71-425f-97aa-5f26de7bc4d7"
23 | buildConfigField(
24 | "String",
25 | "PN_API_BASE_URL",
26 | "\"https://api.particle.network\""
27 | )
28 | proguardFiles(
29 | getDefaultProguardFile("proguard-android-optimize.txt"),
30 | "proguard-rules.pro"
31 | )
32 | }
33 |
34 |
35 |
36 | buildTypes {
37 | debug {
38 | isMinifyEnabled = false
39 | proguardFiles(
40 | getDefaultProguardFile("proguard-android-optimize.txt"),
41 | "proguard-rules.pro"
42 | )
43 | }
44 | release {
45 | isMinifyEnabled = true
46 | proguardFiles(
47 | getDefaultProguardFile("proguard-android-optimize.txt"),
48 | "proguard-rules.pro"
49 | )
50 | }
51 | }
52 | compileOptions {
53 | sourceCompatibility(JavaVersion.VERSION_11)
54 | targetCompatibility(JavaVersion.VERSION_11)
55 | }
56 |
57 | kotlinOptions {
58 | jvmTarget = JavaVersion.VERSION_11.toString()
59 | }
60 | dataBinding {
61 | isEnabled = true
62 | }
63 | namespace = "com.connect.demo"
64 | }
65 |
66 |
67 | dependencies {
68 | modules {
69 | module("org.bouncycastle:bcprov-jdk15to18") {
70 | replacedBy("org.bouncycastle:bcprov-jdk15on")
71 | }
72 | module("org.bouncycastle:bcprov-jdk18on") {
73 | replacedBy("org.bouncycastle:bcprov-jdk15on")
74 | }
75 | }
76 | //required dependencies
77 | implementation(libs.particle.auth) // deprecated use auth-core-service instead
78 | implementation(libs.particle.auth.core)
79 | implementation(libs.particle.api)
80 | implementation(libs.connect.common)
81 | implementation(libs.connect)
82 | implementation(libs.connect.kit)
83 | //optional dependencies
84 | implementation(libs.connect.auth.adapter)// deprecated use auth-core-adapter instead
85 | implementation(libs.connect.auth.core.adapter)
86 | implementation(libs.connect.evm.adapter)
87 | implementation(libs.connect.sol.adapter)
88 | implementation(libs.connect.phantom.adapter)
89 | implementation(libs.connect.wallet.connect.adapter)
90 |
91 | implementation(libs.appcompat)
92 | implementation(libs.core.ktx)
93 | implementation(libs.material)
94 | implementation(libs.utilcodex)
95 | implementation(libs.constraintlayout)
96 | implementation(libs.recyclerview)
97 | implementation(libs.bundles.retrofit)
98 | implementation(libs.bundles.okhttp3)
99 | implementation(libs.recyclerview.adapter)
100 | implementation(libs.coil)
101 | implementation(libs.coil.svg)
102 | implementation(libs.coil.gif)
103 | implementation(libs.zxing.barcodescanner)
104 |
105 | testImplementation(libs.junit)
106 | androidTestImplementation(libs.junit.ext)
107 | androidTestImplementation(libs.espresso.core)
108 |
109 | }
--------------------------------------------------------------------------------
/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
22 | -keepclassmembers enum com.connect.demo.** { *; }
23 | -keep class com.connect.demo.** { *; }
24 | -dontwarn com.connect.demo.**
25 | -repackageclasses com.connect.demo
26 |
27 | -keep class androidx.databinding.DataBindingComponent {*;}
28 | -keepclassmembers class **.R$* {
29 | public static ;
30 | }
31 |
32 | -keepattributes Signature
33 | -keepattributes SourceFile,LineNumberTable
34 | -renamesourcefileattribute SourceFile
35 |
36 | -keep class com.chad.library.adapter.** {
37 | *;
38 | }
39 | -keep public class * extends com.chad.library.adapter.base.BaseQuickAdapter
40 | -keep public class * extends com.chad.library.adapter.base.viewholder.BaseViewHolder
41 | -keepclassmembers class **$** extends com.chad.library.adapter.base.viewholder.BaseViewHolder {
42 | (...);
43 | }
44 | -keepattributes InnerClasses
45 |
46 | -keep class androidx.** {*;}
47 | -keep public class * extends androidx.**
48 | -keep interface androidx.** {*;}
49 | -dontwarn androidx.**
50 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/minijoy/particle_connect_android/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.minijoy.particle_connect_android
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("com.minijoy.particle_connect_android", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | ...
7 |
8 |
9 |
10 |
20 |
25 |
29 |
33 |
37 |
38 |
39 |
40 |
41 |
42 |
43 | >
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
95 |
98 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/App.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo
2 |
3 | import android.app.Application
4 | import auth.core.adapter.AuthCoreAdapter
5 | import com.connect.demo.utils.CoilLoader
6 | import com.evm.adapter.EVMConnectAdapter
7 | import com.particle.base.Env
8 | import com.particle.base.model.DAppMetadata
9 | import com.particle.connect.ParticleConnect
10 | import com.phantom.adapter.PhantomConnectAdapter
11 | import com.solana.adapter.SolanaConnectAdapter
12 | import com.wallet.connect.adapter.BitGetConnectAdapter
13 | import com.wallet.connect.adapter.ImTokenConnectAdapter
14 | import com.wallet.connect.adapter.MetaMaskConnectAdapter
15 | import com.wallet.connect.adapter.OKXConnectAdapter
16 | import com.wallet.connect.adapter.RainbowConnectAdapter
17 | import com.wallet.connect.adapter.TrustConnectAdapter
18 | import com.wallet.connect.adapter.WalletConnectAdapter
19 | import network.particle.chains.ChainInfo.Companion.EthereumSepolia
20 | import particle.auth.adapter.ParticleConnectAdapter
21 |
22 | /**
23 | * Created by chaichuanfa on 2022/7/15
24 | */
25 | class App : Application() {
26 |
27 | private lateinit var instance: App
28 |
29 | override fun onCreate() {
30 | super.onCreate()
31 | instance = this
32 | CoilLoader.init(this)
33 |
34 | ParticleConnect.init(
35 | this, Env.DEV, EthereumSepolia, DAppMetadata(
36 | walletConnectProjectId = "f431aaea6e4dea6a669c0496f9c009c1",
37 | name = "Particle Connect",
38 | icon = "https://connect.particle.network/icons/512.png",
39 | url = "https://particle.network",
40 | description = "Particle Connect is a decentralized wallet connection solution that allows users to connect to DApps with their wallets.",
41 | redirect = "redirect://",
42 | verifyUrl = "verifyUrl",
43 | )
44 | ) {
45 | listOf(
46 | AuthCoreAdapter(),
47 | MetaMaskConnectAdapter(),
48 | RainbowConnectAdapter(),
49 | TrustConnectAdapter(),
50 | ImTokenConnectAdapter(),
51 | BitGetConnectAdapter(),
52 | WalletConnectAdapter(),
53 | PhantomConnectAdapter(),
54 | EVMConnectAdapter(),
55 | SolanaConnectAdapter(),
56 | OKXConnectAdapter()
57 | )
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/base/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.base
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import androidx.databinding.DataBindingUtil
6 | import androidx.databinding.ViewDataBinding
7 |
8 | /**
9 | * Created by chaichuanfa on 2022/7/25
10 | */
11 | open class BaseActivity(private val layoutId: Int) : AppCompatActivity() {
12 |
13 | internal lateinit var binding: BD
14 | private set
15 |
16 | override fun onCreate(savedInstanceState: Bundle?) {
17 | super.onCreate(savedInstanceState)
18 | binding = DataBindingUtil.setContentView(this, layoutId)
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/controller/main/AccountAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.controller.main
2 |
3 | import coil.load
4 | import com.chad.library.adapter.base.BaseQuickAdapter
5 | import com.chad.library.adapter.base.viewholder.BaseDataBindingHolder
6 | import com.connect.demo.R
7 | import com.connect.demo.databinding.ItemAccountBinding
8 | import com.connect.demo.model.WalletAccount
9 |
10 | /**
11 | * Created by chaichuanfa on 2022/7/25
12 | */
13 | class AccountAdapter :
14 | BaseQuickAdapter>(R.layout.item_account) {
15 |
16 | init {
17 | addChildClickViewIds(R.id.edit_account)
18 | }
19 |
20 | override fun convert(holder: BaseDataBindingHolder, item: WalletAccount) {
21 | try {
22 | holder.dataBinding?.apply {
23 | name.text = item.name
24 | address.text = item.account.publicAddress
25 | icon.load(item.account.icons?.get(0))
26 | }
27 | } catch (e: Exception) {
28 | e.printStackTrace()
29 | }
30 |
31 | }
32 |
33 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/controller/main/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.controller.main
2 |
3 | import android.content.ClipData
4 | import android.content.ClipboardManager
5 | import android.content.Intent
6 | import android.os.Bundle
7 | import android.util.Log
8 | import android.widget.EditText
9 | import androidx.appcompat.app.AlertDialog
10 | import androidx.lifecycle.lifecycleScope
11 | import androidx.recyclerview.widget.DividerItemDecoration
12 | import androidx.recyclerview.widget.LinearLayoutManager
13 | import com.connect.common.ConnectKitCallback
14 | import com.connect.common.DisconnectCallback
15 | import com.connect.common.ILocalAdapter
16 | import com.connect.common.model.Account
17 | import com.connect.common.model.ConnectError
18 | import com.connect.common.utils.PrefUtils
19 | import com.connect.demo.R
20 | import com.connect.demo.base.BaseActivity
21 | import com.connect.demo.controller.manage.ManageActivity
22 | import com.connect.demo.controller.reference.ReferenceActivity
23 | import com.connect.demo.databinding.ActivityMainBinding
24 | import com.connect.demo.model.WalletAccount
25 | import com.connect.demo.utils.ChainUtils
26 | import com.connect.demo.utils.MockManger
27 | import com.connect.demo.utils.toast
28 | import com.particle.connect.ParticleConnect
29 | import com.particle.connectkit.AdditionalLayoutOptions
30 | import com.particle.connectkit.ConnectKitConfig
31 | import com.particle.connectkit.ConnectOption
32 | import com.particle.connectkit.EnableSocialProvider
33 | import com.particle.connectkit.EnableWallet
34 | import com.particle.connectkit.EnableWalletLabel
35 | import com.particle.connectkit.EnableWalletProvider
36 | import com.particle.connectkit.ParticleConnectKit
37 | import kotlinx.coroutines.launch
38 | import network.particle.chains.ChainInfo
39 |
40 |
41 | class MainActivity : BaseActivity(R.layout.activity_main) {
42 |
43 | private var selectChain = 1
44 |
45 | private val adapter: AccountAdapter = AccountAdapter()
46 |
47 | override fun onCreate(savedInstanceState: Bundle?) {
48 | super.onCreate(savedInstanceState)
49 | setupData()
50 | setupToolbar()
51 | setupChangeChain()
52 | setupRv()
53 | }
54 |
55 | override fun onResume() {
56 | super.onResume()
57 | refreshAccount()
58 | }
59 |
60 | private fun setupData() {
61 | selectChain = PrefUtils.getSettingInt("current_selected_chain", 1)
62 | }
63 |
64 | private fun setupToolbar() {
65 | binding.toolbar.inflateMenu(R.menu.toolbar_action)
66 | binding.toolbar.setOnMenuItemClickListener { item ->
67 | when (item.itemId) {
68 | R.id.connect -> {
69 | startActivity(Intent(this, ManageActivity::class.java))
70 | true
71 | }
72 | R.id.connectKit->{
73 | val config = ConnectKitConfig(
74 | logo = "",
75 | connectOptions = listOf(
76 | ConnectOption.EMAIL,
77 | ConnectOption.PHONE,
78 | ConnectOption.SOCIAL,
79 | ConnectOption.WALLET),
80 | socialProviders = listOf(
81 | EnableSocialProvider.GOOGLE,
82 | EnableSocialProvider.APPLE,
83 | EnableSocialProvider.DISCORD,
84 | EnableSocialProvider.TWITTER,
85 | EnableSocialProvider.FACEBOOK,
86 | EnableSocialProvider.GITHUB,
87 | EnableSocialProvider.MICROSOFT,
88 | EnableSocialProvider.TWITCH,
89 | EnableSocialProvider.LINKEDIN),
90 | walletProviders = listOf(
91 | EnableWalletProvider(EnableWallet.MetaMask, EnableWalletLabel.RECOMMENDED),
92 | EnableWalletProvider(EnableWallet.OKX),
93 | EnableWalletProvider(EnableWallet.Phantom),
94 | EnableWalletProvider(EnableWallet.Trust),
95 | EnableWalletProvider(EnableWallet.Bitget),
96 | EnableWalletProvider(EnableWallet.WalletConnect),
97 | ),
98 | additionalLayoutOptions = AdditionalLayoutOptions(
99 | isCollapseWalletList = false,
100 | isSplitEmailAndSocial = false,
101 | isSplitEmailAndPhone = false,
102 | isHideContinueButton = false
103 | )
104 | )
105 | ParticleConnectKit.connect(config,connectCallback = object :
106 | ConnectKitCallback {
107 | override fun onConnected(walletName: String, account: Account) {
108 | refreshAccount()
109 | }
110 |
111 | override fun onError(error: ConnectError) {
112 | }
113 |
114 | });
115 | true
116 | }
117 | else -> false
118 | }
119 | }
120 |
121 | updateCurrentChain(ChainUtils.getAllChains()[selectChain])
122 | }
123 |
124 | private fun setupRv() {
125 | binding.accountRv.layoutManager = LinearLayoutManager(this)
126 | binding.accountRv.addItemDecoration(
127 | DividerItemDecoration(
128 | this,
129 | DividerItemDecoration.VERTICAL
130 | )
131 | )
132 | binding.accountRv.adapter = adapter
133 | adapter.setOnItemClickListener { _, _, position ->
134 | val walletAccount = adapter.data[position]
135 | if (walletAccount.connectAdapter.supportChains.contains(ParticleConnect.chainType)) {
136 | MockManger.walletAccount = walletAccount
137 | startActivity(Intent(this, ReferenceActivity::class.java))
138 | } else {
139 | toast("The wallet not support current chain")
140 | }
141 | }
142 | adapter.setOnItemChildClickListener { _, _, position ->
143 | val walletAccount = adapter.data[position]
144 | showAccountMenu(walletAccount)
145 | }
146 | }
147 |
148 | private fun showAccountMenu(walletAccount: WalletAccount) {
149 | AlertDialog.Builder(this).apply {
150 | setIcon(R.drawable.ic_logo)
151 | setTitle(walletAccount.name)
152 | val items = mutableListOf("Copy address", "Rename wallet", "Disconnect")
153 | if (!walletAccount.account.mnemonic.isNullOrEmpty() || walletAccount.connectAdapter is ILocalAdapter) {
154 | items.add("Export Wallet")
155 | }
156 |
157 | setItems(items.toTypedArray()) { _, which ->
158 | when (which) {
159 | 0 -> copyWalletAddress(walletAccount)
160 | 1 -> renameWallet(walletAccount)
161 | 2 -> disconnectWallet(walletAccount)
162 | 3 -> exportWallet(walletAccount)
163 | }
164 | }
165 | setNegativeButton("Cancel", null)
166 | create().show()
167 | }
168 | }
169 |
170 | private fun copyWalletAddress(walletAccount: WalletAccount) {
171 | val cm = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
172 | cm.setPrimaryClip(ClipData.newPlainText(packageName, walletAccount.account.publicAddress))
173 | toast("Copied to clipboard")
174 | }
175 |
176 | private fun renameWallet(walletAccount: WalletAccount) {
177 | AlertDialog.Builder(this).apply {
178 | setIcon(R.drawable.ic_logo)
179 | setTitle("Wallet Name")
180 | val view = layoutInflater.inflate(R.layout.edit_wallet_name_layout, null)
181 | val editView = view.findViewById(R.id.edit)
182 | editView.setText(walletAccount.name)
183 | setView(view)
184 | setNegativeButton("Cancel", null)
185 | setPositiveButton("OK") { _, _ ->
186 | val name = editView.text.trim()
187 | if (name.isNotEmpty()) {
188 | PrefUtils.setSettingString(
189 | "${walletAccount.account.publicAddress}_wallet_name",
190 | name.toString()
191 | )
192 | refreshAccount()
193 | }
194 | }
195 | create().show()
196 | }
197 | }
198 |
199 | private fun disconnectWallet(walletAccount: WalletAccount) {
200 | val address = walletAccount.account.publicAddress
201 | walletAccount.connectAdapter.disconnect(
202 | address,
203 | object : DisconnectCallback {
204 | override fun onDisconnected() {
205 | toast("Wallet Disconnected")
206 | PrefUtils.remove("${address}_wallet_name")
207 | refreshAccount()
208 | }
209 |
210 | override fun onError(error: ConnectError) {
211 | toast(error.message)
212 | }
213 | })
214 | }
215 |
216 | private fun exportWallet(walletAccount: WalletAccount) {
217 | lifecycleScope.launch {
218 | var message = ""
219 | val mnemonic = walletAccount.account.mnemonic
220 | if (!mnemonic.isNullOrEmpty()) {
221 | message += "Mnemonic:\n$mnemonic\n\n"
222 | }
223 | if (walletAccount.connectAdapter is ILocalAdapter) {
224 | val privateKey =
225 | walletAccount.connectAdapter.exportWalletPrivateKey(walletAccount.account.publicAddress)
226 | if (privateKey.isNullOrEmpty()) {
227 | toast("Export Private Key Error")
228 | } else {
229 | message += "Private Key:\n$privateKey\n"
230 | Log.d("privateKey", privateKey)
231 | }
232 | }
233 | AlertDialog.Builder(this@MainActivity).apply {
234 | setIcon(R.drawable.ic_logo)
235 | setTitle(walletAccount.name)
236 | setMessage(message)
237 | setPositiveButton("OK", null)
238 | create().show()
239 | }
240 | }
241 | }
242 |
243 | private fun refreshAccount() {
244 | val adapterAccounts = ParticleConnect.getAccounts()
245 | val walletAccounts = mutableListOf()
246 | adapterAccounts.forEach { adapterAccount ->
247 | adapterAccount.accounts.forEach {
248 | val name =
249 | PrefUtils.getSettingString(
250 | "${it.publicAddress}_wallet_name",
251 | "${it.name} Wallet"
252 | )!!
253 | walletAccounts.add(WalletAccount(name, it, adapterAccount.connectAdapter))
254 | }
255 | }
256 | adapter.setList(walletAccounts)
257 | }
258 |
259 | private fun updateCurrentChain(chain: ChainInfo) {
260 | val name = chain.name
261 | binding.chainName.text = name
262 | binding.chainId.text = chain.id.toString()
263 | binding.chainName.setCompoundDrawablesRelativeWithIntrinsicBounds(
264 | resources.getIdentifier(
265 | name.lowercase(),
266 | "drawable",
267 | packageName
268 | ), 0, 0, 0
269 | )
270 |
271 | ParticleConnect.setChain(chain)
272 | refreshAccount()
273 | }
274 |
275 | private fun setupChangeChain() {
276 | binding.changeChain.setOnClickListener {
277 | val alertDialog = AlertDialog.Builder(this)
278 | alertDialog.setIcon(R.drawable.ic_logo)
279 | alertDialog.setTitle("Choose Chain")
280 | val listItems =
281 | ChainUtils.getAllChains().map {
282 | it.fullname + "-" + it.id.toString()
283 | }.toTypedArray()
284 |
285 | alertDialog.setSingleChoiceItems(listItems, selectChain) { dialog, which ->
286 | selectChain = which
287 | updateCurrentChain(ChainUtils.getAllChains()[which])
288 | PrefUtils.setSettingInt("current_selected_chain", selectChain)
289 | dialog.dismiss()
290 | }
291 | alertDialog.setNegativeButton("Cancel", null)
292 | alertDialog.create().show()
293 | }
294 | }
295 |
296 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/controller/manage/ConnectAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.controller.manage
2 |
3 | import coil.load
4 | import com.chad.library.adapter.base.BaseQuickAdapter
5 | import com.chad.library.adapter.base.viewholder.BaseDataBindingHolder
6 | import com.connect.common.IConnectAdapter
7 | import com.connect.demo.R
8 | import com.connect.demo.databinding.ItemAdapterBinding
9 |
10 | /**
11 | * Created by chaichuanfa on 2022/7/25
12 | */
13 | class ConnectAdapter :
14 | BaseQuickAdapter>(R.layout.item_adapter) {
15 |
16 | override fun convert(holder: BaseDataBindingHolder, item: IConnectAdapter) {
17 | holder.dataBinding?.apply {
18 | icon.load(item.icon)
19 | name.text = item.name
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/controller/manage/ManageActivity.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.controller.manage
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import androidx.appcompat.app.AlertDialog
6 | import androidx.recyclerview.widget.DividerItemDecoration
7 | import androidx.recyclerview.widget.LinearLayoutManager
8 | import auth.core.adapter.ConnectConfigPhone
9 | import com.blankj.utilcode.util.LogUtils
10 | import com.connect.common.ConnectCallback
11 | import com.connect.common.IConnectAdapter
12 | import com.connect.common.model.Account
13 | import com.connect.common.model.ConnectError
14 | import com.connect.common.utils.PrefUtils
15 | import com.connect.demo.R
16 | import com.connect.demo.base.BaseActivity
17 | import com.connect.demo.controller.secret.ImportWalletActivity
18 | import com.connect.demo.databinding.ActivityManageBinding
19 | import com.connect.demo.utils.ChainUtils
20 | import com.connect.demo.utils.MockManger
21 | import com.connect.demo.utils.toast
22 | import com.evm.adapter.EVMConnectAdapter
23 | import com.particle.connect.ParticleConnect
24 | import com.phantom.adapter.PhantomConnectAdapter
25 | import com.solana.adapter.SolanaConnectAdapter
26 | import com.wallet.connect.adapter.TrustConnectAdapter
27 | import com.wallet.connect.adapter.WalletConnectAdapter
28 | import network.particle.chains.ChainInfo
29 |
30 | class ManageActivity : BaseActivity(R.layout.activity_manage) {
31 |
32 | private val adapter = ConnectAdapter()
33 |
34 | override fun onCreate(savedInstanceState: Bundle?) {
35 | super.onCreate(savedInstanceState)
36 | setupRv()
37 | setupToolbar()
38 | }
39 |
40 | private fun setupToolbar() {
41 | binding.toolbar.setNavigationOnClickListener { finish() }
42 | setSubTitle()
43 | }
44 |
45 | lateinit var chainInfo: ChainInfo
46 | private fun setSubTitle() {
47 | val selectChain = PrefUtils.getSettingInt("current_selected_chain", 1)
48 | chainInfo = ChainUtils.getAllChains()[selectChain]
49 | val name = chainInfo.fullname
50 | binding.toolbar.subtitle = "Current Chain: $name"
51 | }
52 |
53 | private fun setupRv() {
54 | binding.adapterRv.layoutManager = LinearLayoutManager(this)
55 | binding.adapterRv.addItemDecoration(
56 | DividerItemDecoration(
57 | this,
58 | DividerItemDecoration.VERTICAL
59 | )
60 | )
61 | binding.adapterRv.setHasFixedSize(true)
62 |
63 | adapter.setOnItemClickListener { _, _, position ->
64 | val connectAdapter = adapter.data[position]
65 | connectWallet(connectAdapter)
66 | }
67 | binding.adapterRv.adapter = adapter
68 | adapter.setList(ParticleConnect.getAdapters())
69 | }
70 |
71 | private var qrDialog: WalletConnectFragment? = null
72 | private fun connectWallet(connectAdapter: IConnectAdapter) {
73 | when (connectAdapter) {
74 | is SolanaConnectAdapter -> {
75 | showImportMenu(connectAdapter, "solana")
76 | }
77 |
78 | is EVMConnectAdapter -> {
79 | showImportMenu(connectAdapter, "evm")
80 | }
81 |
82 | is WalletConnectAdapter -> {
83 | connectAdapter.connect(null, object : ConnectCallback {
84 | override fun onConnected(account: Account) {
85 | toast("connect success")
86 | qrDialog?.dismissAllowingStateLoss()
87 | finish()
88 | }
89 |
90 | override fun onError(error: ConnectError) {
91 | toast(error.message)
92 | qrDialog?.dismissAllowingStateLoss()
93 | }
94 | })
95 | val url = connectAdapter.qrCodeUri()
96 | url?.let {
97 | qrDialog = WalletConnectFragment.show(supportFragmentManager, it)
98 | }
99 | }
100 |
101 | else -> {
102 | if (connectCheck(connectAdapter)) {
103 | return
104 | }
105 | connectAdapter.connect(ConnectConfigPhone(), object : ConnectCallback {
106 | override fun onConnected(account: Account) {
107 | LogUtils.d("connect success account: $account")
108 | toast("connect success")
109 | finish()
110 | }
111 |
112 | override fun onError(error: ConnectError) {
113 | LogUtils.d("connect error",Thread.currentThread())
114 | LogUtils.d("connect error: $error")
115 | }
116 | })
117 | }
118 | }
119 | }
120 |
121 | private fun connectCheck(connectAdapter: IConnectAdapter): Boolean {
122 |
123 | if (connectAdapter is PhantomConnectAdapter) {
124 | if (chainInfo.isEvmChain()) {
125 | toast("Phantom only support Solana Chain,current is ${chainInfo.name} ${chainInfo.id}")
126 | return true
127 | }
128 | }
129 | if (connectAdapter is TrustConnectAdapter) {
130 | return if (chainInfo.isEvmChain()) {
131 | if (chainInfo.isMainnet()) {
132 | false
133 | } else {
134 | toast("Trust only support EVM Mainnet Chain,current is ${chainInfo.name} ${chainInfo.id}")
135 | true
136 | }
137 | } else {
138 | toast("Trust only support EVM Chain,current is ${chainInfo.name} ${chainInfo.id}")
139 | true
140 | }
141 | }
142 | return false
143 | }
144 |
145 | private fun showImportMenu(connectAdapter: IConnectAdapter, chainType: String) {
146 | val alertDialog = AlertDialog.Builder(this)
147 | alertDialog.setIcon(R.drawable.ic_logo)
148 | alertDialog.setTitle("Particle Connect")
149 |
150 | alertDialog.setItems(
151 | arrayOf(
152 | "Import ${chainType.uppercase()} Wallet",
153 | "Create ${chainType.uppercase()} Wallet"
154 | )
155 | ) { _, which ->
156 | if (which == 0) {
157 | val intent = Intent(this, ImportWalletActivity::class.java)
158 | intent.putExtra("chainType", chainType)
159 | startActivity(intent)
160 | } else {
161 | createWallet(connectAdapter)
162 | }
163 | }
164 |
165 | alertDialog.setNegativeButton("Cancel", null)
166 | alertDialog.create().show()
167 | }
168 |
169 | private fun createWallet(connectAdapter: IConnectAdapter) {
170 | connectAdapter.connect(null, object : ConnectCallback {
171 | override fun onConnected(account: Account) {
172 | toast("Create wallet success")
173 | finish()
174 | }
175 |
176 | override fun onError(error: ConnectError) {
177 | toast(error.message)
178 | }
179 | })
180 | }
181 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/controller/manage/WalletConnectFragment.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.controller.manage
2 |
3 | import android.content.Intent
4 | import android.graphics.Bitmap
5 | import android.net.Uri
6 | import android.os.Bundle
7 | import android.os.Parcelable
8 | import android.view.LayoutInflater
9 | import android.view.View
10 | import android.view.ViewGroup
11 | import androidx.annotation.Keep
12 | import androidx.core.content.ContextCompat
13 | import androidx.fragment.app.DialogFragment
14 | import androidx.fragment.app.FragmentManager
15 | import androidx.lifecycle.lifecycleScope
16 | import com.connect.common.ConnectManager
17 |
18 | import com.connect.demo.R
19 | import com.connect.demo.databinding.FragmentWalletConnectBinding
20 | import com.connect.demo.utils.BarcodeEncoder
21 | import com.connect.demo.utils.QrParams
22 | import com.google.zxing.BarcodeFormat
23 | import com.particle.base.ParticleNetwork
24 | import kotlinx.coroutines.Dispatchers
25 | import kotlinx.coroutines.launch
26 | import kotlinx.coroutines.withContext
27 | import kotlinx.parcelize.Parcelize
28 |
29 | @Keep
30 | @Parcelize
31 | data class ReceiveData(
32 | val tokenAddress: String?,
33 | ) : Parcelable
34 |
35 | internal class WalletConnectFragment : DialogFragment() {
36 |
37 | companion object {
38 | private const val DATA_KEY: String = "DATA_KEY"
39 |
40 | fun show(fm: FragmentManager, data: String): WalletConnectFragment {
41 | val frag = WalletConnectFragment()
42 | val bundle = Bundle()
43 | bundle.putString(DATA_KEY, data)
44 | frag.arguments = bundle
45 | frag.show(fm, WalletConnectFragment::javaClass.name)
46 | return frag
47 | }
48 |
49 |
50 | }
51 |
52 | lateinit var binding: FragmentWalletConnectBinding
53 | override fun onCreateView(
54 | inflater: LayoutInflater,
55 | container: ViewGroup?,
56 | savedInstanceState: Bundle?
57 | ): View? {
58 | binding = FragmentWalletConnectBinding.inflate(inflater, container, false)
59 | return binding.root
60 | }
61 |
62 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
63 | super.onViewCreated(view, savedInstanceState)
64 | requireArguments().getString(DATA_KEY)?.let { uri->
65 | binding.connect.setOnClickListener {
66 | val intent = Intent(Intent.ACTION_VIEW)
67 | intent.data = Uri.parse(uri)
68 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
69 | ConnectManager.context.startActivity(intent)
70 | }
71 | lifecycleScope.launch {
72 | try {
73 | val qr = generateQrCode(uri)
74 | binding.ivQrCode.setImageBitmap(qr)
75 | } catch (e: Throwable) {
76 | e.printStackTrace()
77 | }
78 | }
79 | }
80 | }
81 |
82 | suspend fun generateQrCode(address: String): Bitmap = withContext(Dispatchers.Default) {
83 | BarcodeEncoder.encodeBitmap(
84 | address,
85 | BarcodeFormat.QR_CODE,
86 | 900,
87 | 900,
88 | QrParams(
89 | ContextCompat.getColor(ParticleNetwork.context, R.color.black),
90 | ContextCompat.getColor(ParticleNetwork.context, R.color.white)
91 | )
92 | )
93 | }
94 |
95 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/controller/reference/ReferenceActivity.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.controller.reference
2 |
3 | import android.annotation.SuppressLint
4 | import android.os.Bundle
5 | import android.text.method.ScrollingMovementMethod
6 | import android.util.Log
7 | import androidx.lifecycle.lifecycleScope
8 | import com.blankj.utilcode.util.GsonUtils
9 | import com.blankj.utilcode.util.LogUtils
10 | import com.connect.common.*
11 | import com.connect.common.eip4361.Eip4361Message
12 | import com.connect.common.eip4361.Web3jSignatureVerifier
13 | import com.connect.common.model.*
14 | import com.connect.common.utils.PrefUtils
15 | import com.connect.demo.R
16 | import com.connect.demo.base.BaseActivity
17 | import com.connect.demo.databinding.ActivityReferenceBinding
18 | import com.connect.demo.model.WalletAccount
19 | import com.connect.demo.utils.ChainUtils
20 | import com.connect.demo.utils.MockManger
21 | import com.connect.demo.utils.StreamUtils
22 | import com.connect.demo.utils.toast
23 | import com.particle.api.evm
24 | import com.particle.api.service.data.ContractParams
25 | import com.particle.base.ParticleNetwork
26 | import com.particle.base.model.ChainType
27 | import com.particle.base.model.EVMTransactionUtil
28 | import com.particle.base.model.ITxData
29 | import com.particle.base.model.LegacyTransactionData
30 | import com.particle.connect.ParticleConnect
31 | import kotlinx.coroutines.launch
32 | import network.particle.chains.NativeCurrency
33 |
34 | class ReferenceActivity : BaseActivity(R.layout.activity_reference) {
35 |
36 | private lateinit var walletAccount: WalletAccount
37 |
38 |
39 | override fun onCreate(savedInstanceState: Bundle?) {
40 | super.onCreate(savedInstanceState)
41 | walletAccount = MockManger.walletAccount!!
42 | setupUI()
43 | }
44 |
45 | override fun onDestroy() {
46 | super.onDestroy()
47 | MockManger.walletAccount = null
48 | }
49 |
50 | @SuppressLint("SetTextI18n")
51 | private fun setupUI() {
52 | binding.toolbar.setNavigationOnClickListener {
53 | finish()
54 | }
55 | binding.toolbar.title = walletAccount.name
56 | binding.toolbar.inflateMenu(R.menu.delete_action)
57 | binding.toolbar.setOnMenuItemClickListener {
58 | walletAccount.connectAdapter.disconnect(walletAccount.account.publicAddress,
59 | object : DisconnectCallback {
60 | override fun onDisconnected() {
61 | finish()
62 | }
63 |
64 | override fun onError(error: ConnectError) {
65 | toast(error.message)
66 | }
67 |
68 | })
69 | true
70 | }
71 |
72 | binding.address.text = "Address: ${walletAccount.account.publicAddress}"
73 | binding.signAndSendTransaction.setOnClickListener {
74 | if (walletAccount.connectAdapter.connected(walletAccount.account.publicAddress)) {
75 | lifecycleScope.launch {
76 | try {
77 | signAndSendTransaction()
78 | } catch (e: Exception) {
79 | e.printStackTrace()
80 | }
81 | }
82 | } else {
83 | connectWallet()
84 | }
85 | }
86 | binding.signTransaction.setOnClickListener {
87 | if (walletAccount.connectAdapter.connected(walletAccount.account.publicAddress)) {
88 | lifecycleScope.launch {
89 | try {
90 | signTransaction()
91 | } catch (e: Exception) {
92 | e.printStackTrace()
93 | }
94 | }
95 | } else {
96 | connectWallet()
97 | }
98 | }
99 |
100 | binding.signAllTransactions.setOnClickListener {
101 | if (walletAccount.connectAdapter.connected(walletAccount.account.publicAddress)) {
102 | lifecycleScope.launch {
103 | try {
104 | signAllTransactions()
105 | } catch (e: Exception) {
106 | e.printStackTrace()
107 | }
108 | }
109 | } else {
110 | connectWallet()
111 | }
112 | }
113 | binding.signMessage.setOnClickListener {
114 | if (walletAccount.connectAdapter.connected(walletAccount.account.publicAddress)) {
115 | signMessage()
116 | } else {
117 | connectWallet()
118 | }
119 | }
120 | binding.signTypedData.setOnClickListener {
121 | if (walletAccount.connectAdapter.connected(walletAccount.account.publicAddress)) {
122 | signTypedData()
123 | } else {
124 | connectWallet()
125 | }
126 | }
127 | binding.request.movementMethod = ScrollingMovementMethod.getInstance()
128 | binding.result.movementMethod = ScrollingMovementMethod.getInstance()
129 | binding.signMessageVerify.setOnClickListener {
130 | val message = createMessage()
131 | if (walletAccount.connectAdapter.connected(walletAccount.account.publicAddress)) {
132 | login(message)
133 | } else {
134 | connectWallet()
135 | }
136 |
137 | }
138 |
139 | binding.writeContract.setOnClickListener {
140 | lifecycleScope.launch {
141 | val params = ContractParams.customAbiEncodeFunctionCall(
142 | contractAddress = "0xd000f000aa1f8accbd5815056ea32a54777b2fc4",
143 | methodName = "mint",
144 | params = listOf("1")
145 | )
146 | val txData: ITxData? = ParticleNetwork.evm.writeContract(
147 | walletAccount.account.publicAddress,
148 | params
149 | )
150 | walletAccount.connectAdapter.signAndSendTransaction(
151 | walletAccount.account.publicAddress,
152 | txData!!.serialize(),
153 | object : TransactionCallback {
154 | override fun onTransaction(transactionId: String?) {
155 | binding.result.text = transactionId ?: ""
156 | toast("signAndSendTransaction success")
157 | }
158 |
159 | override fun onError(error: ConnectError) {
160 |
161 | Log.e("signAndSendTransaction", error.message)
162 | toast(error.message)
163 | }
164 | })
165 | }
166 |
167 | }
168 | }
169 |
170 |
171 | private fun connectWallet() {
172 | walletAccount.connectAdapter.connect(null, object : ConnectCallback {
173 | override fun onConnected(account: Account) {
174 | LogUtils.d("onConnected: $account")
175 | if (!account.publicAddress.equals(walletAccount.account.publicAddress, true)) {
176 | toast("Please connect correct account")
177 | }
178 |
179 | }
180 |
181 | override fun onError(error: ConnectError) {
182 | toast(error.message)
183 | }
184 | })
185 | }
186 |
187 | // val testTransactionStr ="7b22616374696f6e223a226e6f726d616c222c22636861696e4964223a2230783261222c2264617461223a223078613037313264363830303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303031222c2266726f6d223a22307835303466383364363530323966623630376663616134336562643062373032326162313631623063222c226761734c6576656c223a22222c226761734c696d6974223a2230783230633835222c226d6178466565506572476173223a2230783737333539343063222c226d61785072696f72697479466565506572476173223a2230783737333539343030222c226e6f6e6365223a22307830222c2272223a6e756c6c2c2273223a6e756c6c2c22746f223a22307864303030663030306161316638616363626435383135303536656133326135343737376232666334222c2274797065223a22307832222c2276223a6e756c6c2c2276616c7565223a6e756c6c7d"
188 |
189 | private suspend fun signAndSendTransaction() {
190 | val transaction = MockManger.mockCreateTransaction(walletAccount.account.publicAddress)
191 |
192 |
193 | val transNativeWithApi = ParticleNetwork.evm.createTransaction(walletAccount.account.publicAddress,"0x504F83D65029fB607fcAa43ebD0b7022ab161B0C","0x9184e72a000",)
194 |
195 | val contractParams = ContractParams.erc20Transfer("0xfE642A6EABfAc01b758829661f01eA92824c6807","0x504F83D65029fB607fcAa43ebD0b7022ab161B0C","10000000000000")
196 | val transTokenWithApi = ParticleNetwork.evm.createTransaction(walletAccount.account.publicAddress,"0xfE642A6EABfAc01b758829661f01eA92824c6807",contractParams = contractParams)
197 |
198 | binding.request.text = GsonUtils.toJson(transNativeWithApi)
199 | binding.result.text = ""
200 | walletAccount.connectAdapter.signAndSendTransaction(
201 | walletAccount.account.publicAddress,
202 | transNativeWithApi!!.serialize(),
203 | object : TransactionCallback {
204 | override fun onTransaction(transactionId: String?) {
205 | binding.result.text = transactionId ?: ""
206 | toast("signAndSendTransaction success")
207 | }
208 |
209 | override fun onError(error: ConnectError) {
210 |
211 | Log.e("signAndSendTransaction", error.message)
212 | toast(error.message)
213 | }
214 | })
215 | }
216 |
217 | private suspend fun signTransaction() {
218 | val transaction = MockManger.mockCreateTransaction(walletAccount.account.publicAddress)
219 | binding.request.text = GsonUtils.toJson(transaction)
220 | binding.result.text = ""
221 | walletAccount.connectAdapter.signTransaction(
222 | walletAccount.account.publicAddress,
223 | transaction,
224 | object : SignCallback {
225 | override fun onSigned(signature: String) {
226 | binding.result.text = signature
227 | toast("signTransaction success")
228 | }
229 |
230 | override fun onError(error: ConnectError) {
231 | toast(error.message)
232 | }
233 | })
234 | }
235 |
236 | private suspend fun signAllTransactions() {
237 | // val transactions = listOf(
238 | // MockManger.mockCreateTransaction(walletAccount.account.publicAddress),
239 | // MockManger.mockCreateTransaction(walletAccount.account.publicAddress)
240 | // )
241 | // binding.request.text = GsonUtils.toJson(transactions)
242 | val transactions = Array(1) {
243 | "3igAAYeQWiGxrTXP47oHcCqgbLeYt9uHEmzhR7ZXNfffL9kGxgoCpw4qsBwby6y7kCJC2JDRopPvFtqs4C377ygMb6z4rcodDfB5Qt1Wz6iTXBVGht462fGWcumarJx6J5tnMpzRN8DffZfwuaJFxuE9WSora2cXWWyRW2Ps7n2A5NGv2ULPMGumkzPaw3mUyLfDQDndrgxHPYaWzogMxWdwTQ76t44ZBBqhswoTx1KkqYku1tL1p2qnhtPZcS73czrBuhprqwLNSo5tP5ijRvUM8x9qeMhtUvsSZ9Le5y27GaGFHznB4ET85KfyujVtWbSWeHTRyDkzoR2EEHpfm6gw6DHj9o7f72c5rWrtjaDe7CFdUuKYcJcQDWwHnKgn3A2s1iHnv4ZN4sm5HXuGNTYMdxuLZdbvZzmHN3Ai4dn2vVKFks7uYcv15AKnfj9mv2RLXSLkubrP5GCymPyhZ1HUxk7qjVHJFQ89w2q71fzvPmhnMU6vzTz2LTBoXttnTRWxBk818Pg1J2sUHaqKgvp77N9LGhf6aEE2d5XQ68dzRpm6NQRkRX4jF5oyDDWZ9x95q6WQTFeENdUZxPJFYy4WUqGqqRPMkphFbpDCPQAAKkmoNCbkehwPULMBQw3p4BRW7pQLs3cvDcPgxjtBUMQtkCqPNRAk7tvxPLM6EhcFYB9d9TV3BuQ9yxJvyorD9rCzyk8qCkCuNJc7S2NPsCDcMYDRSD8GhcMbjnuWdYwdcMj2xw6Gz5zB1rK9wGcAMJmefDFv89VoBDnktKVa4sWp9Y519YmpPRuN2CMaaiuwAqQyZJaeE5V7WZXZAMppNYntyevu4P4Ws2HPqG8wM49fuDf1au6ehhDRDrihTh3wdwouyVACEuAomjqMEYtYaSJMA6YChm6Y8vXposU4Svog7cCenpoHLY3rYPGaR5orqJS7rfNDmRLp8UKqQ4XLwdKkjoFXBPB9DeptAFstpRPGGsSpk4hqCAEy6Z59AJWaKfD51PcVaN88z53uwfhfoWTKAecqAx9u2YctpueWrD9KMscXxjLbNgz7USSN3CFCtBXERiawEEepgRPKHiUhejFfpECoXhMDR36TmBusQ5TZ4fyoffwQDpf1L3iNcxcVv23YXnHqgC3dJunJQmkajUYS9qMokjJUBTtwbi9ZUcTGDtAGwKmsvsDetwn3WYVhLVtmSPF33URKQ3zH7CKAVEEdmUfVEboDB3mv5CuV8aJ8f23qzuqA8qUDbF3MYNTYDgPyN2Z8vQStxKQSbtu3uvzuW6rPKHjSwvm6RmHziVPsrNqVabyean9Nx1Gdf1fN6BYSN9DcW9EoFaTb2ucFFwz3yFgVePzEjezLi346xYZDwuYH5unkFA8eczYwSVpy7rNy6uKMkWR4REVubeLuHnkMTYucGkjFoyP6JCow6P2xb7cv2YA5ngXeQ5xQ1enAnqgKMTjxDfRSX4Wj5sBPJhyZ47gTkuT5GKZYpo5DuJ8ksvB91hPq3U4YbvKmVrvdEp"
244 | }
245 | binding.result.text = ""
246 | walletAccount.connectAdapter.signAllTransactions(
247 | walletAccount.account.publicAddress,
248 | transactions,
249 | object : SignAllCallback {
250 | override fun onSigned(signatures: List) {
251 | binding.result.text = GsonUtils.toJson(signatures)
252 | toast("signAllTransactions success")
253 | }
254 |
255 | override fun onError(error: ConnectError) {
256 | toast(error.message)
257 | }
258 | })
259 | }
260 |
261 | private fun signMessage(message: String = "Hello Particle Connect!") {
262 | binding.request.text = message
263 | binding.result.text = ""
264 | walletAccount.connectAdapter.signMessage(
265 | walletAccount.account.publicAddress,
266 | MockManger.encode(message),
267 | object : SignCallback {
268 | override fun onSigned(signature: String) {
269 | binding.result.text = signature
270 | toast("signMessage success")
271 | LogUtils.d(
272 | "signMessage",
273 | signature
274 | )
275 | if (ParticleConnect.chainType == ChainType.EVM) {
276 | val address = Web3jSignatureVerifier.recoverAddressFromSignature(
277 | signature,
278 | message
279 | )
280 | LogUtils.d(
281 | "recoverAddressFromSignature",
282 | address,
283 | walletAccount.account.publicAddress
284 | )
285 | }
286 | }
287 |
288 | override fun onError(error: ConnectError) {
289 | LogUtils.d("signMessage", error.message)
290 | toast(error.message)
291 | }
292 | })
293 | }
294 |
295 | private fun signTypedData() {
296 | val message = StreamUtils.getRawString(resources, R.raw.typed_data)
297 | binding.request.text = message
298 | binding.result.text = ""
299 | walletAccount.connectAdapter.signTypedData(
300 | walletAccount.account.publicAddress,
301 | MockManger.encode(message),
302 | object : SignCallback {
303 | override fun onSigned(signature: String) {
304 | binding.result.text = signature
305 | toast("signTypedData success")
306 | }
307 |
308 | override fun onError(error: ConnectError) {
309 | toast(error.message)
310 | }
311 | })
312 | }
313 |
314 | private fun login(eip4361Message: Eip4361Message) {
315 | val message = eip4361Message.toString()
316 | binding.request.text = eip4361Message.toString()
317 | binding.result.text = ""
318 | walletAccount.connectAdapter.login(
319 | walletAccount.account.publicAddress,
320 | eip4361Message,
321 | object : SignCallback {
322 | override fun onSigned(signature: String) {
323 | val result = walletAccount.connectAdapter.verify(
324 | walletAccount.account.publicAddress,
325 | signature,
326 | message
327 | )
328 | val displayTxt = "signature:$signature\n\n verify:$result"
329 | LogUtils.d("login verify", result)
330 | binding.result.text = displayTxt
331 | }
332 |
333 | override fun onError(error: ConnectError) {
334 | toast(error.message)
335 | }
336 | })
337 | }
338 |
339 | private fun createMessage(): Eip4361Message {
340 | // Message example from
341 | // evm-> https://eips.ethereum.org/EIPS/eip-4361
342 | // sol-> https://github.com/ChainAgnostic/CAIPs/blob/master/CAIPs/caip-74.md
343 | /* val msg = Eip4361Message.fromString(
344 | """
345 | particle.network wants you to sign in with your Ethereum account:
346 | 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc21
347 |
348 | I accept the ServiceOrg Terms of Service: https://service.org/tos
349 |
350 | URI: https://service.org/login
351 | Version: 1
352 | Chain ID: 1
353 | Nonce: 32891756
354 | Issued At: 2021-09-30T16:25:24Z
355 | Resources:
356 | - ipfs://bafybeiemxf5abjwjbikoz4mc3a3dla6ual3jsgpdr4cjr3oz3evfyavhwq/
357 | - https://example.com/my-web2-claim.json
358 | """.trimIndent()
359 | )*/
360 | //you can use val msg = Eip4361Message("xx"...)
361 | val msg = Eip4361Message.createWithRequiredParameter(
362 | "particle.network",
363 | "https://service.org/login",
364 | "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc21"
365 | )
366 | return msg
367 | }
368 |
369 |
370 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/controller/secret/ImportWalletActivity.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.controller.secret
2 |
3 | import android.os.Bundle
4 | import androidx.lifecycle.lifecycleScope
5 | import com.connect.common.ILocalAdapter
6 | import com.connect.demo.R
7 | import com.connect.demo.base.BaseActivity
8 | import com.connect.demo.databinding.ActivityImportWalletBinding
9 | import com.connect.demo.utils.toast
10 | import com.evm.adapter.EVMConnectAdapter
11 | import com.particle.connect.ParticleConnect
12 | import com.solana.adapter.SolanaConnectAdapter
13 | import kotlinx.coroutines.launch
14 |
15 | class ImportWalletActivity :
16 | BaseActivity(R.layout.activity_import_wallet) {
17 |
18 | private var chainType: String = "evm"
19 |
20 | override fun onCreate(savedInstanceState: Bundle?) {
21 | super.onCreate(savedInstanceState)
22 | chainType = intent.getStringExtra("chainType") ?: "evm"
23 | binding.toolbar.setNavigationOnClickListener {finish() }
24 |
25 | binding.btImport.setOnClickListener {
26 | val secret = binding.secret.text.trim()
27 | if (secret.isNotEmpty()) {
28 | importWallet(secret.toString())
29 | } else {
30 | toast("Please input private key or mnemonic")
31 | }
32 | }
33 | }
34 |
35 | private fun importWallet(secret: String) {
36 |
37 | lifecycleScope.launch {
38 | try {
39 | val account = if (secret.contains(" ")) {
40 | // import mnemonic
41 | getAdapter().importWalletFromMnemonic(secret)
42 | } else {
43 | // import private key
44 | getAdapter().importWalletFromPrivateKey(secret)
45 | }
46 | if (account != null) {
47 | toast("Import wallet success")
48 | } else {
49 | toast("import wallet fail")
50 | }
51 | finish()
52 | } catch (e: Exception) {
53 | e.printStackTrace()
54 | toast("import wallet fail")
55 | }
56 | }
57 |
58 | }
59 |
60 | private fun getAdapter(): ILocalAdapter {
61 | if (chainType == "evm") {
62 | return ParticleConnect.getAdapters().first { it is EVMConnectAdapter } as ILocalAdapter
63 | }
64 | return ParticleConnect.getAdapters().first { it is SolanaConnectAdapter } as ILocalAdapter
65 | }
66 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/custom_connectadapter/Coin98ConnectAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.custom_connectadapter
2 |
3 | import com.connect.common.ConnectManager
4 | import com.connect.common.model.WalletReadyState
5 | import com.connect.common.utils.AppUtils
6 | import com.particle.base.model.IconUrl
7 | import com.particle.base.model.MobileWCWallet
8 | import com.particle.base.model.WalletName
9 | import com.particle.base.model.WebsiteUrl
10 | import com.wallet.connect.adapter.BaseWalletConnectAdapter
11 |
12 |
13 | class Coin98ConnectAdapter : BaseWalletConnectAdapter() {
14 |
15 | val coin98 = MobileWCWallet("Coin98", "coin98.crypto.finance.media", "coin98")
16 |
17 | override val name: WalletName = coin98.name
18 |
19 | override val icon: IconUrl = "https://registry.walletconnect.com/v2/logo/md/dee547be-936a-4c92-9e3f-7a2350a62e00"
20 |
21 | override val url: WebsiteUrl = "https://coin98.com"
22 |
23 |
24 | override val readyState: WalletReadyState
25 | get() {
26 | if (supportChains.contains(ConnectManager.chainType)) {
27 | return if (AppUtils.isAppInstalled(
28 | ConnectManager.context, coin98.packageName
29 | )
30 | ) WalletReadyState.Installed else WalletReadyState.NotDetected
31 | }
32 | return WalletReadyState.Unsupported
33 | }
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/model/RpcRequest.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.model
2 |
3 | /**
4 | * Created by chaichuanfa on 2022/7/27
5 | */
6 | data class RpcRequest(
7 | val chainId: Long,
8 | val id: String,
9 | val jsonrpc: String = "2.0",
10 | val method: String,
11 | val params: List? = null,
12 | )
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/model/RpcResponse.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.model
2 |
3 | /**
4 | * Created by chaichuanfa on 2022/7/27
5 | */
6 | data class RpcResponse(
7 | val chainId: Long,
8 | val jsonrpc: String,
9 | val id: String,
10 | val result: T,
11 | )
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/model/SerializeTransaction.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.model
2 |
3 | /**
4 | * Created by chaichuanfa on 2022/7/27
5 | */
6 | data class SerializeTransaction(
7 | val transaction: Transaction
8 | )
9 |
10 | data class Transaction(
11 | val hasPartialSign: Boolean,
12 | val serialized: String
13 | )
14 |
15 | data class SOLTransfer(
16 | val sender: String,
17 | val receiver: String,
18 | val lamports: Long,
19 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/model/TransactionAddressData.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.model
2 |
3 | import org.p2p.solanaj.core.PublicKey
4 |
5 | /**
6 | * Created by chaichuanfa on 2022/7/28
7 | */
8 | data class TransactionAddressData(
9 | val associatedAddress: PublicKey,
10 | val shouldCreateAssociatedInstruction: Boolean
11 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/model/WalletAccount.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.model
2 |
3 | import com.connect.common.IConnectAdapter
4 | import com.connect.common.model.Account
5 |
6 | /**
7 | * Created by chaichuanfa on 2022/7/25
8 | */
9 | data class WalletAccount(
10 | val name: String,
11 | val account: Account,
12 | val connectAdapter: IConnectAdapter,
13 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/transaction/SolanaRpcRepository.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.transaction
2 |
3 | import com.connect.common.provider.NetworkProvider
4 | import com.connect.demo.model.RpcRequest
5 | import com.connect.demo.model.TransactionAddressData
6 | import com.connect.demo.utils.SolanaRpcApi
7 | import com.particle.connect.ParticleConnect
8 | import org.p2p.solanaj.core.PublicKey
9 | import org.p2p.solanaj.kits.TokenTransaction
10 | import org.p2p.solanaj.model.types.*
11 | import org.p2p.solanaj.programs.TokenProgram
12 | import java.util.*
13 |
14 | /**
15 | * Created by chaichuanfa on 2022/7/29
16 | */
17 | object SolanaRpcRepository {
18 |
19 | private val solanaRpcApi: SolanaRpcApi =
20 | NetworkProvider.createRetrofit("${com.connect.common.BuildConfig.PN_API_BASE_URL}/solana/")
21 | .create(SolanaRpcApi::class.java)
22 |
23 | suspend fun getRecentBlockhash(commitment: String = "finalized"): String {
24 | val response = solanaRpcApi.getRecentBlockhash(
25 | RpcRequest(
26 | ParticleConnect.chainId,
27 | UUID.randomUUID().toString(),
28 | method = "getRecentBlockhash",
29 | params = listOf(ConfigObjects.Commitment(commitment))
30 | )
31 | )
32 | return response.result.recentBlockhash
33 | }
34 |
35 | suspend fun findSplTokenAddressData(
36 | destinationAddress: PublicKey,
37 | mintAddress: String,
38 | ): TransactionAddressData {
39 | val associatedAddress = try {
40 | findSplTokenAddress(destinationAddress, mintAddress)
41 | } catch (e: IllegalStateException) {
42 | throw IllegalStateException("Invalid owner address")
43 | }
44 |
45 | /* If account is not found, create one */
46 | val accountInfo = getAccountInfo(associatedAddress.toBase58())
47 | val value = accountInfo?.value
48 | val accountExists = value?.owner == TokenProgram.PROGRAM_ID.toString() && value.data != null
49 | return TransactionAddressData(
50 | associatedAddress,
51 | !accountExists
52 | )
53 | }
54 |
55 | suspend fun getAccountInfo(account: String): AccountInfo? {
56 | val response = solanaRpcApi.getAccountInfo(
57 | RpcRequest(
58 | ParticleConnect.chainId,
59 | UUID.randomUUID().toString(),
60 | method = "getAccountInfo",
61 | params = listOf(
62 | account,
63 | RequestConfiguration(encoding = Encoding.BASE64.encoding)
64 | )
65 | )
66 | )
67 | return response.result
68 | }
69 |
70 | suspend fun findSplTokenAddress(
71 | destinationAddress: PublicKey,
72 | mintAddress: String
73 | ): PublicKey {
74 | val accountInfo = getAccountInfo(destinationAddress.toBase58())
75 | val info = TokenTransaction.parseAccountInfoData(accountInfo, TokenProgram.PROGRAM_ID)
76 |
77 | // create associated token address
78 | val value = accountInfo?.value
79 | if (value == null || value.data?.get(0).isNullOrEmpty()) {
80 | return TokenTransaction.getAssociatedTokenAddress(
81 | PublicKey(mintAddress),
82 | destinationAddress
83 | )
84 | }
85 |
86 | // detect if destination address is already a SPLToken address
87 | if (info?.mint == destinationAddress) {
88 | return destinationAddress
89 | }
90 |
91 | // detect if destination address is a SOL address
92 | if (info?.owner?.toBase58() == TokenProgram.PROGRAM_ID.toBase58()) {
93 | // create associated token address
94 | return TokenTransaction.getAssociatedTokenAddress(
95 | PublicKey(mintAddress),
96 | destinationAddress
97 | )
98 | }
99 |
100 | throw IllegalStateException("Wallet address is not valid")
101 | }
102 |
103 | suspend fun getTokenAccountsByOwner(owner: String): TokenAccounts {
104 | val programId = TokenProgram.PROGRAM_ID
105 | val programIdParam = HashMap()
106 | programIdParam["programId"] = programId.toBase58()
107 |
108 | val encoding = HashMap()
109 | encoding["encoding"] = "jsonParsed"
110 | encoding["commitment"] = "confirmed"
111 |
112 | val params = listOf(
113 | owner,
114 | programIdParam,
115 | encoding
116 | )
117 |
118 | val rpcRequest = RpcRequest(
119 | ParticleConnect.chainId,
120 | UUID.randomUUID().toString(),
121 | method = "getTokenAccountsByOwner",
122 | params = params,
123 | )
124 | return solanaRpcApi.getTokenAccountsByOwner(rpcRequest).result
125 | }
126 |
127 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/transaction/SolanaTransactionManager.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.transaction
2 |
3 | import org.p2p.solanaj.core.Transaction
4 | import org.p2p.solanaj.core.PublicKey
5 | import org.p2p.solanaj.core.TransactionInstruction
6 | import org.p2p.solanaj.kits.TokenTransaction
7 | import org.p2p.solanaj.programs.SystemProgram
8 | import org.p2p.solanaj.programs.TokenProgram
9 | import java.math.BigInteger
10 |
11 | /**
12 | * Created by chaichuanfa on 2022/7/29
13 | */
14 | object SolanaTransactionManager {
15 |
16 | suspend fun transferNativeToken(
17 | fromAddress: String,
18 | destinationAddress: String,
19 | lamports: BigInteger,
20 | recentBlockhash: String? = null,
21 | feePayerPublicKey: String? = null,
22 | ): Transaction {
23 | if (fromAddress == destinationAddress) {
24 | error("You can not send tokens to yourself")
25 | }
26 |
27 | val transaction = Transaction()
28 | transaction.addInstruction(
29 | SystemProgram.transfer(
30 | PublicKey(fromAddress),
31 | PublicKey(destinationAddress),
32 | lamports
33 | )
34 | )
35 | transaction.feePayer = PublicKey(feePayerPublicKey ?: fromAddress)
36 | transaction.recentBlockhash = recentBlockhash ?: SolanaRpcRepository.getRecentBlockhash()
37 | return transaction
38 | }
39 |
40 | suspend fun transferSplToken(
41 | fromAddress: String,
42 | destinationAddress: String,
43 | mintAddress: String,
44 | lamports: BigInteger,
45 | recentBlockhash: String? = null,
46 | feePayerAddress: String? = null,
47 | ): Transaction {
48 | val transaction = Transaction()
49 | val destinationPublicKey = PublicKey(destinationAddress)
50 | val feePayer = PublicKey(feePayerAddress ?: fromAddress)
51 | val senderPublicKey = PublicKey(fromAddress)
52 |
53 | val senderAta = TokenTransaction.getAssociatedTokenAddress(
54 | PublicKey(mintAddress),
55 | senderPublicKey
56 | )
57 |
58 | val splDestinationAddress = SolanaRpcRepository.findSplTokenAddressData(
59 | destinationAddress = destinationPublicKey,
60 | mintAddress = mintAddress
61 | )
62 | // get address
63 | val toPublicKey = splDestinationAddress.associatedAddress
64 | val instructions = mutableListOf()
65 |
66 | // create associated token address
67 | if (splDestinationAddress.shouldCreateAssociatedInstruction) {
68 | val createAccount = TokenProgram.createAssociatedTokenAccountInstruction(
69 | TokenProgram.ASSOCIATED_TOKEN_PROGRAM_ID,
70 | TokenProgram.PROGRAM_ID,
71 | PublicKey(mintAddress),
72 | toPublicKey,
73 | destinationPublicKey,
74 | feePayer
75 | )
76 |
77 | instructions += createAccount
78 | }
79 |
80 | instructions += TokenProgram.transferInstruction(
81 | TokenProgram.PROGRAM_ID,
82 | senderAta,
83 | toPublicKey,
84 | senderPublicKey,
85 | lamports
86 | )
87 |
88 | transaction.addInstruction(*instructions.toTypedArray())
89 | transaction.recentBlockhash = recentBlockhash ?: SolanaRpcRepository.getRecentBlockhash()
90 | transaction.feePayer = feePayer
91 |
92 | return transaction
93 | }
94 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/utils/BarcodeEncoder.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.utils
2 |
3 | import android.graphics.Bitmap
4 | import android.graphics.Canvas
5 | import android.graphics.Matrix
6 | import com.google.zxing.BarcodeFormat
7 | import com.google.zxing.EncodeHintType
8 | import com.google.zxing.MultiFormatWriter
9 | import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
10 |
11 | /**
12 | * Helper class for encoding barcodes as a Bitmap.
13 | *
14 | * Adapted from QRCodeEncoder, from the zxing project:
15 | * https://github.com/zxing/zxing
16 | *
17 | * Licensed under the Apache License, Version 2.0.
18 | */
19 |
20 | object BarcodeEncoder {
21 |
22 | fun encodeBitmap(
23 | contents: String,
24 | format: BarcodeFormat,
25 | width: Int,
26 | height: Int,
27 | qrParams: QrParams
28 | ): Bitmap {
29 | val matrix = MultiFormatWriter()
30 | .encode(
31 | contents,
32 | format,
33 | width,
34 | height,
35 | mapOf(
36 | EncodeHintType.CHARACTER_SET to "UTF-8",
37 | EncodeHintType.MARGIN to 0,
38 | EncodeHintType.ERROR_CORRECTION to ErrorCorrectionLevel.Q
39 | )
40 | )
41 | val pixels = IntArray(matrix.width * matrix.height)
42 | for (y in 0 until matrix.height) {
43 | val offset = y * matrix.width
44 | for (x in 0 until matrix.width) {
45 | pixels[offset + x] =
46 | if (matrix.get(x, y)) qrParams.contentColor else qrParams.backgroundColor
47 | }
48 | }
49 | val bitmap = Bitmap.createBitmap(matrix.width, matrix.height, Bitmap.Config.ARGB_8888)
50 | bitmap.setPixels(pixels, 0, matrix.width, 0, 0, matrix.width, matrix.height)
51 | return bitmap
52 | }
53 |
54 | fun createQRcode(
55 | overlay: Bitmap,
56 | qrCodeData: String,
57 | width: Int,
58 | height: Int,
59 | qrParams: QrParams
60 | ): Bitmap {
61 | val hintMap = mapOf(EncodeHintType.ERROR_CORRECTION to ErrorCorrectionLevel.H)
62 | // generating qr code in bitmatrix type
63 | val charsetData = charset("UTF-8")
64 | val matrix = MultiFormatWriter().encode(
65 | String(qrCodeData.toByteArray(charsetData), charsetData),
66 | BarcodeFormat.QR_CODE, width, height, hintMap
67 | )
68 |
69 | // converting bitmatrix to bitmap
70 | val newWidth = matrix.width
71 | val newHeight = matrix.height
72 | val pixels = IntArray(newWidth * newHeight)
73 | // All are 0, or black, by default
74 | for (y in 0 until newHeight) {
75 | val offset = y * newWidth
76 | for (x in 0 until newWidth) {
77 | pixels[offset + x] =
78 | if (matrix.get(x, y)) qrParams.contentColor else qrParams.backgroundColor
79 | }
80 | }
81 | val bitmap = Bitmap.createBitmap(newWidth, newHeight, Bitmap.Config.ARGB_8888)
82 | bitmap.setPixels(pixels, 0, newWidth, 0, 0, newWidth, newHeight)
83 | return mergeBitmaps(overlay, bitmap)
84 | }
85 |
86 | fun mergeBitmaps(overlay: Bitmap, bitmap: Bitmap): Bitmap {
87 | val height = bitmap.height
88 | val width = bitmap.width
89 | val combined = Bitmap.createBitmap(width, height, bitmap.config)
90 | val canvas = Canvas(combined)
91 | val canvasWidth: Int = canvas.width
92 | val canvasHeight: Int = canvas.height
93 | canvas.drawBitmap(bitmap, Matrix(), null)
94 | val centreX = (canvasWidth - overlay.width) / 2
95 | val centreY = (canvasHeight - overlay.height) / 2
96 | canvas.drawBitmap(overlay, centreX.toFloat(), centreY.toFloat(), null)
97 | return combined
98 | }
99 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/utils/ChainUtils.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.utils
2 |
3 | import network.particle.chains.ChainInfo
4 |
5 | /**
6 | * Created by chaichuanfa on 2022/7/25
7 | */
8 | object ChainUtils {
9 |
10 | fun getAllChains(): List {
11 | return ChainInfo.ParticleChains.values.toList()
12 | }
13 |
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/utils/CoilLoader.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.utils
2 |
3 | import android.content.Context
4 | import android.os.Build
5 | import coil.Coil
6 | import coil.ImageLoader
7 | import coil.decode.ImageDecoderDecoder
8 | import coil.decode.SvgDecoder
9 | import coil.disk.DiskCache
10 | import coil.memory.MemoryCache
11 | import coil.util.DebugLogger
12 | import com.particle.base.ParticleNetwork
13 | import okhttp3.Dispatcher
14 | import okhttp3.OkHttpClient
15 |
16 | object CoilLoader {
17 |
18 | fun init(context: Context) {
19 | Coil.setImageLoader(newImageLoader(context.applicationContext))
20 | }
21 |
22 | private fun newImageLoader(context: Context): ImageLoader {
23 | return ImageLoader.Builder(context)
24 | .components {
25 | // GIFs
26 | if (Build.VERSION.SDK_INT >= 28) {
27 | add(ImageDecoderDecoder.Factory())
28 | } else {
29 | add(coil.decode.GifDecoder.Factory())
30 | }
31 | // SVGs
32 | add(SvgDecoder.Factory())
33 | }
34 | .memoryCache {
35 | MemoryCache.Builder(context)
36 | // Set the max size to 25% of the app's available memory.
37 | .maxSizePercent(0.25)
38 | .build()
39 | }
40 | .diskCache {
41 | DiskCache.Builder()
42 | .directory(context.filesDir.resolve("image_cache"))
43 | .maxSizeBytes(512L * 1024 * 1024) //512MB
44 | .build()
45 | }
46 | .okHttpClient {
47 | // Don't limit concurrent network requests by host.
48 | val dispatcher = Dispatcher().apply { maxRequestsPerHost = maxRequests }
49 |
50 | // Lazily create the OkHttpClient that is used for network operations.
51 | OkHttpClient.Builder()
52 | .dispatcher(dispatcher)
53 | .build()
54 | }
55 | // Show a short crossfade when loading images asynchronously.
56 | .crossfade(true)
57 | // Ignore the network cache headers and always read from/write to the disk cache.
58 | .respectCacheHeaders(false)
59 | // Enable logging to the standard Android log if this is a debug build.
60 | .apply { if (ParticleNetwork.isDebug()) logger(DebugLogger()) }
61 | .build()
62 | }
63 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/utils/ContextExt.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.utils
2 |
3 | import android.content.Context
4 | import android.widget.Toast
5 |
6 | /**
7 | * Created by chaichuanfa on 2022/7/25
8 | */
9 | fun Context.toast(msg: String) {
10 | Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
11 | }
12 |
13 | fun Context.toast(resId: Int) {
14 | Toast.makeText(this, getString(resId), Toast.LENGTH_SHORT).show()
15 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/utils/MockManger.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.utils
2 |
3 | import com.connect.common.ConnectManager
4 | import com.connect.common.provider.NetworkProvider
5 | import com.connect.demo.model.RpcRequest
6 | import com.connect.demo.model.SOLTransfer
7 | import com.connect.demo.model.WalletAccount
8 | import com.connect.demo.transaction.SolanaTransactionManager
9 | import com.particle.base.model.ChainType
10 | import com.particle.base.model.EIP1559TransactionData
11 | import com.particle.base.model.LegacyTransactionData
12 | import com.particle.base.utils.Base58Utils
13 | import com.particle.base.utils.HexUtils
14 | import com.particle.connect.ParticleConnect
15 | import org.p2p.solanaj.core.ITransactionData
16 | import org.p2p.solanaj.core.Transaction
17 | import java.math.BigInteger
18 | import java.util.UUID
19 |
20 | /**
21 | * Created by chaichuanfa on 2022/7/26
22 | */
23 | object MockManger {
24 |
25 | private val solanaRpcApi: SolanaRpcApi =
26 | NetworkProvider.createRetrofit("${com.connect.common.BuildConfig.PN_API_BASE_URL}")
27 | .create(SolanaRpcApi::class.java)
28 |
29 | private var minBalanceForRentExemption: BigInteger? = null
30 | private var lamportsPerSignature: BigInteger? = null
31 |
32 | var walletAccount: WalletAccount? = null
33 |
34 |
35 | fun encode(message: String): String {
36 | return if (ParticleConnect.chainType == ChainType.Solana) {
37 | Base58Utils.encode(message.toByteArray(Charsets.UTF_8))
38 | } else {
39 | HexUtils.encodeWithPrefix(message.toByteArray(Charsets.UTF_8))
40 | }
41 | }
42 |
43 | suspend fun mockCreateTransaction(from: String): ITransactionData {
44 | return if (ParticleConnect.chainType == ChainType.Solana) {
45 | mockSendSolanaTransaction(from)
46 | } else {
47 | if (ConnectManager.chainInfo.isEIP1559Supported()) {
48 | EIP1559TransactionData(
49 | "0x${ParticleConnect.chainId.toString(16)}",
50 | from,
51 | "0x504F83D65029fB607fcAa43ebD0b7022ab161B0C",
52 | "0x9184e72a000",
53 | gasLimit = "0x${Integer.toHexString(25000)}",
54 | maxFeePerGas = "0x9502f90e",
55 | maxPriorityFeePerGas = "0x9502F900",
56 | )
57 | } else {
58 | LegacyTransactionData(
59 | "0x${ParticleConnect.chainId.toString(16)}",
60 | from = from,
61 | to = "0x504F83D65029fB607fcAa43ebD0b7022ab161B0C",
62 | value = "0x2386f26fc10000",
63 | data = "0x",
64 | nonce = "0x0",
65 | gasPrice = "0x25cfcb580",
66 | gasLimit = "0x5208",
67 | type = "0x0",
68 | action = "normal",
69 | gasLevel = "medium"
70 | )
71 | }
72 |
73 | }
74 | }
75 |
76 | private suspend fun mockSendSolanaTransaction(from: String): Transaction {
77 | val tx = SolanaTransactionManager.transferNativeToken(
78 | from,
79 | "DtbnGhuAzK1NhbgdBhX6DLLFzFJFFEpjtzrbxwVEZEAs",
80 | BigInteger("100000"),
81 | )
82 |
83 | return tx
84 | }
85 |
86 | private suspend fun mockCreateSendSplTokenTransaction(
87 | from: String,
88 | ): Transaction {
89 | val tokenMintAddress = "GobzzzFQsFAHPvmwT42rLockfUCeV3iutEkK218BxT8K"
90 | val destinationAddress = "DtbnGhuAzK1NhbgdBhX6DLLFzFJFFEpjtzrbxwVEZEAs"
91 |
92 | return SolanaTransactionManager.transferSplToken(
93 | from,
94 | destinationAddress,
95 | tokenMintAddress,
96 | BigInteger("100000")
97 | )
98 | }
99 |
100 | suspend fun createSolanaTransaction(
101 | sender: String,
102 | receiver: String,
103 | lamports: Long,
104 | ): String {
105 | val response = solanaRpcApi.enhancedSerializeTransaction(
106 | RpcRequest(
107 | ParticleConnect.chainId,
108 | UUID.randomUUID().toString(),
109 | method = "enhancedSerializeTransaction",
110 | params = listOf("transfer-sol", SOLTransfer(sender, receiver, lamports))
111 | )
112 | )
113 | return response.result.transaction.serialized
114 | }
115 |
116 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/utils/QrParams.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.utils
2 |
3 | import androidx.annotation.ColorInt
4 |
5 | data class QrParams(
6 | @ColorInt val contentColor: Int,
7 | @ColorInt val backgroundColor: Int,
8 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/utils/SolanaRpcApi.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.utils
2 |
3 | import com.connect.demo.model.RpcRequest
4 | import com.connect.demo.model.RpcResponse
5 | import com.connect.demo.model.SerializeTransaction
6 | import org.p2p.solanaj.model.types.AccountInfo
7 | import org.p2p.solanaj.model.types.FeesResponse
8 | import org.p2p.solanaj.model.types.RecentBlockhash
9 | import org.p2p.solanaj.model.types.TokenAccounts
10 | import retrofit2.http.Body
11 | import retrofit2.http.POST
12 | import retrofit2.http.Url
13 |
14 |
15 | interface SolanaRpcApi {
16 |
17 | @POST
18 | suspend fun enhancedSerializeTransaction(
19 | @Body body: RpcRequest,
20 | @Url url: String = ""
21 | ): RpcResponse
22 |
23 | @POST
24 | suspend fun getRecentBlockhash(
25 | @Body body: RpcRequest,
26 | @Url url: String = ""
27 | ): RpcResponse
28 |
29 | @POST
30 | suspend fun getAccountInfo(
31 | @Body body: RpcRequest,
32 | @Url url: String = ""
33 | ): RpcResponse
34 |
35 | @POST
36 | suspend fun getMinimumBalanceForRentExemption(
37 | @Body rpcRequest: RpcRequest,
38 | @Url url: String = ""
39 | ): RpcResponse
40 |
41 | @POST
42 | suspend fun getFees(
43 | @Body rpcRequest: RpcRequest,
44 | @Url url: String = ""
45 | ): RpcResponse
46 |
47 | @POST
48 | suspend fun getTokenAccountsByOwner(
49 | @Body rpcRequest: RpcRequest,
50 | @Url url: String = ""
51 | ): RpcResponse
52 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/connect/demo/utils/StreamUtils.kt:
--------------------------------------------------------------------------------
1 | package com.connect.demo.utils
2 |
3 | import android.content.res.Resources
4 | import java.io.BufferedReader
5 | import java.io.InputStreamReader
6 |
7 | /**
8 | * Created by chaichuanfa on 2022/7/26
9 | */
10 | object StreamUtils {
11 |
12 | fun getRawString(resource: Resources, rawId: Int): String {
13 | val stream = resource.openRawResource(rawId)
14 | try {
15 | val reader = BufferedReader(InputStreamReader(stream, "utf-8"))
16 | val sb = StringBuilder()
17 | var line: String? = null
18 | while (run {
19 | line = reader.readLine()
20 | line
21 | } != null) {
22 | sb.append(line + "\n")
23 | }
24 | return sb.toString()
25 | } catch (e: Exception) {
26 | e.printStackTrace()
27 | } finally {
28 | stream.close()
29 | }
30 | return ""
31 | }
32 |
33 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/arbitrum.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/drawable-xxhdpi/arbitrum.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/aurora.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/drawable-xxhdpi/aurora.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/avalanche.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/drawable-xxhdpi/avalanche.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/bsc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/drawable-xxhdpi/bsc.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ethereum.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/drawable-xxhdpi/ethereum.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/fantom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/drawable-xxhdpi/fantom.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/harmony.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/drawable-xxhdpi/harmony.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/heco.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/drawable-xxhdpi/heco.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/drawable-xxhdpi/ic_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/kcc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/drawable-xxhdpi/kcc.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/moonbeam.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/drawable-xxhdpi/moonbeam.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/moonriver.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/drawable-xxhdpi/moonriver.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/optimism.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/drawable-xxhdpi/optimism.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/polygon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/drawable-xxhdpi/polygon.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/solana.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/drawable-xxhdpi/solana.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/edit_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_create_wallet.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_delete.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_edit.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_left.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_more.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_right.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_wallet.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/list_item_divider.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | -
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/popup_window_transparent.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_import_wallet.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
12 |
22 |
23 |
39 |
40 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
11 |
12 |
19 |
20 |
27 |
28 |
38 |
39 |
47 |
48 |
49 |
50 |
51 |
52 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_manage.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
20 |
21 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_reference.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
19 |
20 |
34 |
35 |
42 |
43 |
54 |
55 |
66 |
67 |
68 |
69 |
74 |
75 |
76 |
83 |
84 |
97 |
98 |
110 |
111 |
123 |
124 |
136 |
137 |
149 |
150 |
162 |
163 |
175 |
176 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/edit_wallet_name_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_wallet_connect.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
16 |
17 |
21 |
22 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_account.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
20 |
21 |
34 |
35 |
47 |
48 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_adapter.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
11 |
18 |
19 |
26 |
27 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/delete_action.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/toolbar_action.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/raw/typed_data.json:
--------------------------------------------------------------------------------
1 | {
2 | "types": {
3 | "EIP712Domain": [
4 | {
5 | "name": "name",
6 | "type": "string"
7 | },
8 | {
9 | "name": "version",
10 | "type": "string"
11 | },
12 | {
13 | "name": "chainId",
14 | "type": "uint256"
15 | },
16 | {
17 | "name": "verifyingContract",
18 | "type": "address"
19 | }
20 | ],
21 | "Order": [
22 | {
23 | "name": "exchange",
24 | "type": "address"
25 | },
26 | {
27 | "name": "maker",
28 | "type": "address"
29 | },
30 | {
31 | "name": "taker",
32 | "type": "address"
33 | },
34 | {
35 | "name": "makerRelayerFee",
36 | "type": "uint256"
37 | },
38 | {
39 | "name": "takerRelayerFee",
40 | "type": "uint256"
41 | },
42 | {
43 | "name": "makerProtocolFee",
44 | "type": "uint256"
45 | },
46 | {
47 | "name": "takerProtocolFee",
48 | "type": "uint256"
49 | },
50 | {
51 | "name": "feeRecipient",
52 | "type": "address"
53 | },
54 | {
55 | "name": "feeMethod",
56 | "type": "uint8"
57 | },
58 | {
59 | "name": "side",
60 | "type": "uint8"
61 | },
62 | {
63 | "name": "saleKind",
64 | "type": "uint8"
65 | },
66 | {
67 | "name": "target",
68 | "type": "address"
69 | },
70 | {
71 | "name": "howToCall",
72 | "type": "uint8"
73 | },
74 | {
75 | "name": "calldata",
76 | "type": "bytes"
77 | },
78 | {
79 | "name": "replacementPattern",
80 | "type": "bytes"
81 | },
82 | {
83 | "name": "staticTarget",
84 | "type": "address"
85 | },
86 | {
87 | "name": "staticExtradata",
88 | "type": "bytes"
89 | },
90 | {
91 | "name": "paymentToken",
92 | "type": "address"
93 | },
94 | {
95 | "name": "basePrice",
96 | "type": "uint256"
97 | },
98 | {
99 | "name": "extra",
100 | "type": "uint256"
101 | },
102 | {
103 | "name": "listingTime",
104 | "type": "uint256"
105 | },
106 | {
107 | "name": "expirationTime",
108 | "type": "uint256"
109 | },
110 | {
111 | "name": "salt",
112 | "type": "uint256"
113 | },
114 | {
115 | "name": "nonce",
116 | "type": "uint256"
117 | }
118 | ]
119 | },
120 | "primaryType": "Order",
121 | "domain": {
122 | "name": "LifeForm Exchange Contract",
123 | "version": "2.3",
124 | "chainId": 1,
125 | "verifyingContract": "0x9407Ec32b440aEcbDbC1Ff93324Af5FE626D4dd3"
126 | },
127 | "message": {
128 | "exchange": "0x9407Ec32b440aEcbDbC1Ff93324Af5FE626D4dd3",
129 | "maker": "0x2CeD4F9bBfcD178F7Cf0F949249cd1C3b649bDb7",
130 | "taker": "0x0000000000000000000000000000000000000000",
131 | "makerRelayerFee": 50,
132 | "takerRelayerFee": 0,
133 | "makerProtocolFee": 0,
134 | "takerProtocolFee": 0,
135 | "feeMethod": 1,
136 | "side": 1,
137 | "saleKind": 0,
138 | "target": "0xC4f609c43448b462a042e5E5E9E2100D070A0E04",
139 | "howToCall": 0,
140 | "calldata": "0xf242432a0000000000000000000000002ced4f9bbfcd178f7cf0f949249cd1c3b649bdb70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000025b378602000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000",
141 | "replacementPattern": "0x000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
142 | "staticTarget": "0x0000000000000000000000000000000000000000",
143 | "staticExtradata": "0x",
144 | "paymentToken": "0x4f500465c89c2f8A44D1142e02338534C0c421be",
145 | "basePrice": "1000000000000000000",
146 | "extra": 0,
147 | "listingTime": 1666347130,
148 | "expirationTime": 1666606330,
149 | "salt": "1666347130000",
150 | "feeRecipient": "0xfE517e9d1E74787660a3202D3916367c6e363f2e",
151 | "nonce": "0"
152 | }
153 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 | #aaa
11 | #E4E3E3
12 | #03FF03
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFFFFF
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Particle Connect
3 | Connect_V1
4 | Connect_V2
5 | Disconnect
6 | Connected
7 | signAndSendTransaction
8 | signTransaction
9 | signAllTransactions
10 | signMessage
11 | signTypedData
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
--------------------------------------------------------------------------------
/app/src/test/java/com/minijoy/particle_connect_android/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.minijoy.particle_connect_android
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | id 'com.android.application' version '8.5.1' apply false
4 | id 'com.android.library' version '8.5.1' apply false
5 | id 'org.jetbrains.kotlin.android' version '1.9.10' apply false
6 | }
7 |
8 | task clean(type: Delete) {
9 | delete rootProject.buildDir
10 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
24 | android.injected.testOnly=false
25 | android.enableJetifier=true
26 |
27 | android.defaults.buildfeatures.buildconfig=true
28 | android.nonFinalResIds=false
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | compileSdk = "34"
3 | targetSdk = "34"
4 |
5 | minSdk = "23"
6 | retrofit = "2.9.0"
7 | okhttp = "4.9.3"
8 | connect = "2.0.9"
9 | auth = "2.0.9"
10 |
11 | [libraries]
12 | #particle sdk
13 | particle-auth = { module = "network.particle:auth-service", version.ref = "auth" } # deprecated use auth-core instead
14 | particle-auth-core = { module = "network.particle:auth-core", version.ref = "auth" }
15 | particle-api = { module = "network.particle:api-service", version.ref = "auth" }
16 | connect-common = { module = "network.particle:connect-common", version.ref = "connect" }
17 | connect = { module = "network.particle:connect", version.ref = "connect" }
18 | connect-kit = { module = "network.particle:connect-kit", version.ref = "connect" }
19 | connect-auth-adapter = { module = "network.particle:connect-auth-adapter", version.ref = "connect" }# deprecated use connect-auth-core-adapter instead
20 | connect-auth-core-adapter = { module = "network.particle:connect-auth-core-adapter", version.ref = "connect" }
21 | connect-evm-adapter = { module = "network.particle:connect-evm-adapter", version.ref = "connect" }
22 | connect-sol-adapter = { module = "network.particle:connect-solana-adapter", version.ref = "connect" }
23 | connect-phantom-adapter = { module = "network.particle:connect-phantom-adapter", version.ref = "connect" }
24 | connect-wallet-connect-adapter = { module = "network.particle:connect-wallet-connect-adapter", version.ref = "connect" }
25 | #official
26 | core-ktx = "androidx.core:core-ktx:1.7.0"
27 | appcompat = "androidx.appcompat:appcompat:1.4.1"
28 | material = "com.google.android.material:material:1.5.0"
29 | constraintlayout = "androidx.constraintlayout:constraintlayout:2.1.3"
30 | livedata = "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0"
31 | viewmodel = "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
32 | annotation = "androidx.annotation:annotation:1.3.0"
33 | activity-ktx = "androidx.activity:activity-ktx:1.4.0"
34 | fragment-ktx = "androidx.fragment:fragment-ktx:1.4.1"
35 | recyclerview = "androidx.recyclerview:recyclerview:1.2.0"
36 |
37 | glide-core = "com.github.bumptech.glide:glide:4.13.2"
38 | glide-compiler = "com.github.bumptech.glide:compiler:4.13.2"
39 | coil = "io.coil-kt:coil:2.0.0-rc03"
40 | coil-svg = "io.coil-kt:coil-svg:2.0.0-rc03"
41 | coil-gif = "io.coil-kt:coil-gif:2.0.0-rc03"
42 | zxing-barcodescanner = "com.github.bingoogolapple.BGAQRCode-Android:zxing:1.3.8"
43 |
44 |
45 |
46 | retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
47 | retrofit-converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }
48 | retrofit-converter-moshi = { module = "com.squareup.retrofit2:converter-moshi", version.ref = "retrofit" }
49 | okhttp3-logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
50 | okhttp3 = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
51 | utilcodex = "com.github.xxhong:CollectsPkg:2.1.0"
52 | recyclerview-adapter = "com.github.CymChad:BaseRecyclerViewAdapterHelper:3.0.7"
53 | #test
54 | junit = "junit:junit:4.13.2"
55 | junit-ext = "androidx.test.ext:junit:1.1.3"
56 | espresso-core = "androidx.test.espresso:espresso-core:3.4.0"
57 | [bundles]
58 | retrofit = ["retrofit", "retrofit-converter-gson", "retrofit-converter-moshi"]
59 | okhttp3 = ["okhttp3", "okhttp3-logging-interceptor"]
60 |
61 | [plugins]
62 | jmh = { id = "me.champeau.jmh", version = "0.6.5" }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Particle-Network/particle-connect-android/f7022c0e45f9ca6197b0c71329db415e9f0dffc9/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Aug 12 18:16:43 CST 2024
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | maven { setUrl("https://jitpack.io") }
14 | maven { setUrl("https://maven.google.com/") }
15 | maven { setUrl("https://s01.oss.sonatype.org/content/groups/staging/") }
16 | maven { setUrl("https://s01.oss.sonatype.org/content/repositories/releases/") }
17 | }
18 |
19 |
20 | }
21 | rootProject.name = "particle-connect-android"
22 | include ':app'
23 |
24 |
--------------------------------------------------------------------------------