Remote Configuration for Android using Alibaba Cloud Table Store

Image for post
Image for post

By Sai Sarath Chandra Alibaba Cloud Tech Share Author and Alibaba Cloud MVP

In this tutorial, you will learn how to use Table Store to create a simple user information database with a form accessible on mobile devices.

Prerequisites:

• You need an Alibaba Cloud Account. If you need you can get one with $300 by signing up here.
• Need Android Studio 3.0.0
• Basic knowledge of Android/Java.

Tutorial:

1.Activate Table Store Service:

a) Please login to Alibaba Cloud Console and Activate the Table Store service, if you are using this for the first time.

Image for post
Image for post

b) If you haven’t used before you will see a “Activate Now” button. Upon which the service will be activated.
c) Go ahead to the Table Store console and “Create Instance” in the region you are interested in.
d) Fill in the relevant details here.
e) And your Instance should be created. Please click on the Instance and copy the endpoint.
f) This is the Internet endpoint is what we are interested in. Please copy it and keep it for future reference.
g) You can follow the below Illustrative guide for the obtaining access keys.
https://www.alibabacloud.com/help/doc-detail/51665.htm?spm=a3c0i.o51500en.b99.9.733cc1c22ZHdyM

2.Create an Android Project with the name you want

Image for post
Image for post

a) Select next and I have selected the minimum target Android version as Android Kitkat. Please find the screenshot below.

Image for post
Image for post

b) Click next and select empty activity

Image for post
Image for post

c) Click next and leave the defaults as is and select “Finish” button

Image for post
Image for post

d) Let the Gradle build finish and then follow ahead

3.Downloading dependencies :

a) We need to download 2 jar files which are the core for using the Alibaba Cloud. Please use the following link and download “tablestore-4.2.0-jar-with-dependencies.jar” & “slf4j-api-1.7.7.jar”.
http://central.maven.org/maven2/com/aliyun/openservices/tablestore/4.2.0/
http://central.maven.org/maven2/org/slf4j/slf4j-api/1.7.7/
b) Add the jars into the libs folder in the projectview and add the jars to the dependencies

Image for post
Image for post

c) Make sure you right click the jar and the select “Add as library”.

4.Adding dependencies to app level gradle.

The following should be your dependencies in gradle.

dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:26.1.0'
compile 'com.android.support:design:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
compile 'joda-time:joda-time:2.9.9'
compile 'com.android.support:design:26.1.0'
implementation files('libs/tablestore-4.2.0-jar-with-dependencies.jar')
implementation files('libs/slf4j-api-1.7.7.jar')
}

Code level Guide:

Project Files:

1.We have three layouts

a) activity_main.xml
b) activity_config.xml
c) activity_welcome.xml

2.we have three activities

a) WelcomeActivity.java
b) RemoteConfig.java
c) MainActivity.java

3.One Strings.xml file, which handles the externalization of strings in the project.

4.One Styles.xml, which externalizes the styles in the project

Deep Dive:

1.First of all, we will talk about creating the layouts of the application. For reference, the following screenshot shows how the final layout will look like:

Image for post
Image for post

To achieve this create activity_welcome.xml in res > layout & copy the following code.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/alibaba_logo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginBottom="16dp"
android:layout_marginTop="32dp"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:src="@drawable/alibaba_cloud" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/alibaba_logo"
android:layout_margin="8dp"
android:orientation="horizontal">
<Button
android:id="@+id/userForm"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="UserForm" />
<Button
android:id="@+id/remoteConfig"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="App Congig" />
</LinearLayout>
</RelativeLayout>

This is a relative layout which consists of a Image View and Two Button. It is pretty simple layout

2.Create activity_config.xml and copy the following code in the same way what we have did earlier.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/headingText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="Character Count"
android:textAlignment="center"
android:textSize="18sp" />
<android.support.design.widget.TextInputLayout
android:id="@+id/countLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/headingText"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:textColorHint="@color/colorPrimary"
app:hintTextAppearance="@style/TextLabel">
<EditText
android:id="@+id/countValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:hint="Character Count"
android:inputType="text"
android:textSize="18sp" />
</android.support.design.widget.TextInputLayout> <android.support.design.widget.TextInputLayout
android:id="@+id/ageMinLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/countLayout"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:textColorHint="@color/colorPrimary"
app:hintTextAppearance="@style/TextLabel">
<EditText
android:id="@+id/ageMinValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:hint="Minimum Age"
android:inputType="text"
android:textSize="18sp" />
</android.support.design.widget.TextInputLayout> <android.support.design.widget.TextInputLayout
android:id="@+id/ageMaxLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/ageMinLayout"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:textColorHint="@color/colorPrimary"
app:hintTextAppearance="@style/TextLabel">
<EditText
android:id="@+id/ageMaxValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:hint="Maximum Age"
android:inputType="text"
android:textSize="18sp" />
</android.support.design.widget.TextInputLayout> <android.support.design.widget.TextInputLayout
android:id="@+id/imageLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/ageMaxLayout"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:textColorHint="@color/colorPrimary"
app:hintTextAppearance="@style/TextLabel">
<EditText
android:id="@+id/imageValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:hint="Image value"
android:inputType="text"
android:textSize="18sp" />
</android.support.design.widget.TextInputLayout>
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/imageLayout"
android:layout_centerHorizontal="true"
android:layout_marginBottom="16dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="16dp"
android:text="Update Configuration" />
<TextView
android:id="@+id/output"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/button"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:text="Updated Config:"
android:textAlignment="center"
android:textColor="#000000"
android:textSize="18sp" />
</RelativeLayout>

3.The final layout of the file will look something like this:

Image for post
Image for post

4.Create activity_main.xml and copy the following code. The final layout will look something like this:

Image for post
Image for post
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="sample.alibabacloud.remoteconfig.MainActivity">
<ImageView
android:id="@+id/backgroundImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.7"
android:scaleType="centerCrop"
android:src="@drawable/background_2" />

<TextView
android:id="@+id/headingText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="User Form"
android:textAlignment="center"
android:textSize="18sp" />
<android.support.design.widget.TextInputLayout
android:id="@+id/nameInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/headingText"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:textColorHint="@color/colorPrimary"
app:hintTextAppearance="@style/TextLabel">
<EditText
android:id="@+id/nameValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:hint="@string/NameHint"
android:inputType="text"
android:textSize="18sp" />
</android.support.design.widget.TextInputLayout> <TextView
android:id="@+id/ageHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/nameInputLayout"
android:layout_marginBottom="4dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:text="Age"
android:textSize="14sp" />
<Spinner
android:id="@+id/ageValue"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/ageHeader"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:spinnerMode="dialog" />
<TextView
android:id="@+id/genderHeader"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/ageValue"
android:layout_marginBottom="4dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:text="Gender"
android:textSize="14sp" />
<RadioGroup
android:id="@+id/radioGender"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/genderHeader"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp">
<RadioButton
android:id="@+id/radioMale"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="Male"
android:textSize="18sp" />
<RadioButton
android:id="@+id/radioFemale"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Female"
android:textSize="18sp" />
</RadioGroup> <Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/radioGender"
android:layout_centerHorizontal="true"
android:layout_marginBottom="16dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="16dp"
android:text="Submit Form" />
<TextView
android:id="@+id/output"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/button"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:text="Click 'SUBMIT FORM' to see output"
android:textAlignment="center"
android:textColor="#000000"
android:textSize="18sp" />
</RelativeLayout>

5.Since we have created all the layouts, we will now work on creating the Activities. Create MainActivity.java file by right clicking on the package > New > Java Class and copy the following code

package sample.alibabacloud.remoteconfig;import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.design.widget.TextInputLayout;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements View.OnClickListener { //Creating local objects
TextInputLayout nameInputLayout;
EditText nameValue;
Spinner ageValue;
String[] ageArr;
RadioGroup radioGender;
RadioButton radioMale, radioFemale, selectedRdoBtn;
Button button;
TextView output;
ImageView backgroundImage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
nameInputLayout = findViewById(R.id.nameInputLayout);
nameValue = findViewById(R.id.nameValue);
ageValue = findViewById(R.id.ageValue);
radioGender = findViewById(R.id.radioGender);
radioMale = findViewById(R.id.radioMale);
radioFemale = findViewById(R.id.radioFemale);
button = findViewById(R.id.button);
output = findViewById(R.id.output);
backgroundImage = findViewById(R.id.backgroundImage);
nameInputLayout.setCounterEnabled(true); int minAge;
int maxAge;
//Logic for checking the App is installed first or existing
SharedPreferences sharedPreferences = getSharedPreferences(WelcomeActivity.SHARED_PREF_FILE_NAME,MODE_PRIVATE);
boolean isPresent = sharedPreferences.contains(WelcomeActivity.COUNT_KEY);
if(isPresent){
minAge = Integer.parseInt(sharedPreferences.getString(WelcomeActivity.MIN_AGE,null));
maxAge = Integer.parseInt(sharedPreferences.getString(WelcomeActivity.MAX_AGE,null));
nameInputLayout.setCounterMaxLength(Integer.parseInt(sharedPreferences.getString(WelcomeActivity.COUNT_KEY,null)));
if(sharedPreferences.getString(WelcomeActivity.IMG_NUM,null).equals("1")){
backgroundImage.setImageResource(R.drawable.background_1);
}else{
backgroundImage.setImageResource(R.drawable.background_2);
}
}else{
//if the app opening is new
minAge = 18;
maxAge = 35;
nameInputLayout.setCounterMaxLength(20);
}
ageArr = new String[maxAge-minAge+1];
for(int i =minAge;i<=maxAge;i++){
ageArr[i-minAge] = String.valueOf(i);
}
// set the values into the spinner
ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_spinner_item,ageArr);
ageValue.setAdapter(adapter);
button.setOnClickListener(this);
} @Override
public void onClick(View view) {
StringBuffer opStringBuf = new StringBuffer(); int id = view.getId();
if(id == R.id.button){
opStringBuf.append("Form Submitted : ");
// Get Name Input
opStringBuf.append("\n Name given : "+nameValue.getEditableText());
// Get Selected Age
opStringBuf.append("\n"+"The age selected is : "+ageValue.getSelectedItem());
// Get selected Gender
int selectedRadio = radioGender.getCheckedRadioButtonId();
selectedRdoBtn = findViewById(selectedRadio);
opStringBuf.append("\n"+"selected Gender : "+selectedRdoBtn.getText());
output.setText(opStringBuf); } }}

6.Create RemoteConfig.java file and copy the following code

package sample.alibabacloud.remoteconfig;import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.alicloud.openservices.tablestore.ClientException;
import com.alicloud.openservices.tablestore.SyncClient;
import com.alicloud.openservices.tablestore.TableStoreException;
import com.alicloud.openservices.tablestore.model.Column;
import com.alicloud.openservices.tablestore.model.ColumnValue;
import com.alicloud.openservices.tablestore.model.PrimaryKey;
import com.alicloud.openservices.tablestore.model.PrimaryKeyBuilder;
import com.alicloud.openservices.tablestore.model.PrimaryKeyValue;
import com.alicloud.openservices.tablestore.model.PutRowRequest;
import com.alicloud.openservices.tablestore.model.RowPutChange;
import com.alicloud.openservices.tablestore.model.RowUpdateChange;
import com.alicloud.openservices.tablestore.model.UpdateRowRequest;
import java.util.ArrayList;
import java.util.List;
import sample.alibabacloud.remoteconfig.model.ColumnData;import static sample.alibabacloud.remoteconfig.WelcomeActivity.TABLE_NAME;public class RemoteConfig extends Activity implements View.OnClickListener { //Fetch class level variables
Button button;
EditText countValue,ageMinValue,ageMaxValue,imageValue;
TextView headingText,output;
private final static String TAG = "RemoteConfig"; @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_config);
button = findViewById(R.id.button);
countValue = findViewById(R.id.countValue);
ageMinValue = findViewById(R.id.ageMinValue);
ageMaxValue = findViewById(R.id.ageMaxValue);
imageValue = findViewById(R.id.imageValue);
headingText = findViewById(R.id.headingText);
output = findViewById(R.id.output);
button.setOnClickListener(this);
}
@Override
public void onClick(View view) {
int id = view.getId();
if(id == R.id.button){
SyncClient client = new SyncClient(getString(R.string.Endpoint), getString(R.string.AccessKey), getString(R.string.AccessKeySecret),
getString(R.string.InstanceName));
updateConfigValues updateConfigValues = new updateConfigValues();
updateConfigValues.execute(client);
}
}
private static void updateRow(SyncClient client, ColumnData cData) {
// Creating Primary Key
PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn(WelcomeActivity.PRIMARY_KEY_NAME, PrimaryKeyValue.fromString(WelcomeActivity.PRIMARY_KEY_VALUE));
PrimaryKey primaryKey = primaryKeyBuilder.build();
// Creating list of Columns
List<Column> columnsList = new ArrayList<>();
columnsList.add(new Column(WelcomeActivity.COUNT_KEY,ColumnValue.fromString(cData.getCharCount())));
columnsList.add(new Column(WelcomeActivity.MIN_AGE,ColumnValue.fromString(cData.getMinAge())));
columnsList.add(new Column(WelcomeActivity.MAX_AGE,ColumnValue.fromString(cData.getMaxAge())));
columnsList.add(new Column(WelcomeActivity.IMG_NUM,ColumnValue.fromString(cData.getImageNum())));
RowUpdateChange rowUpdateChange = new RowUpdateChange(TABLE_NAME, primaryKey); rowUpdateChange.put(columnsList); try {
client.updateRow(new UpdateRowRequest(rowUpdateChange));
} catch (TableStoreException ex) {
Log.e(TAG, "updateRow: Error "+ex);
ex.printStackTrace();
}
RowPutChange rowPutChange = new RowPutChange(TABLE_NAME, primaryKey); rowPutChange.addColumn(WelcomeActivity.COUNT_KEY,ColumnValue.fromString(cData.getCharCount()));
rowPutChange.addColumn(WelcomeActivity.MIN_AGE,ColumnValue.fromString(cData.getMinAge()));
rowPutChange.addColumn(WelcomeActivity.MAX_AGE,ColumnValue.fromString(cData.getMaxAge()));
rowPutChange.addColumn(WelcomeActivity.IMG_NUM,ColumnValue.fromString(cData.getImageNum()));
client.putRow(new PutRowRequest(rowPutChange));
}
class updateConfigValues extends AsyncTask<SyncClient, Void, Void> { ProgressDialog loading = new ProgressDialog(RemoteConfig.this);
SyncClient client;
@Override
protected void onPreExecute() {
super.onPreExecute();
loading.setMessage("Updating App Config Values");
loading.show();
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
loading.dismiss();
}
@Override
protected Void doInBackground(SyncClient... syncClients) {
try {
client = syncClients[0];

try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}

updateRow( client ,new ColumnData(countValue.getText().toString(),ageMinValue.getText().toString(),ageMaxValue.getText().toString(),imageValue.getText().toString()));
runOnUiThread(new Runnable() {
@Override
public void run() {
updateOutput();
}
});
} catch (TableStoreException e) {
Log.e(TAG, "TableStoreException: Request error Message : " + e.getMessage());
Log.e(TAG, "TableStoreException: Request error ID : " + e.getRequestId());
} catch (ClientException e) {
Log.e(TAG, "ClientException: Request error Message : " + e.getMessage());
} finally {

}
return null;
}
}
private void updateOutput() {
StringBuffer opBuf = new StringBuffer();
opBuf.append("Updated Config:\n")
.append("Character Count : ")
.append(countValue.getText())
.append("\nMinimum Age : ")
.append(ageMinValue.getText())
.append("\nMaximum Age : ")
.append(ageMaxValue.getText())
.append("\nUpdated Image Value : ")
.append(imageValue.getText());

output.setText(opBuf);
}
}

7.Create WelcomeActivity.java and copy the following code

package sample.alibabacloud.remoteconfig;import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.alicloud.openservices.tablestore.ClientException;
import com.alicloud.openservices.tablestore.SyncClient;
import com.alicloud.openservices.tablestore.TableStoreException;
import com.alicloud.openservices.tablestore.model.ColumnValue;
import com.alicloud.openservices.tablestore.model.CreateTableRequest;
import com.alicloud.openservices.tablestore.model.GetRowRequest;
import com.alicloud.openservices.tablestore.model.GetRowResponse;
import com.alicloud.openservices.tablestore.model.PrimaryKey;
import com.alicloud.openservices.tablestore.model.PrimaryKeyBuilder;
import com.alicloud.openservices.tablestore.model.PrimaryKeySchema;
import com.alicloud.openservices.tablestore.model.PrimaryKeyType;
import com.alicloud.openservices.tablestore.model.PrimaryKeyValue;
import com.alicloud.openservices.tablestore.model.PutRowRequest;
import com.alicloud.openservices.tablestore.model.Row;
import com.alicloud.openservices.tablestore.model.RowPutChange;
import com.alicloud.openservices.tablestore.model.SingleRowQueryCriteria;
import com.alicloud.openservices.tablestore.model.TableMeta;
import com.alicloud.openservices.tablestore.model.TableOptions;
import sample.alibabacloud.remoteconfig.model.ColumnData;/**
* Created by saisarathchandra on 06/12/17.
*/
public class WelcomeActivity extends Activity implements View.OnClickListener {
public final static String COUNT_KEY = "CHARACTER_COUNT";
public final static String MIN_AGE = "MINIMUM_AGE";
public final static String MAX_AGE = "MAXIMUM_AGE";
public final static String IMG_NUM = "IMAGE_NUMBER";
public final static String DEF_COUNT_VALUE = "20";
public final static String DEF_MIN_AGE = "18";
public final static String DEF_MAX_AGE = "35";
public final static String DEF_IMG_NUM = "1";
private final static String TAG = "WelcomeActivity";
public final static String SHARED_PREF_FILE_NAME = "RemoteConfigPref";
public static final String PRIMARY_KEY_NAME = "Version";
public static final String PRIMARY_KEY_VALUE = "User1";
public static final String TABLE_NAME = "RemoteConfiguration";
//Fetch class level variables
Button userForm,remoteConfig;
static SharedPreferences sharedPreferences;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_welcome);
userForm = findViewById(R.id.userForm);
remoteConfig = findViewById(R.id.remoteConfig);
userForm.setOnClickListener(this);
remoteConfig.setOnClickListener(this);
sharedPreferences = getSharedPreferences(SHARED_PREF_FILE_NAME,MODE_PRIVATE);
boolean isPresent = sharedPreferences.contains(COUNT_KEY);
if(!isPresent){
// create Table & Shared preferences and put initial values
SyncClient client = new SyncClient(getString(R.string.Endpoint), getString(R.string.AccessKey), getString(R.string.AccessKeySecret),
getString(R.string.InstanceName));
CreateTableNDefValues createTableNDefValues = new CreateTableNDefValues();
createTableNDefValues.execute(client);
SharedPreferences.Editor prefFileEdit = sharedPreferences.edit(); prefFileEdit.putString(COUNT_KEY,DEF_COUNT_VALUE);
prefFileEdit.putString(MIN_AGE,DEF_MIN_AGE);
prefFileEdit.putString(MAX_AGE,DEF_MAX_AGE);
prefFileEdit.putString(IMG_NUM,DEF_IMG_NUM);
prefFileEdit.apply();
}else{
SyncClient client = new SyncClient(getString(R.string.Endpoint), getString(R.string.AccessKey), getString(R.string.AccessKeySecret),
getString(R.string.InstanceName));
GetRowValues getRowValues = new GetRowValues();
getRowValues.execute(client);
}
} @Override
protected void onResume() {
super.onResume();
SyncClient client = new SyncClient(getString(R.string.Endpoint), getString(R.string.AccessKey), getString(R.string.AccessKeySecret),
getString(R.string.InstanceName));
GetRowValues getRowValues = new GetRowValues();
getRowValues.execute(client);
}
//Create a Table with a Primary Key Defined with some Additional Parameters
private static void createTable(SyncClient client) {
TableMeta tableMeta = new TableMeta(TABLE_NAME);
tableMeta.addPrimaryKeyColumn(new PrimaryKeySchema(PRIMARY_KEY_NAME, PrimaryKeyType.STRING));
int timeToLive = -1;
int maxVersions = 1;
TableOptions tableOptions = new TableOptions(timeToLive, maxVersions);
CreateTableRequest request = new CreateTableRequest(tableMeta, tableOptions);
client.createTable(request);
}
private static void updatePreferences(Row row){

SharedPreferences.Editor prefFileEdit = sharedPreferences.edit();
prefFileEdit.putString(COUNT_KEY,row.getColumn(COUNT_KEY).get(0).getValue().toString());
prefFileEdit.putString(MIN_AGE,row.getColumn(MIN_AGE).get(0).getValue().toString());
prefFileEdit.putString(MAX_AGE,row.getColumn(MAX_AGE).get(0).getValue().toString());
prefFileEdit.putString(IMG_NUM,row.getColumn(IMG_NUM).get(0).getValue().toString());
prefFileEdit.apply(); } private static void putRow(SyncClient client, ColumnData cData) {
// Creating Primary Key
PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString(PRIMARY_KEY_VALUE));
PrimaryKey primaryKey = primaryKeyBuilder.build();
RowPutChange rowPutChange = new RowPutChange(TABLE_NAME, primaryKey);// if you wish to have timestamp, add it as the third parameter to the addColumn method
// long ts = System.currentTimeMillis();
rowPutChange.addColumn(COUNT_KEY,ColumnValue.fromString(cData.getCharCount()));
rowPutChange.addColumn(MIN_AGE,ColumnValue.fromString(cData.getMinAge()));
rowPutChange.addColumn(MAX_AGE,ColumnValue.fromString(cData.getMaxAge()));
rowPutChange.addColumn(IMG_NUM,ColumnValue.fromString(cData.getImageNum()));
client.putRow(new PutRowRequest(rowPutChange));
}
private static void getRow(SyncClient client) {

PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME, PrimaryKeyValue.fromString(PRIMARY_KEY_VALUE));
PrimaryKey primaryKey = primaryKeyBuilder.build();

SingleRowQueryCriteria criteria = new SingleRowQueryCriteria(TABLE_NAME, primaryKey);

criteria.setMaxVersions(1);
GetRowResponse getRowResponse = client.getRow(new GetRowRequest(criteria));
Row row = getRowResponse.getRow();
Log.d(TAG, "getRow: Row Received Compelte");
Log.d(TAG, "getRow:" + row);
String[] columnNames = new String[]{WelcomeActivity.COUNT_KEY,
WelcomeActivity.MIN_AGE,
WelcomeActivity.MAX_AGE,WelcomeActivity.IMG_NUM};
criteria.addColumnsToGet(columnNames);
getRowResponse = client.getRow(new GetRowRequest(criteria));
row = getRowResponse.getRow();
Log.d(TAG, "getRow: Row Received after get Operation");
Log.d(TAG, "getRow:" + row);
updatePreferences(row);
}
@Override
public void onClick(View view) {
int id = view.getId();
if(id == R.id.userForm){
Intent intent = new Intent(this,MainActivity.class);
startActivity(intent);
}else if (id == R.id.remoteConfig){
Intent intent = new Intent(this,RemoteConfig.class);
startActivity(intent);
}
}
class CreateTableNDefValues extends AsyncTask<SyncClient, Void, Void> { ProgressDialog loading = new ProgressDialog(WelcomeActivity.this);
SyncClient client;
@Override
protected void onPreExecute() {
super.onPreExecute();
loading.setMessage("Creating Tables and Setting Values");
loading.show();
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
loading.dismiss();
}
@Override
protected Void doInBackground(SyncClient... syncClients) {
try {
client = syncClients[0]; // Create Table
createTable(client);
// Give some time to load
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Creating Object with all the values
ColumnData cData = new ColumnData();
cData.setCharCount(DEF_COUNT_VALUE);
cData.setMinAge(DEF_MIN_AGE);
cData.setMaxAge(DEF_MAX_AGE);
cData.setImageNum(DEF_IMG_NUM);
// putRow
putRow(client, cData);
} catch (TableStoreException e) {
Log.e(TAG, "TableStoreException: Request error Message : " + e.getMessage());
Log.e(TAG, "TableStoreException: Request error ID : " + e.getRequestId());
} catch (ClientException e) {
Log.e(TAG, "ClientException: Request error Message : " + e.getMessage());
} finally {
// Do your maintainence activites here
}
// client.shutdown();
return null;
}
}
class GetRowValues extends AsyncTask<SyncClient, Void, Void> { ProgressDialog loading = new ProgressDialog(WelcomeActivity.this);
SyncClient client;
@Override
protected void onPreExecute() {
super.onPreExecute();
loading.setMessage("Fetching updated values");
loading.show();
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
loading.dismiss();
}
@Override
protected Void doInBackground(SyncClient... syncClients) {
try {
client = syncClients[0]; // Give some time to load
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
getRow(client); } catch (TableStoreException e) {
Log.e(TAG, "TableStoreException: Request error Message : " + e.getMessage());
Log.e(TAG, "TableStoreException: Request error ID : " + e.getRequestId());
} catch (ClientException e) {
Log.e(TAG, "ClientException: Request error Message : " + e.getMessage());
} finally {
// Do your maintainence activites here
}
return null;
}
}
}

8.Final configuration files:

• Open your “AndroidManifest.xml”and copy the following code

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="sample.alibabacloud.remoteconfig">
<uses-permission android:name="android.permission.INTERNET"/> <application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".WelcomeActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity"/>
<activity android:name=".RemoteConfig"/>
</application>
</manifest>

• Open your styles.xml under the values folder and copy the following one.

<resources>    <!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="TextLabel" parent="TextAppearance.Design.Hint">
<item name="android:textSize">14sp</item>
</style>
</resources>

• Update the strings.xml with the values you store earlier

<resources>
<string name="app_name">RemoteConfig</string>
<!-- Alibaba TableStore Service details-->
<!-- Please replace this details with your own-->
<!--Public Endpoint-->
<string name="Endpoint"></string>
<!-- Access ID -->
<string name="AccessKey"></string>
<!-- Access key Secret -->
<string name="AccessKeySecret"></string>
<!-- Queue Names -->
<string name="InstanceName">AndroidDemo</string>

<!--App Related Strings-->
<string name="NameHint">Name</string>
</resources>

Detailed Code Explanation:

  1. In this Remote Configuration project, we are storing some app attributes like the maximum number of characters that the person can enter for his name, minimum age and the maximum age allowed, background image. We can see the user app analytics, review comments in appstore and sometimes we think we need to change some parameters for the code. We tend to launch and update which may not be efficient as we can achieve the same through remote configuration.
  2. When the Code loads the activity_welcome.xml is loaded and the respective WelcomeActivity is initialized. This happens through the WeclomeActivity declaration in the Android manifest.
  3. The welcome activity is responsible for checking the app is installed in the device for the first time or is it installed already. We use this scenario to setup the local secured
    SharedPreferences file.
  4. If the app is opening for the first time we will create a table called “Remote Configuration”& Create a primary key named “Version”. And we will update the values to be default. This will be onetime activity along side we will also create the sharedpreferences file and update the values to be default.
  5. We will also copy the same logic onto the onPause() method so whenever the user navigates to the home screen the app can fetch the latest values from the server.
  6. The UserForm screen is responsible for checking the latest values from the TableStore and updating the same in the form.
  7. The RemoteConfig is responsible for updating the values in the TableStore.
  8. You can see all the methods are self-explanatory.
  9. If everything is done correctly all your compilation issues will go away and the application starts installing in by clicking the small play(run) button in the status bar of the android studio.
Image for post
Image for post

I strongly recommend to clone the repo you will eliminate lot of manual errors and get this app running in minutes.

Please take a look at this repo for the final code repo and let me know if you face any issues or raise any pull requests for improvements.

Reference:

https://www.alibabacloud.com/blog/Remote-Configuration-for-Android-using-Alibaba-Cloud-Table-Store_p313872?spm=a2c41.11214695.0.0

Written by

Follow me to keep abreast with the latest technology news, industry insights, and developer trends.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store