Tutorials
No Results
Tutorial

Beginner · 30 minutes

Getting Started with Developing Android Apps on the Algorand Platform Using java-algorand-sdk

Tutorial Overview

This tutorial is meant for Android developers who want to begin to build interesting applications on the Algorand blockchain.
In this tutorial we will cover the following:

  • Java-algorand-sdk Setup and Configuration in Android Studio.
  • Account Creation with And Without Mnemonic.
  • Checking Account Balance.

Requirements

Prerequisites

  • A familiarity with the java programming language and its use within the Android Environment.
  • Basic understanding of some blockchain terminologies.

Steps

1. Java-algorand-sdk Setup and Configuration in Android Studio

Create a new android studio project, you can choose the minimum SDK version of 16 and above.
Now, in your top level build.gradle file, make sure maven { url 'https://jitpack.io' } is added in the allprojects repositories section like it is done below.

allprojects {
    repositories {
        google()
        jcenter()
        maven { url 'https://jitpack.io' }

    }
}

Finally, add the following dependencies in the app-level build.gradle file, they are the dependencies for both BouncyCastle and the java-algorand-sdk.

implementation 'org.bouncycastle:bcprov-jdk15on:1.61'
implementation 'com.github.Jesulonimi21:java-algorand-sdk:1.5.0'

Yayyy, so simple, that’s all we need to get started developing android apps on the Algorand platform.

2. Account Creation

First of all, lets make sure we import some classes from the java-algorand-sdk and the BouncyCastle library we added to our project previously.

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.algorand.algosdk.account.Account;
import com.algorand.algosdk.crypto.Address;
import com.algorand.algosdk.transaction.SignedTransaction;
import com.algorand.algosdk.transaction.Transaction;
import com.algorand.algosdk.util.Encoder;
import com.algorand.algosdk.v2.client.common.AlgodClient;
import com.algorand.algosdk.v2.client.common.Response;
import com.algorand.algosdk.v2.client.model.PendingTransactionResponse;
import com.algorand.algosdk.v2.client.model.TransactionParametersResponse;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;

Creating an account using the java-algorand-sdk is very simple but because we are targeting the android platform, we will need to write some additional code which I’ll explain after, write the code below in the onCreate function of your MainActivity.java file.

Security.removeProvider("BC");
Security.insertProviderAt(new BouncyCastleProvider(), 0);

What I did above is pretty simple, the problem is that android ships with an outdated version of BouncyCastle, and has been added as a Provider, what we have to do is remove that provider and add our own version of BouncyCastle as a new Provider. Please note that the two lines of code above must be added before any other code that uses public or private Key Cryptographic algorithm. Let’s proceed by creating an instance of the AlgodClient by creating a field variable at the top level of our MainActivity.java class.

  AlgodClient client;

Let’s initialize these variable in our onCreate function.

    String ALGOD_API_ADDR = "http://hackathon.algodev.network";
    Integer ALGOD_PORT = 9100;
    String ALGOD_API_TOKEN = "ef920e2e7e002953f4b29a8af720efe8e4ecc75ff102b165e0472834b25832c1";

  client = (AlgodClient) new AlgodClient(ALGOD_API_ADDR,
            ALGOD_PORT, ALGOD_API_TOKEN);

What we do above is to create an instance of our AlgodClient by passing in the Hackathon TestNet server address as API address, its port number as Port and its token as API token.

3. Creating a Keypair without a Mnemonic String

 public  Account createAccountWithoutMnemonic( ){
        Account myAccount1= null;

        try {
            myAccount1 = new Account();
            Log.d("algoDebug"," algod account address: " + myAccount1.getAddress());
            Log.d("algoDebug"," algod account MNEMONIC: " + myAccount1.toMnemonic());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            Log.d("algoDebug"," Eror while creating new account "+e);
        }
        return myAccount1;
    }

What we do above is to create a new Algorand account and print its mnemonic and public address to the logcat. You can proceed to the TestNet dispenser to fund this account.

4. Creating a Keypair with a Mnemonic String

let’s create a function called createAccountWithMnemonic that will create a keypair with mnemonic seed words.

    public   Account createAccountWithMnemonic(String mnemonic){
        Account myAccount1= null;
        try {
            myAccount1 = new Account(mnemonic);
            Log.d("algoDebug"," algod account address: " + myAccount1.getAddress());
            Log.d("algoDebug"," algod account MNEMONIC: " + myAccount1.toMnemonic());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            Log.d("algoDebug"," Eror while creating new account "+e);
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        }
        return  myAccount1;
    }

What we have above is a function that simply expects a mnemonic seed word, creates an Algorand account and prints the public address and the mnemonic seed word to the logcat. See this link for information on how to create a standalone account. Let’s proceed by learning how to check the balance of our created account.

5. Checking Account Balance

Lastly we will create a function to get the balance of a user using the public address.

    public  Long getWalletBalance(Address address) throws Exception {
        com.algorand.algosdk.v2.client.model.Account accountInfo = client.AccountInformation(address).execute().body();
        Log.d("algoDebug","Account Balance: "+ accountInfo.amount+" microAlgos");
        return  accountInfo.amount;
    }

The method above simply receives an Address object and passes it as an argument into the AccountInformation public method of the AlgodClient instance created earlier. Note that the Account Class used in the function above is not of the same package with the one we created earlier.

For the method above to work, you’ll need internet permissions on Android so make sure to add internet permissions to your AndroidManifest.xml file like this.

<uses-permission android:name="android.permission.INTERNET"/>

6. Full MainActivity.java code

package com.example.algorandv2beginner1tutorial;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;

import com.algorand.algosdk.account.Account;
import com.algorand.algosdk.crypto.Address;
import com.algorand.algosdk.transaction.SignedTransaction;
import com.algorand.algosdk.transaction.Transaction;
import com.algorand.algosdk.util.Encoder;
import com.algorand.algosdk.v2.client.common.AlgodClient;
import com.algorand.algosdk.v2.client.common.Response;
import com.algorand.algosdk.v2.client.model.PendingTransactionResponse;
import com.algorand.algosdk.v2.client.model.TransactionParametersResponse;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.Security;

public class MainActivity extends AppCompatActivity {
    String ALGOD_API_ADDR = "http://hackathon.algodev.network";
    Integer ALGOD_PORT = 9100;
    String ALGOD_API_TOKEN = "ef920e2e7e002953f4b29a8af720efe8e4ecc75ff102b165e0472834b25832c1";
    AlgodClient client;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Security.removeProvider("BC");
        Security.insertProviderAt(new BouncyCastleProvider(), 0);
        String providerName = "BC";

        if (Security.getProvider(providerName) == null)
        {
            Log.d("algoDebug",providerName + " provider not installed");
        }
        else
        {
            Log.d("algoDebug",providerName + " is installed.");
        }

        client = (AlgodClient) new AlgodClient(ALGOD_API_ADDR,
                ALGOD_PORT, ALGOD_API_TOKEN);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Address address=new Address("ZY72XHRFKHPGPRN7MGN5FSPUZRGGZZ63HTEN7UGYV6LVZOKCEFOEN6TGHE");
                    getWalletBalance(address);
                    gettingStartedExample();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
        createAccountWithoutMnemonic();
        Account testAccount=createAccountWithMnemonic("degree secret exhibit pond toddler elbow message input can shield park educate gallery notice ten vintage scale close possible earn fat source define able fluid");


    }
    public  Long getWalletBalance(Address address) throws Exception {
        com.algorand.algosdk.v2.client.model.Account accountInfo = client.AccountInformation(address).execute().body();
        Log.d("algoDebug","Account Balance: "+ accountInfo.amount+" microAlgos");
        return  accountInfo.amount;
    }

    //Method that restores an account from an existing Mnemonic String
    public static Account createAccountWithMnemonic(String mnemonic){
        Account myAccount1= null;
        try {
            myAccount1 = new Account(mnemonic);
            Log.d("algoDebug"," algod account address: " + myAccount1.getAddress());
            Log.d("algoDebug"," algod account MNEMONIC: " + myAccount1.toMnemonic());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            Log.d("algoDebug"," Eror while creating new account "+e);
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
        }
        return  myAccount1;
    }

    //Method that creates a new account without using an existing mnemonic String
    public  void createAccountWithoutMnemonic( ){
        Account myAccount1= null;

        try {
            myAccount1 = new Account();
            Log.d("algoDebug"," algod account address: " + myAccount1.getAddress());
            Log.d("algoDebug"," algod account MNEMONIC: " + myAccount1.toMnemonic());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            Log.d("algoDebug"," Eror while creating new account "+e);
        }
    }
    public  void waitForConfirmation(String txID) throws Exception {
        //        if (client == null)
        //            this.client = connectToNetwork();
        Long lastRound = client.GetStatus().execute().body().lastRound;
        while (true) {
            try {
                // Check the pending tranactions
                Response<PendingTransactionResponse> pendingInfo = client.PendingTransactionInformation(txID).execute();
                if (pendingInfo.body().confirmedRound != null && pendingInfo.body().confirmedRound > 0) {
                    // Got the completed Transaction
                    Log.d("algoDebug", "Transaction " + txID + " confirmed in round " + pendingInfo.body().confirmedRound);
                    break;
                }
                lastRound++;
                client.WaitForBlock(lastRound).execute();
            } catch (Exception e) {
                throw (e);
            }
        }
    }

    public  void gettingStartedExample() throws Exception {
        // Import your private key mnemonic and address
        final String PASSPHRASE = "patrol target joy dial ethics flip usual fatigue bulb security prosper brand coast arch casino burger inch cricket scissors shoe evolve eternal calm absorb school";
        com.algorand.algosdk.account.Account myAccount = new Account(PASSPHRASE);
        Log.d("algoDebug","My Address: " + myAccount.getAddress());

        String myAddress = myAccount.getAddress().toString();

        com.algorand.algosdk.v2.client.model.Account accountInfo = client.AccountInformation(myAccount.getAddress())
                .execute().body();

        Log.d("algoDebug",String.format("Account Balance: %d microAlgos", accountInfo.amount));

        try {
        // Construct the transaction
            final String RECEIVER = "L5EUPCF4ROKNZMAE37R5FY2T5DF2M3NVYLPKSGWTUKVJRUGIW4RKVPNPD4";
            String note = "Hello World";
            TransactionParametersResponse params = client.TransactionParams().execute().body();
            Transaction txn = Transaction.PaymentTransactionBuilder().sender(myAddress).note(note.getBytes())
                    .amount(100000).receiver(new Address(RECEIVER)).suggestedParams(params).build();

        // Sign the transaction
            SignedTransaction signedTxn = myAccount.signTransaction(txn);
            Log.d("algoDebug","Signed transaction with txid: " + signedTxn.transactionID);

        // Submit the transaction to the network
            byte[] encodedTxBytes = Encoder.encodeToMsgPack(signedTxn);
            String id = client.RawTransaction().rawtxn(encodedTxBytes).execute().body().txId;
            Log.d("algoDebug","Successfully sent tx with ID: " + id);

        // Wait for transaction confirmation
            waitForConfirmation(id);

    // Read the transaction
            PendingTransactionResponse pTrx = client.PendingTransactionInformation(id).execute().body();
            Log.d("algoDebug","Transaction information (with notes): " + pTrx.toString());
            Log.d("algoDebug","Decoded note: " + new String(pTrx.txn.tx.note));

        } catch (Exception e) {
            Log.e("algoDebug","Exception when calling algod#transactionInformation: " + e.getMessage());
        }
    }

}

The code above contains two methods that were not explained which are the gettingStartedExample and the waitForConfirmation method, you can find more info on them here.

Things to Note

  • You can find a link to the completed sample project code here.
  • You can also find a link to an android showcase example with a frontend here.
  • You can also find a link to download the APK of the showcase here.
  • This tutorial was done using the Gradle plugin 3.6.1 and Gradle build tools version 5.6.4.
  • BigInteger is used instead of int for a higher precision while making calculations.
  • It is important to remember to put most of these operations(especially the getWalletBalance method) in another thread using Coroutines, Thread (used above in Step 6) or AsyncTasks, you can find more info on Threads here.

token

java

accounts

Android

October 29, 2020