Creating the application's settings activity

It is now the time to develop a simple application settings activity from which a user can update their profile status. Create a new package within com.example.messenger.ui named settings. Within this package, create a new settings activity. Name the activity SettingsActivity. To create a settings activity, right-click on the settings package, then select New | Activity | Settings Activity. Input the necessary details of the new settings activity, such as an activity name and activity title, then click Finish.

In the process of creating a new SettingsActivity, Android Studio will add a number of additional files to your project. In addition to this, a new .xml resource directory (app | res | xml) will be added to your project. This directory should contain the following files:

  • pref_data_sync.xml
  • pref_general.xml
  • pref_headers.xml
  • pref_notification.xml

You may choose to delete the pref_notification.xml and pref_data_sync.xml files. We will not make use of them in this project. Let's take a look at pref_general.xml:

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreference
android:defaultValue="true"
android:key="example_switch"
android:summary=
"@string/pref_description_social_recommendations"
android:title="@string/pref_title_social_recommendations" />

<!-- NOTE: EditTextPreference accepts EditText attributes. -->
<!-- NOTE: EditTextPreference's summary should be set to
its value by the activity code. -->
<EditTextPreference
android:capitalize="words"
android:defaultValue="@string/pref_default_display_name"
android:inputType="textCapWords"
android:key="example_text"
android:maxLines="1"
android:selectAllOnFocus="true"
android:singleLine="true"
android:title="@string/pref_title_display_name" />

<!-- NOTE: Hide buttons to simplify the UI.
Users can touch outside the dialog to
dismiss it. -->
<!-- NOTE: ListPreference's summary should be set to
its value by the activity code. -->
<ListPreference
android:defaultValue="-1"
android:entries="@array/pref_example_list_titles"
android:entryValues="@array/pref_example_list_values"
android:key="example_list"
android:negativeButtonText="@null"
android:positiveButtonText="@null"
android:title="@string/pref_title_add_friends_to_messages" />

</PreferenceScreen>

The root view of this xml layout file is a PreferenceScreen. A PreferenceScreen is the root of a Preference hierarchy. A PreferenceScreen in itself is a top-level Preference. The word Preference has been used a few times now. Let's define what a Preference is. It is a representation of the basic Preference user-interface building block that is displayed in the form of a list by a PreferenceActivity.

The Preference class provides an appropriate view for a preference to be displayed within a PreferenceActivity and its associated SharedPreferences for the storage and retrieval of preference data. SwitchPreference, EditTextPreference, and ListPreference in the preceding code snippet are all subclasses of DialogPreference, which in turn is a subclass of the Preference class. A PreferenceActivity is the base class needed by an activity in order to display a hierarchy of preferences to a user.

The SwitchPreference, EditTextPreferenceand ListPreference views in pref_general.xml are not needed. Remove them from the XML file now. We need a preference that enables the user to update their status on our Messenger platform. This is a highly specific use case and thus it comes as no surprise that there's no preference widget that provides us with this ability. No worries! We will implement a custom preference that does the job. Let's call it ProfileStatusPreference. Create a new ProfileStatusPreference class containing the following code in the settings package:

package com.example.messenger.ui.settings

import android.content.Context
import android.preference.EditTextPreference
import android.text.TextUtils
import android.util.AttributeSet
import android.widget.Toast
import com.example.messenger.data.local.AppPreferences
import com.example.messenger.data.remote.request.StatusUpdateRequestObject
import com.example.messenger.service.MessengerApiService
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers

class ProfileStatusPreference(context: Context, attributeSet: AttributeSet) : EditTextPreference(context, attributeSet) {

private val service: MessengerApiService = MessengerApiService
.getInstance()
private val preferences: AppPreferences = AppPreferences
.create(context)

override fun onDialogClosed(positiveResult: Boolean) {
if (positiveResult) {

The following snippet binds ProfileStatusPreference's EditText to etStatus variable:

      val etStatus = editText

if (TextUtils.isEmpty(etStatus.text)) {
// Display error message when user tries
// to submit an empty status.
Toast.makeText(context, "Status cannot be empty.",
Toast.LENGTH_LONG).show()

} else {
val requestObject =
StatusUpdateRequestObject(etStatus.text.toString())

Let's use MessengerApiService to update the user's status:


service.updateUserStatus(requestObject,
preferences.accessToken as String)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ res ->

Now, we will store the updated user details if status update is successful:


preferences.storeUserDetails(res) },
{ error ->
Toast.makeText(context, "Unable to update status at the " +
"moment. Try again later.", Toast.LENGTH_LONG).show()
error.printStackTrace()})
}
}

super.onDialogClosed(positiveResult)
}
}

The ProfileStatusPreference class extends EditTextPreference. EditTextPreference is a Preference that permits string input in an EditText. EditTextPreference is a DialogPreferenceand, as such, presents a dialog to the user containing the Preference view when the Preference is clicked. When a dialog of the DialogPreference is closed, its onDialogClosed(Boolean) method is invoked. A positive Boolean value argument, true, is passed to onDialogClosed() when the dialog is dismissed with a positive result. false is passed to onDialogClosed() when the dialog is dismissed with a negative result, for example, when the dialog's cancel button is clicked.

The ProfileStatusPreference overrides the onDialogClosed() function of EditTextPreference. If the dialog is closed with a positive result, the validity of the status contained within the EditText function of ProfileStatusPreference is checked. If the status message is valid, the status is updated with the API, otherwise an error message is shown.

Having created ProfileStatusPreference, go back to pref_general.xml and update it to reflect the XML in the following snippet:

<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<com.example.messenger.ui.settings.ProfileStatusPreference
android:key="profile_status"
android:singleLine="true"
android:inputType="text"
android:maxLines="1"
android:selectAllOnFocus="true"
android:title="Profile status"
android:defaultValue="Available"
android:summary="Set profile status (visible to contacts)."/>
</PreferenceScreen>

As can be seen in the preceding code, we made use of ProfileStatusPreference in the code snippet as we would any other preference bundled within the Android application framework.

Moving on to other aspects, let's check out pref_headers.xml:

<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<!-- These settings headers are only used on tablets. -->
<header
android:fragment=
"com.example.messenger.ui.settings.SettingsActivity
$GeneralPreferenceFragment"
android:icon="@drawable/ic_info_black_24dp"
android:title="@string/pref_header_general" />
<header
android:fragment=
"com.example.messenger.ui.settings.SettingsActivity
$NotificationPreferenceFragment"
android:icon="@drawable/ic_notifications_black_24dp"
android:title="@string/pref_header_notifications" />
<header
android:fragment=
"com.example.messenger.ui.settings.SettingsActivity
$DataSyncPreferenceFragment"
android:icon="@drawable/ic_sync_black_24dp"
android:title="@string/pref_header_data_sync" />
</preference-headers>

The preference header file defines headers for various preferences in the SettingsActivity. Modify the file to contain the following code:

<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
<header
android:fragment=
"com.example.messenger.ui.settings.SettingsActivity
$GeneralPreferenceFragment"
android:icon="@drawable/ic_info_black_24dp"
android:title="@string/pref_header_account" />
</preference-headers>

Perfectly done! Now we have to work on the SettingsActivity. Modify the body of SettingsActivity to contain the content shown in the following code block:

package com.example.messenger.ui.settings

import android.content.Intent
import android.os.Bundle
import android.preference.PreferenceActivity
import android.preference.PreferenceFragment
import android.view.MenuItem
import android.support.v4.app.NavUtils
import com.example.messenger.R

PreferenceActivity presenting a set of application settings:


class SettingsActivity : AppCompatPreferenceActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}

override fun onMenuItemSelected(featureId: Int, item: MenuItem):
Boolean {
val id = item.itemId

if (id == android.R.id.home) {
if (!super.onMenuItemSelected(featureId, item)) {
NavUtils.navigateUpFromSameTask(this)
}
return true
}
return super.onMenuItemSelected(featureId, item)
}

The following function onBuildHeaders() is called when the activity needs a list of headers build:


override fun onBuildHeaders(target: List<PreferenceActivity.Header>)
{
loadHeadersFromResource(R.xml.pref_headers, target)
}

The below method preventing fragment injection from malicious applications and all unknown fragments should be denied here:


override fun isValidFragment(fragmentName: String): Boolean {
return PreferenceFragment::class.java.name == fragmentName
|| GeneralPreferenceFragment::class.java.name == fragmentName
}

The below fragment shows general preferences:


class GeneralPreferenceFragment : PreferenceFragment() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

addPreferencesFromResource(R.xml.pref_general)
setHasOptionsMenu(true)
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
val id = item.itemId

if (id == android.R.id.home) {
startActivity(Intent(activity, SettingsActivity::class.java))
return true
}
return super.onOptionsItemSelected(item)
}
}
}

The SettingsActivity extends AppCompatPreferenceActivity—an activity that implements the required calls to be used with AppCompat. SettingsActivity is a PreferenceActivity that represents a set of application settings. The onBuildHeaders() function of SettingsActivity is called when the activity needs a list of headers built. isValidFragment() prevents malicious applications from injecting fragments into the SettingsActivity. The isValidFragment() returns true when a fragment is valid and false otherwise.

Within SettingsActivity, we defined a GeneralPreferenceFragment class. GeneralPreferenceFragment extends PreferenceFragment. The PreferenceFragment fragment is an abstract class defined in the Android application framework. A PreferenceFragment shows a hierarchy of Preference instances as lists.

Preferences from pref_general.xml are added to the GeneralPreferenceFragment in the onCreate() method by the invocation of addPreferencesFromResource(R.xml.pref_general).

With these changes made to the SettingsActivity, I am pleased to inform you that you have successfully finished work on the settings of the Messenger application.

Having completed the SettingsActivity, we are now ready to run the Messenger app. Go ahead and build and run the Messenger application on a device (virtual or physical). Once the app launches, you will be directed straight to the LoginActivity.

The first thing we must do is register a new user on the Messenger platform. We can do this on the SignUpActivity. Go ahead and click on the DON'T HAVE AN ACCOUNT? SIGN UP! button. You will be directed to the SignUpActivity:

Create a new user in this activity. Enter popeye as the username, as well as a phone number and a password, then click the SIGN UP button. A new user will be registered on the Messenger platform with the username popeye. Once the registration is completed, you will be directed to the MainActivity and the conversations view will be rendered immediately:

As the newly registered user does not have any active conversations, a toast message informing them of this will be displayed. We need to create another user on the messenger platform to demonstrate the chat functionality. Log out of popeye's account by clicking the three dots at the top-right corner of the screen and selecting logout:

Once logged out, create a new Messenger account with the username dexter. After logging in as dexter, click on the new message creation floating action button at the bottom-right of the conversations view. The contacts view will be rendered to you:

Clicking on the popeye contact will open the ChatActivity. Let's send a message to popeye. Type Hey Popeye! into the message input field at the bottom of the screen and click Send. The message will immediately be sent to popeye:

Upon going back to the conversation view of the MainActivity, you will notice a conversation item now exists for the conversation initiated with popeye:

Let's check whether the message has actually been delivered to popeye. Log out of the Messenger platform and then log in as popeye. Upon login, you will be greeted by the new conversation initiated by dexter:

Fantastic! It's been delivered. Now let's reply to dexter. Open the conversation and send dexter a message:

We sent a simple How are you Dexter? in the preceding screenshot. It is time to update popeye's profile status. Navigate back to the main activity and access the settings activity (click the three dots on the action bar and select Settings). Tapping Account in the launched settings activity will display the general preference fragment. Click on the Profile status preference:

A dialog containing an EditText in which you can input a new profile status will pop up. Input a status message of your choosing and click OK:

The status of the current profile will be updated immediately.

At this juncture, I am pleased to inform you that you have successfully implemented the Messenger application in its entirety. Feel free to make modifications and additions to the code we created in this chapter—you will learn a lot more if you do. Before we conclude this chapter, there are two topics we need to cover briefly. The first is application testing, and the second is performing background tasks.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.88.110