Creating a Simple Android App
The sample backend API we are using stores two Greetings. So, for our simple app, we'll create a UI that lets you do the following interactions with that backend data in this order:
- Sign into your Google account.
- Specify which of the two greetings to display.
- List all of the greetings.
- Supply your own custom greeting and an integer count with the backend returning that greeting multiplied count times.
- Get a greeting personalized with your account email.
The complete UI will look something like this:
Add the UI
To add the UI to support interactions with the backend API:
-
Open the Hello World project file
src/main/res/layout/activity_main.xml
in Android Studio, and replace all of the contents with the following layout and views:<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity"> <LinearLayout android:orientation="horizontal" android:layout_height="wrap_content" android:layout_width="fill_parent"> <TextView android:id="@+id/email_address_tv" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" android:text="Not signed in" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/sign_in_button" android:text="Sign in" android:onClick="onClickSignIn"/> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="5dip" android:background="#000000"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Get Greeting" /> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android:id="@+id/greeting_id_edit_text" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="right" android:inputType="number" android:hint="Greeting ID to GET"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:text="GET Greeting" android:onClick="onClickGetGreeting" /> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="5dip" android:background="#000000"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Multiply Greetings" /> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical"> <EditText android:id="@+id/greeting_text_edit_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:gravity="right" android:inputType="textAutoCorrect" android:hint="Greeting to send"/> <EditText android:id="@+id/greeting_count_edit_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:gravity="right" android:inputType="number" android:hint="Greeting count to send"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:onClick="onClickSendGreetings" android:text="Send Greetings"/> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="5dip" android:background="#000000"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Authenticated Greeting" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:text="Get Authenticated Greeting" android:onClick="onClickGetAuthenticatedGreeting"/> <View android:layout_width="match_parent" android:layout_height="30sp" /> <ListView android:id="@+id/greetings_list_view" android:layout_height="500sp" android:layout_width="fill_parent" android:layout_marginLeft="30sp" android:layout_marginRight="30sp" android:scrollbarAlwaysDrawVerticalTrack="true" tools:listitem="@android:layout/simple_list_item_1" android:text="No Messages retrieved yet"/> </LinearLayout> </ScrollView>
Modify
main.xml
Locate the file
src/main/res/menu/main.xml
and replace the contents with the
following:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/action_settings"
android:title="@string/action_settings"
android:orderInCategory="100"
android:showAsAction="never" />
</menu>
Modify
styles.xml
Locate the file
src/main/res/values/styles.xml
and replace
all
of the contents with the
following:
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
</resources>
Modify
strings.xml
We'll need to add some message strings expected by our error handlers. Locate
the file
src/main/res/values/strings.xml
and replace the contents with the
following:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Hello Endpoints</string>
<string name="action_settings">Settings</string>
<string name="hello_world">Hello world!</string>
<string name="toast_no_google_account_selected">No Google Account Selected</string>
<string name="toast_exception_checking_authorization">Exception checking authorization</string>
<string name="toast_no_google_accounts_registered">No Google Accounts Registered</string>
<string name="toast_only_one_google_account_registered">Your only registered Google account was
selected
</string>
</resources>
Add an
Application
class for local data storage
We need to add a class for local storage. To add the class:
-
Select
src/main/java/com.google.devrel.samples.helloendpoints
in the Android Studio left pane and right-click. - Select New > Java Class .
- Supply the name Application for the class.
-
Replace the contents of
Application.java
with the following:package com.google.devrel.samples.helloendpoints; import com.appspot.<your_project_id>.helloworld.model.HelloGreeting; import com.google.api.client.util.Lists; import java.util.ArrayList; /** * Dummy Application class that can hold static data for use only in sample applications. * * TODO(developer): Implement a proper data storage technique for your application. */ public class Application extends android.app.Application { ArrayList<HelloGreeting> greetings = Lists.newArrayList(); }
Update the Android Manifest
To update the manifest:
Locate the file
src/main/res/AndroidManifest.xml
and replace the contents
of the file with the following:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.devrel.samples.helloendpoints"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:name="com.google.devrel.samples.helloendpoints.Application">
<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<activity
android:name="com.google.devrel.samples.helloendpoints.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
This change adds the
Internet
permission and adds the name attribute
android:name="com.google.devrel.samples.helloendpoints.Application"
to
the
<application>
tag.
Add an empty
AppConstants
class for later use
We'll need a constants and utility class to hold information needed by the app so it can connect to the backend API. In the next part of the tutorial, Connecting the App to the Backend , we'll fill this in, but for now, we'll leave this class empty.
To create a new constants/utility method class:
-
Select
src/main/java/com.google.devrel.samples.helloendpoints
and right-click. - Select New > Java Class .
- Supply the name AppConstants for the class.
At this point, we are finished with the basic layout and controls. In the next part of the tutorial, we'll add the click handlers for the buttons.
Replace code in
MainActivity
We'll need to replace the autogenerated code in
MainActivity.java
:
-
Open the file
src/main/java/com.google.devrel.samples.helloendpoints/MainActivity.java
. -
Delete the contents of
MainActivity.java
. -
Add the following code:
package com.google.devrel.samples.helloendpoints; import android.graphics.Color; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.TextView; import com.appspot.<your_project_id>.helloworld.Helloworld; import com.appspot.<your_project_id>.helloworld.Helloworld.Greetings.GetGreeting; import com.appspot.<your_project_id>.helloworld.Helloworld.Greetings.Multiply; import com.appspot.<your_project_id>.helloworld.model.HelloGreeting; import com.google.common.base.Strings; import java.io.IOException; import java.util.Arrays; import java.util.List; import java.util.Set; public class MainActivity extends ActionBarActivity { private static final String LOG_TAG = "MainActivity"; private GreetingsDataAdapter mListAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Prevent the keyboard from being visible upon startup. getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); ListView listView = (ListView) findViewById(R.id.greetings_list_view); mListAdapter = new GreetingsDataAdapter((Application) getApplication()); listView.setAdapter(mListAdapter); } /** * Simple use of an ArrayAdapter but we're using a static class to ensure no references to the * Activity exists. */ static class GreetingsDataAdapter extends ArrayAdapter { GreetingsDataAdapter(Application application) { super(application.getApplicationContext(), android.R.layout.simple_list_item_1, application.greetings); } void replaceData(HelloGreeting[] greetings) { clear(); for (HelloGreeting greeting : greetings) { add(greeting); } } @Override public View getView(int position, View convertView, ViewGroup parent) { TextView view = (TextView) super.getView(position, convertView, parent); HelloGreeting greeting = (HelloGreeting)this.getItem(position); StringBuilder sb = new StringBuilder(); Set<String> fields = greeting.keySet(); boolean firstLoop = true; for (String fieldName : fields) { // Append next line chars to 2.. loop runs. if (firstLoop) { firstLoop = false; } else { sb.append("\n"); } sb.append(fieldName) .append(": ") .append(greeting.get(fieldName)); } view.setText(sb.toString()); return view; } } }
-
Replace the value
<your_project_id>
in the imports listed above with the actual project ID for the backend API, using Android studio's code completion. Alternatively, you can copy the actual project ID listed in the project navigator line/libs/helloworld-v1-1.17.0-rc-SNAPSHOT.jar/com.appspot.the_actual_project_id.helloworld
.Notice the
LOG_TAG
defined up at the top. This tag allows us to filter error messages by theActivty
tag name while running in debug mode in Android Studio.Notice also that the code snippet includes an ArrayAdapter class to handle the display.
-
Click File > Save All and Build > Rebuild Project to make sure the project compiles.
-
Next, we'll add the code required for interactions with the backend API.
Next...
Continue to Connecting the App to the Backend .