Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix compatibility on Android platforms below Android API 28 #543

Merged
merged 20 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/test-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ jobs:
# minSdk and targetSdk, see ${project_root}/android_test/app/build.gradle.kts
# TODO: Investigate why it is unstable on API 33.
emulator: [
{ api-level: 26, target: google_apis },
{ api-level: 24, target: google_apis },
{ api-level: 32, target: playstore }
]
steps:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
As this project is pre 1.0, breaking changes may happen for minor version bumps. A breaking change will get clearly notified in this log.

## Pending
* Remove `commons-codec:commons-codec:1.16.0`. ([#543](https://github.com/stellar/java-stellar-sdk/pull/543))

## 0.41.0
* Add support for Soroban Preview 11. ([#530](https://github.com/stellar/java-stellar-sdk/pull/530))
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ xdr/Stellar-contract-spec.x \
xdr/Stellar-contract.x \
xdr/Stellar-internal.x \
xdr/Stellar-contract-config-setting.x

# TODO: update XDRGEN_COMMIT
XDRGEN_COMMIT=f0c41458ca0b66b4649b18deddc9f7a11199f1f9
XDRNEXT_COMMIT=9ac02641139e6717924fdad716f6e958d0168491

Expand Down
5 changes: 2 additions & 3 deletions android_test/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ android {

defaultConfig {
applicationId = "org.stellar.javastellarsdkdemoapp"
minSdk = 26
minSdk = 24
targetSdk = 33
versionCode = 1
versionName = "1.0"
Expand Down Expand Up @@ -59,15 +59,14 @@ dependencies {
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
implementation(files("libs/stellar-sdk.jar"))
// Since we are adding local jar(libs/stellar-sdk.jar) as dependency,
// gradle cannot automatically download the required third-party dependencies.
implementation(files("libs/stellar-sdk.jar"))
implementation("com.squareup.okhttp3:okhttp:4.11.0")
implementation("com.squareup.okhttp3:okhttp-sse:4.11.0")
implementation("com.moandjiezana.toml:toml4j:0.7.2")
implementation("com.google.code.gson:gson:2.10.1")
implementation("net.i2p.crypto:eddsa:0.3.0")
implementation("commons-codec:commons-codec:1.16.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0-alpha03")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ private const val PACKAGE = "org.stellar.javastellarsdkdemoapp"
@RunWith(AndroidJUnit4::class)
class MainInstrumentedTest {
@Test
fun testGetNetwork() {
fun testSDK() {
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
// open app
device.pressHome()
Expand All @@ -41,22 +41,22 @@ class MainInstrumentedTest {
)

// get text
val textNoNetworkInfo = device.wait(
Until.findObject(By.text("No network info")),
val textNoTestResult = device.wait(
Until.findObject(By.text("Not Run")),
ONE_MINUTE
)
assertNotNull(textNoNetworkInfo)
assertNotNull(textNoTestResult)

// get button
val button = device.wait(
Until.findObject(By.text("Get Network")),
Until.findObject(By.text("Run Test")),
ONE_MINUTE
)
assertNotNull(button)

// click button and wait text to appear
button.click()

assertTrue(device.wait(Until.hasObject(By.text("public")), ONE_MINUTE * 5))
assertTrue(device.wait(Until.hasObject(By.text("SUCCESS")), ONE_MINUTE * 5))
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.stellar.javastellarsdkdemoapp

import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
Expand All @@ -26,63 +27,101 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.stellar.javastellarsdkdemoapp.ui.theme.JavaStellarSDKDemoAppTheme
import org.stellar.sdk.Account
import org.stellar.sdk.AccountConverter
import org.stellar.sdk.Address
import org.stellar.sdk.Auth
import org.stellar.sdk.InvokeHostFunctionOperation
import org.stellar.sdk.KeyPair
import org.stellar.sdk.Network
import org.stellar.sdk.Server
import org.stellar.sdk.SorobanServer
import org.stellar.sdk.TimeBounds
import org.stellar.sdk.Transaction
import org.stellar.sdk.TransactionBuilder
import org.stellar.sdk.TransactionPreconditions
import org.stellar.sdk.federation.FederationServer
import org.stellar.sdk.scval.Scv
import org.stellar.sdk.xdr.ContractExecutable
import org.stellar.sdk.xdr.ContractExecutableType
import org.stellar.sdk.xdr.ContractIDPreimage
import org.stellar.sdk.xdr.ContractIDPreimage.ContractIDPreimageFromAddress
import org.stellar.sdk.xdr.ContractIDPreimageType
import org.stellar.sdk.xdr.CreateContractArgs
import org.stellar.sdk.xdr.ExtensionPoint
import org.stellar.sdk.xdr.HostFunction
import org.stellar.sdk.xdr.HostFunctionType
import org.stellar.sdk.xdr.Int64
import org.stellar.sdk.xdr.InvokeContractArgs
import org.stellar.sdk.xdr.LedgerEntryType
import org.stellar.sdk.xdr.LedgerFootprint
import org.stellar.sdk.xdr.LedgerKey
import org.stellar.sdk.xdr.LedgerKey.LedgerKeyAccount
import org.stellar.sdk.xdr.SorobanAddressCredentials
import org.stellar.sdk.xdr.SorobanAuthorizationEntry
import org.stellar.sdk.xdr.SorobanAuthorizedFunction
import org.stellar.sdk.xdr.SorobanAuthorizedFunctionType
import org.stellar.sdk.xdr.SorobanAuthorizedInvocation
import org.stellar.sdk.xdr.SorobanCredentials
import org.stellar.sdk.xdr.SorobanCredentialsType
import org.stellar.sdk.xdr.SorobanResources
import org.stellar.sdk.xdr.SorobanTransactionData
import org.stellar.sdk.xdr.Uint256
import org.stellar.sdk.xdr.Uint32
import org.stellar.sdk.xdr.XdrUnsignedInteger

private const val HORIZON_SERVER = "https://horizon.stellar.org/"
private const val PUBLIC = "Public Global Stellar Network ; September 2015"
private const val TESTNET = "Test SDF Network ; September 2015"

class MainActivity : ComponentActivity() {
private lateinit var networkViewModel: NetworkViewModel
private lateinit var sdkTestViewModel: SdkTestViewModel

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

networkViewModel = ViewModelProvider(this).get(NetworkViewModel::class.java)
sdkTestViewModel = ViewModelProvider(this).get(SdkTestViewModel::class.java)

setContent {
JavaStellarSDKDemoAppTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Main(networkViewModel)
Main(sdkTestViewModel)
}
}
}
}
}

class NetworkViewModel : ViewModel() {
private val _network = mutableStateOf<String?>(null)
val network: String? get() = _network.value
class SdkTestViewModel : ViewModel() {
private val _result = mutableStateOf<String?>(null)
val result: String? get() = _result.value

fun fetchNetworkPassphrase() {
fun runSDKTest() {
viewModelScope.launch {
_network.value = withContext(Dispatchers.IO) {
getNetwork()
_result.value = withContext(Dispatchers.IO) {
testSDK()
}
}
}
}

@Composable
fun Main(networkViewModel: NetworkViewModel, modifier: Modifier = Modifier) {
fun Main(sdkTestViewModel: SdkTestViewModel, modifier: Modifier = Modifier) {
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Button(
onClick = { networkViewModel.fetchNetworkPassphrase() }
onClick = { sdkTestViewModel.runSDKTest() }
) {
Text(text = "Get Network")
Text(text = "Run Test")
}

Spacer(modifier = Modifier.height(16.dp))

Text(
text = networkViewModel.network ?: "No network info",
text = sdkTestViewModel.result ?: "Not Run",
modifier = Modifier.padding(horizontal = 16.dp)
)
}
Expand All @@ -92,28 +131,157 @@ fun Main(networkViewModel: NetworkViewModel, modifier: Modifier = Modifier) {
@Composable
fun MainPreview() {
JavaStellarSDKDemoAppTheme {
val networkViewModel = NetworkViewModel()
Main(networkViewModel)
val sdkTestViewModel = SdkTestViewModel()
Main(sdkTestViewModel)
}
}

private fun getNetwork(): String? {
val server = Server(HORIZON_SERVER)
private fun testSDK(): String {
return try {
when (server.root().networkPassphrase) {
PUBLIC -> {
"public"
}
// send request to horizon server
val server = Server("https://horizon-testnet.stellar.org")
val horizonResp = server.root()
if (horizonResp == null || horizonResp.networkPassphrase != Network.TESTNET.networkPassphrase) {
throw Exception("Query Horizon failed")
}

TESTNET -> {
"testnet"
}
// send request to Soroban RPC server
val sorobanServer = SorobanServer("https://soroban-testnet.stellar.org:443")
if (sorobanServer.network.passphrase != Network.TESTNET.networkPassphrase) {
throw Exception("Query Soroban Server failed")
}

else -> {
"others"
}
// Test Federation
val fedResp =
FederationServer.createForDomain("lobstr.co").resolveAddress("example*lobstr.co")
if (fedResp == null || fedResp.accountId == null) {
throw Exception("Query Federation failed")
}

// build and parse transaction
val source: KeyPair =
KeyPair.fromSecretSeed("SCH27VUZZ6UAKB67BDNF6FA42YMBMQCBKXWGMFD5TZ6S5ZZCZFLRXKHS")

val ledgerKey = LedgerKey.Builder()
.discriminant(LedgerEntryType.ACCOUNT)
.account(
LedgerKeyAccount.Builder()
.accountID(
KeyPair.fromAccountId(
"GB7TAYRUZGE6TVT7NHP5SMIZRNQA6PLM423EYISAOAP3MKYIQMVYP2JO"
)
.xdrAccountId
)
.build()
)
.build()
val sorobanData = SorobanTransactionData.Builder()
.resources(
SorobanResources.Builder()
.footprint(
LedgerFootprint.Builder()
.readOnly(arrayOf(ledgerKey))
.readWrite(arrayOf())
.build()
)
.readBytes(Uint32(XdrUnsignedInteger(699)))
.writeBytes(Uint32(XdrUnsignedInteger(0)))
.instructions(Uint32(XdrUnsignedInteger(34567)))
.build()
)
.refundableFee(Int64(100L))
.ext(ExtensionPoint.Builder().discriminant(0).build())
.build()
val sorobanDataString = sorobanData.toXdrBase64()

val createContractArgs = CreateContractArgs.Builder()
.contractIDPreimage(
ContractIDPreimage.Builder()
.discriminant(ContractIDPreimageType.CONTRACT_ID_PREIMAGE_FROM_ADDRESS)
.fromAddress(
ContractIDPreimageFromAddress.Builder()
.address(
Address(
"GB7TAYRUZGE6TVT7NHP5SMIZRNQA6PLM423EYISAOAP3MKYIQMVYP2JO"
)
.toSCAddress()
)
.salt(Uint256(ByteArray(32)))
.build()
)
.build()
)
.executable(
ContractExecutable.Builder()
.discriminant(ContractExecutableType.CONTRACT_EXECUTABLE_TOKEN)
.build()
)
.build()
val hostFunction = HostFunction.Builder()
.discriminant(HostFunctionType.HOST_FUNCTION_TYPE_CREATE_CONTRACT)
.createContract(createContractArgs)
.build()
val invokeHostFunctionOperation =
InvokeHostFunctionOperation.builder().hostFunction(hostFunction).build()

val sequenceNumber = 2908908335136768L
val account = Account(source.accountId, sequenceNumber)
val transaction: Transaction =
TransactionBuilder(AccountConverter.enableMuxed(), account, Network.TESTNET)
.addOperation(invokeHostFunctionOperation)
.addPreconditions(
TransactionPreconditions.builder().timeBounds(TimeBounds(0, 0)).build()
)
.setBaseFee(100)
.setSorobanData(sorobanDataString)
.build()
transaction.sign(source)
Transaction.fromEnvelopeXdr(transaction.toEnvelopeXdrBase64(), Network.TESTNET)

// sign entry
val contractId = "CDCYWK73YTYFJZZSJ5V7EDFNHYBG4QN3VUNG2IGD27KJDDPNCZKBCBXK"
val signer =
KeyPair.fromSecretSeed("SAEZSI6DY7AXJFIYA4PM6SIBNEYYXIEM2MSOTHFGKHDW32MBQ7KVO6EN")
val validUntilLedgerSeq = 654656L
val network = Network.TESTNET

val credentials = SorobanCredentials.Builder()
.discriminant(SorobanCredentialsType.SOROBAN_CREDENTIALS_ADDRESS)
.address(
SorobanAddressCredentials.Builder()
.address(Address(signer.accountId).toSCAddress())
.nonce(Int64(123456789L))
.signatureExpirationLedger(Uint32(XdrUnsignedInteger(0L)))
.signature(Scv.toVoid())
.build()
)
.build()
val invocation = SorobanAuthorizedInvocation.Builder()
.function(
SorobanAuthorizedFunction.Builder()
.discriminant(
SorobanAuthorizedFunctionType.SOROBAN_AUTHORIZED_FUNCTION_TYPE_CONTRACT_FN
)
.contractFn(
InvokeContractArgs.Builder()
.contractAddress(Address(contractId).toSCAddress())
.functionName(Scv.toSymbol("increment").sym)
.args(arrayOfNulls(0))
.build()
)
.build()
)
.subInvocations(arrayOfNulls(0))
.build()
val entry = SorobanAuthorizationEntry.Builder()
.credentials(credentials)
.rootInvocation(invocation)
.build()
Auth.authorizeEntry(entry.toXdrBase64(), signer, validUntilLedgerSeq, network)

"SUCCESS"
} catch (e: Exception) {
null
Log.e("MainActivity", "testSDK ERROR", e)
"FAILED"
}
}
1 change: 1 addition & 0 deletions android_test/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}

Expand Down
Loading
Loading