Fast and Simple Android Development

Getting started with Android development.

This article acts as a tutorial for getting started with Android development. It demonstrates how to use basic Android components, pass data between one Activity to another, load images asynchronously through the use of third-party libraries, and more. To learn these components, we’ll build a simple applet to help keep our co-workers’ information handy. The app will show a list of employees and their contact details, and will allow users to add a contact to their address book. The full source code for this project can be found on AppCentrica’s GitHub page.

Create a New Android Project

The first thing we need to do is create a new project in Android Studio. Give your app a name and domain. Android Studio has many built-in templates to make getting started easy. For this app, we will choose a “Master/Detail Flow” template.

fs-android-dev-1

Adding Dependencies and AndroidManifest Configuration

Our company directory app requires two third-party libraries. Libraries are a great way to ensure you’re not reinventing the wheel each time. The two libraries we use are Picasso, for loading our contact images from the internet, and CircleImageView, for displaying a rounded contact image. Go ahead and add these dependencies in our build.gradle.

dependencies {
  …
  compile 'de.hdodenhof:circleimageview:2.1.0'
  compile 'com.squareup.picasso:picasso:2.5.2'
}

Since we load our contact images from the internet, we need internet permission in AndroidManifest.xml just above the tag.

uses-permission android:name="android.permission.INTERNET"

Along with this, we define our two activities here. The ContactListActivity is the main activity that the user sees when they launch the app, which contains the main list of contacts. The ContactDetailActivity is the activity that contains the details of the individual contact once a list item is clicked.

Setting Up the Contact Data

We will be hard-coding all our contact data in a JSON file, and reading it when the app launches. This is not the most effective solution to store employee data, but works just fine for 40 employees, which was our case a few months ago. For a more scalable solution, it would be better to use a database.

The data will contain the contact’s name, email, title, phone number, LinkedIn URL and a contact image URL. This file will be in our assets directory, under app\src\main\assets\contact_data.json. One contact object in the JSON file will look like this:

{
  "name":"John Doe",
  "email":"john.doe@example.com",
  "title":"Project Manager",
  "number":"123-456-7890",
  "linkedIn":"https://www.linkedin.com/in/john-doe",
  "pictureUrl":"https://www.example.com/john.jpg"
},

Reading the Contact Data

To read the information in the JSON file, we will create a method to read a file from the asset directory. This information will return a string with the file’s contents.

private static String loadJSONFromAsset(Activity activity) {
    String json;
    try {
        InputStream is = activity.getAssets().open(CONTACT_DATA_FILE_NAME);
        int size = is.available();
        byte[] buffer = new byte[size];
        is.read(buffer);
        is.close();
        json = new String(buffer, "UTF-8");
    } catch (IOException ex) {
        ex.printStackTrace();
        return null;
    }
    return json;
}

Once we have this information, we can go ahead and parse the JSON data into a Contact object, defined in the Contact.java file. To do this, we use the following method, found in the Contacts.java file. We use this method to generate a static array of Contact objects.

private static Contact parseSingleContact(JSONObject contactJSON) throws JSONException{
    Contact contact = new Contact();
    contact.setName(contactJSON.optString("name"));
    contact.setTitle(contactJSON.optString("title"));
    contact.setNumber(contactJSON.optString("number"));
    contact.setLinkedInUrl(contactJSON.optString("linkedIn"));
    contact.setPictureUrl(contactJSON.optString("pictureUrl"));
    contact.setEmail(contactJSON.optString("email"));
    return contact;
}

Creating the Layouts

Our company directory app has three layouts: the activity containing the main contact list, the layout for a list item in the contact list, and the activity containing the details of an individual contact. Let’s begin with the ContactListActivity. The main component that holds the list is called the RecyclerView, found in activity_contact_list.xml.

 android.support.v7.widget.RecyclerView
    android:id="@+id/contact_list"
    android:name="com.appcentrica.directory.ContactListFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutManager="LinearLayoutManager"
    tools:listitem="@layout/contact_list_content"

The RecyclerView is a flexible component of Android used for creating a layout with repeating items. The individual items are defined in its own layout file. In this case, it is defined in contact_list_content.xml. This view contains two views side by side, a custom view called CircleImageView for holding rounded contact images, and a TextView to show the contact name.

Our third layout file is activity_contact_detail.xml, which is responsible for the layout of our ContactDetailActivity. The two main sections here are the AppBarLayout, which contains the collapsible toolbar layout with the contact image and name, along with a few TextViews containing contact details.

Java Components: Contact List Activity

In our main activity, we simply show the list of contacts. In the activity, we need to first set the content view to activity_contact_list, load the contact data, and attach the contact data to the RecyclerView. RecyclerViews require an adapter to bind the data to the individual list view item. The adapter we use here is ContactListAdapter. The onBindViewHolder callback is called when the data is bound to the list item view. Here, we load the contact image using the URL provided in our JSON file, and set the onClickListener that takes the user to the details activity of the contact they chose. We need to pass the index of the chosen contact to our ContactDetailActivity, so we know which contact to show the details for. We do this through an Intent. An Intent can be used for launching an Activity, and we can store data in the intent to pass on to the second Activity.

…
// Set contact image in toolbar
Picasso.with(this)
        .load(mContact.getPictureUrl())
        .placeholder(R.drawable.default_contact)
        .error(R.drawable.default_contact)
        .into(appBarImage);
…

// Set onClickListener for list item
holder.mView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Context context = v.getContext();
        Intent intent = new Intent(context, ContactDetailActivity.class);
        intent.putExtra(ContactDetailActivity.ARG_ITEM_ID, position);
…
        context.startActivity(intent);
}
});

Java Components: Contact Detail Activity

Once the user clicks a contact list item, we start the ContactDetailActivity and get the index from the intent that was used to launch the activity. This is done by getting the intent extras.

int contactPosition = getIntent().getIntExtra(ARG_ITEM_ID, 0);
mContact = Contacts.LIST.get(contactPosition);

Now, we have a reference to the Contact object and can set the data from this object to the corresponding views from the activity_contact_detail layout. Just like the contact list item, we use the Picasso library to load the contact image, and set the email, phone number, title, and LinkedIn URL. We can also use intents here to dial a phone number when the user taps the phone number, for example.

number.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + mContact.getNumber()));
        startActivity(intent);
    }
});

The last thing we have left to do is to add a button that allows the user to add the current contact to their phone’s address book. We use an Android component called a FloatingActionButton for this task. Again, we can use an intent here to launch an “add contact” activity with some fields pre-filled. Special attention must be taken when inserting the contact image into the intent, because the image bitmap needs to be converted to a byte array first. The code for that looks like the following:

FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(ContactsContract.Intents.Insert.ACTION);
        intent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
        intent.putExtra(ContactsContract.Intents.Insert.EMAIL, mContact.getEmail());
        intent.putExtra(ContactsContract.Intents.Insert.EMAIL_TYPE, ContactsContract.CommonDataKinds.Email.TYPE_WORK);
        intent.putExtra(ContactsContract.Intents.Insert.PHONE, mContact.getNumber());
        intent.putExtra(ContactsContract.Intents.Insert.NAME, mContact.getName());
        intent.putExtra(ContactsContract.Intents.Insert.COMPANY, "AppCentrica");
        intent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, mContact.getTitle());

        // Contact picture is a bit different since we have to pass the bitmap as a byte array
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        Bitmap contactPicture = ((BitmapDrawable) appBarImage.getDrawable()).getBitmap();
        contactPicture.compress(Bitmap.CompressFormat.PNG, 100, stream);
        ArrayList<ContentValues> data = new ArrayList<>();
        ContentValues row = new ContentValues();
        row.put(ContactsContract.Contacts.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
        row.put(ContactsContract.CommonDataKinds.Photo.PHOTO, stream.toByteArray());
        data.add(row);
        intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, data);
        startActivity(intent);
    }
});

And that’s all there is to it! Hit the green run button to compile your code and install it on an Android device. The end result will look something like this:

fs-android-dev-2

Share