Kin Android development tutorial — part 8— Connecting the app with the app backend for earns

Luc Hendriks
Sep 19, 2019 · 7 min read

This is part 8 of the Android development tutorial series. In this part, we are going to connect the app with the app backend so we can create actual earns. To be able to handle earns in our application, there are two steps involved: first, we need to register the Kin address with the associated device ID. Second, we need to send earn requests whenever the user has earned something.

For the sake of simplicity, the user can earn 10 Kin for pressing a button. But this only works every 2 minutes per the verification rules we built in the app backend.

Registering the Kin address in the app backend

The first step is to register the Kin address in the app backend, using the register endpoint we created in the previous part. This has to be done only once. To make sure we only register once, we can save whether we have registered using shared preferences. You can take a look at the code, here is the full reference. The file we need to change is the Kin class file (app > java > com.kincompany.kindevtutorial > Kin.java):

package com.kincompany.kindevtutorial;

import android.app.Application;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.util.Log;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

import kin.sdk.AccountStatus;
import kin.sdk.Balance;
import kin.sdk.Environment;
import kin.sdk.KinAccount;
import kin.sdk.KinClient;
import kin.sdk.exception.CreateAccountException;
import kin.utils.Request;
import kin.utils.ResultCallback;
import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Response;

public class Kin extends Application {
private KinClient kinClient;
private KinAccount kinAccount;
private String URL_CREATE_ACCOUNT = "https://friendbot.developers.kinecosystem.com?addr=%s&amount=100";
private String URL_REGISTER_APP_BACKEND = "http://YOUR-BACKEND-IP:8001/register?device_id=%s&kin_address=%s";
// Moved this
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.build();



public Kin() {
Log.i("main", "Constructor fired");
}

private void getBalance() {
Request<Balance> balanceRequest = this.kinAccount.getBalance();
balanceRequest.run(new ResultCallback<Balance>() {

@Override
public void onResult(Balance result) {
Log.d("main", "The balance is: " + result.value(2));
}

@Override
public void onError(Exception e) {
e.printStackTrace();
}
});
}

private void registerAccount() {
final Kin kin = this;
okhttp3.Request request = new okhttp3.Request.Builder()
.url(String.format(this.URL_CREATE_ACCOUNT, this.kinAccount.getPublicAddress()))
.get()
.build();

okHttpClient.newCall(request)
.enqueue(new okhttp3.Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
Log.i("main", "Something went wrong");
}

@Override
public void onResponse(@NonNull Call call, @NonNull Response response) {
kin.getBalance();
Log.i("main", "Kin account created");
}
});
}

private void checkAccountRegistered() {
final Kin kin = this;
Request<Integer> statusRequest = this.kinAccount.getStatus();
statusRequest.run(new ResultCallback<Integer>() {
@Override
public void onResult(Integer result) {
switch (result) {
case AccountStatus.CREATED:
Log.i("main", "Kin account already exists on the blockchain");
kin.getBalance();
break;
case AccountStatus.NOT_CREATED:
kin.registerAccount();
break;
}
}

@Override
public void onError(Exception e) {
Log.i("main","Something went wrong");
}
});
}

private void registerAccountOnAppBackend(final SharedPreferences sharedPreferences) {
String androidId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);

okhttp3.Request request = new okhttp3.Request.Builder()
.url(String.format(URL_REGISTER_APP_BACKEND, androidId, this.kinAccount.getPublicAddress()))
.get()
.build();

okHttpClient.newCall(request)
.enqueue(new okhttp3.Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
Log.i("main", "Something went wrong");
}

@Override
public void onResponse(@NonNull Call call, @NonNull Response response) {
Log.i("main", "Address registered on app backend");
// Save registered, so we don't do this again
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putBoolean("registered", true);
editor.apply();
}
});

}


@Override
public void onCreate() {
super.onCreate();

this.kinClient = new KinClient(this, Environment.TEST, "ABCD");

try {
if (!kinClient.hasAccount()) {
this.kinAccount = kinClient.addAccount();
} else {
this.kinAccount = kinClient.getAccount(0);
}
} catch (CreateAccountException e) {
e.printStackTrace();
}

this.checkAccountRegistered();

SharedPreferences sharedPreferences = PreferenceManager
.getDefaultSharedPreferences(this);
boolean registeredInBackend = sharedPreferences.getBoolean("registered", false);
if (registeredInBackend == false) {
Log.i("main", "Registering address on app backend");
this.registerAccountOnAppBackend(sharedPreferences);
}

Log.i("main", this.kinAccount.getPublicAddress());
}
}

Let’s start at the bottom. First, we check if the device ID has already been registered before, by checking for this in the shared preferences. If not, we call the registerAccountOnAppBackend function. That function takes the android device_id and the public address of the Kin account on the device, then sends it to the register endpoint of the app backend. When done, we set the registered parameter to “true” in the shared preferences, so next time we open the app it is not called again.

As in this example code we are sending the request over http, the request will initially be blocked by Android. If you create your backend, either enable https following this guide or disable the security rule following this guide. For production environments, I highly recommend the first.

Make sure the app backend server is running, and enable logging:

adb logcat -s 'main'

Open the AVD manager, start the virtual Android device, and run your code by clicking on the green “play” button. You should see a log entry saying:

08-23 12:35:52.175 22325 22361 I main    : Address registered on app backend

You can check in the users directory of the backend that the file exists with the correct Kin address in it. If you close and re-open the app, it should not register again. We can verify this by relaunching the app and noting that the log above is not there.

Sending the earn request

To send the earn request, we will create a button the user can click. If the user clicks on the button, the earn request will be sent and we will update the Kin balance on the screen.

Creating the button

Open the file res > layout > activity_main.xml. A screen will pop up with the current design of the app, like this:

Our current app “design”

Now drag and drop a “Button” from the palette on the left to the screen. I also renamed “Hello World!” to “Balance:” as we will use that text field to show the balance of the user. Here’s what I made:

App with earn button

Show the balance

To actually show the balance, we need to add the balance to the textView element.

The Kin class we created runs in the background and has no connection to the activity (the screen with the text and button). So in order to tell the activity that the text should be updated we can use PubSub: the activity subscribes to events. This means it listens for events and handles accordingly. The Kin class publishes events. For this we can use the excellent NYBus package. In the build.gradle file where we added the Kin SDK, add this line:

implementation "com.mindorks.nybus:nybus-android:1.0.0"

and resync (Android Studio will tell you to do this). Then we can send the balance to the textView. Update the getBalance function in Kin.java as follows:

...
import com.mindorks.nybus.NYBus;
import com.mindorks.nybus.event.Channel;
...
private void getBalance() {
Request<Balance> balanceRequest = this.kinAccount.getBalance();
balanceRequest.run(new ResultCallback<Balance>() {

@Override
public void onResult(Balance result) {
Log.d("main", "The balance is: " + result.value(2));
NYBus.get().post(result.value(2), Channel.ONE);
}

@Override
public void onError(Exception e) {
e.printStackTrace();
}
});
}

This posts the balance on channel 1 (we are going to use another channel for different data). Then update the MainActivity.java:

package com.kincompany.kindevtutorial;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import com.mindorks.nybus.NYBus;
import com.mindorks.nybus.annotation.Subscribe;
import com.mindorks.nybus.event.Channel;

public class MainActivity extends AppCompatActivity {

public void updateText(String text) {
TextView txtView = findViewById(R.id.textView);
txtView.setText(text);
}

@Subscribe(channelId = Channel.ONE)
public void onEvent(String balance) {
updateText("Balance: " + balance);
}


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Start listening on channel 1
NYBus.get().register(this, Channel.ONE);
}
}

When some data is posted on channel 1, the onEvent function calls the updateText with the balance. Try to run the app; you should see something like this:

Showing the balance on the device

Send the earn request

The earn request works the other way around. The button which lives in the MainActivity is clicked, and the Kin class needs to send the earn request. So this time, the MainActivity publishes and Kin subscribes.

In MainActivity.java, add the following method:

public void earnButtonClicked(View view) {
NYBus.get().post("x", Channel.TWO);
}

And in the Kin.java file, make the following changes:

1: Add the NYBus line below to the onCreate method.

@Override
public void onCreate() {
super.onCreate();

this.kinClient = new KinClient(this, Environment.TEST, "ABCD");

try {
if (!kinClient.hasAccount()) {
this.kinAccount = kinClient.addAccount();
} else {
this.kinAccount = kinClient.getAccount(0);
}
} catch (CreateAccountException e) {
e.printStackTrace();
}

this.checkAccountRegistered();

SharedPreferences sharedPreferences = PreferenceManager
.getDefaultSharedPreferences(this);
boolean registeredInBackend = sharedPreferences.getBoolean("registered", false);
if (registeredInBackend == false) {
Log.i("main", "Registering address on app backend");
this.registerAccountOnAppBackend(sharedPreferences);
}

NYBus.get().register(this, Channel.TWO);

Log.i("main", this.kinAccount.getPublicAddress());
}

2: Add a new method that listens on channel two (when the user clicks the earn button), and does the earn request.

@Subscribe(channelId = Channel.TWO)
public void onEvent(String clicked) {
String androidId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);

okhttp3.Request request = new okhttp3.Request.Builder()
.url(String.format(URL_EARN, androidId))
.get()
.build();

okHttpClient.newCall(request)
.enqueue(new okhttp3.Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
Log.i("main", "Something went wrong");
Log.i("main", e.toString());
}

@Override
public void onResponse(@NonNull Call call, @NonNull Response response) {
Log.i("main", "Earn sent");
// getBalances posts the balance back to the activity
getBalance();
}
});
}

3: Define the URL_EARN at the top:

private String URL_EARN = "http://34.245.20.131:8001/earn?id=%s&amount=10";

You also need to import a few things, but by now you should be able to do this :-)

Lastly, we need to tell the app that clicking the button triggers the earnButtonClicked function. In the activity_main.xml, you can click on the button and the on the right enter earnButtonClicked in the “onClick” field.

Now run the app, click the button, and wait a few seconds. The balance should update with 10 Kin! If you quickly click again, you’ll see it doesn’t work (because you have to wait 2 minutes).

Wrapup

We now have an actual earn in our app! You can click on a button and it will earn you 10 Kin. It may look simple, but hopefully you see that there is a lot going on beneath the surface. Next up, we’re going to add spend options!

Kin Blog

Kin is money. Earn, spend, and transfer value across an ecosystem of apps and services. Get paid for developing engaging user experiences with Kin.

Thanks to Tali Sachs

Luc Hendriks

Written by

I like to think about the future

Kin Blog

Kin Blog

Kin is money. Earn, spend, and transfer value across an ecosystem of apps and services. Get paid for developing engaging user experiences with Kin.

More From Medium

More on Kin from Kin Blog

Kevin R Ricoy
Mar 27 · 5 min read

123

More on Kin from Kin Blog

More on Kin from Kin Blog

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade