Thursday 1 May 2014

Modifying the Settings app

This article will outline how you can add things to the settings app. Before diving into the code, I'll go over some of the files you will be working with. 


Terminology

settings_header.xml

This file that's used to generate a list of settings the end user can access. If you're using a tablet, this is basically a list of the items 

strings.xml

Good old strings.xml. Basically a key/value array of public static final strings. We'll be keeping titles for settings in this file.

your_settings.xml

This file holds information about all the different preferences you want to store. Name it something memorable. If you're on a tablet, it's basically the list of individual preferences on the right hand side. In this particular example, it's called sip_settings.xml

YourSettings.java

You'll be creating a class that extends SettingsPreferenceFragment. The most simple implementation of this class is very bare-bones yet extensible. Here, we'll be implementing an OnPreferenceChangeListener to update the keys held in Settings.System. In this example, it's called SipPhoneSettings.java.


Now; Let's get coding!

I'm going to use my implementation of SIP Settings as an example.

Step 1. Navigate to the settings app in your android source folder. For me it's:

cd ~/android/system/packages/apps/Settings/

From here on out, any files I refer to are relative from this location.

Step 2. Open the settings_header.xml file.

vim ./res/xml/settings_headers.xml

Here, I'll add a bunch of <header>s.

After editing, my settings_header.xml looks like this:
<preference-headers
xmlns:android="http://schemas.android.com/apk/res/android">

    <!--4Com Settings Header -->
    <header
        android:id="@+id/four_com_setting"
        android:title="@string/four_com_settings_title" />

    <!-- SIP Settings -->
    <header
        android:id="@+id/sip_settings"
        android:fragment="com.android.settings.SipPhoneSettings"
        android:icon="@drawable/ic_settings_date_time"
        android:title="@string/sip_settings_title" />

    <!-- WIRELESS and NETWORKS -->
    <header android:id="@+id/wireless_section"
        android:title="@string/header_category_wireless_networks" />
...


You'll notice that I've added 2 headers. One is with icon and and fragment and one is without. This is so that we have this effect:
The header tag without fragment/icon created a simple label whereas the one with said properties created a clickable one.

Step 3. 

Create your settings file like so:
touch ./res/xml/sip_settings.xml

and edit it so that it looks something like this:
<PreferenceScreen  xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory 
        android:title="@string/sip_phone_setting">
        <EditTextPreference
            android:key="sip_extension_number"
            android:title="@string/extension_number" 
            android:digits="0123456789"/>
        <EditTextPreference
            android:key="sip_server_host"
            android:title="@string/server_host"
            android:digits=".0123456789"/>
    </PreferenceCategory>
    <PreferenceCategory 
        android:title="@string/sip_credentials_setting">
        <EditTextPreference
            android:key="sip_credentials_username"
            android:title="@string/sip_username"/>
        <EditTextPreference
            android:key="sip_credentials_password"
            android:title="@string/sip_password"/>
    </PreferenceCategory>
</PreferenceScreen>

Step 4. 

You may have noticed that we are referring to a lot of @strings in the XML files above. I've added the following entries to my strings.xml file:
    <string name="sip_phone_setting">SIP Phone Settings</string>
    <string name="extension_number">Extension number</string>
    <string name="sip_credentials_setting">SIP Credentials</string>
    <string name="server_host">Server Host</string>
    <string name="sip_username">Username</string>
    <string name="sip_password">Password</string>
    <string name="sip_contact_server">SIP Contact Server</string>

which can be found here:
cd ./res/values/strings.xml

Step 5.

Finally, we want to create and edit the SipPhoneSettings.java file.
touch ./src/com/android/settings/SipPhoneSettings.java
vim ./src/com/android/settings/SipPhoneSettings.java

Mine ended up looking like this:
public class SipPhoneSettings extends SettingsPreferenceFragment
                                implements Preference.OnPreferenceChangeListener {
    //Define the keys that will be stored in Settings.System
    public static final String EXCHANGE_KEY = "sip_extension_number";
    public static final String HOST_KEY = "sip_server_host";
    public static final String USERNAME_KEY = "sip_credentials_username";
    public static final String PASSWORD_KEY = "sip_credentials_password";

    //Have references to the different preferences that you want stored
    private EditTextPreference mExchangePreference;
    private EditTextPreference mHostPreference;
    private EditTextPreference mUsernamePreference;
    private EditTextPreference mPasswordPreference;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Load the preferences from an XML resource
        addPreferencesFromResource(R.xml.sip_settings);
        
        //Fetch the preferences
        mExchangePreference = (EditTextPreference) findPreference(EXCHANGE_KEY);
        mHostPreference = (EditTextPreference) findPreference(HOST_KEY);
        mUsernamePreference = (EditTextPreference) findPreference(USERNAME_KEY);
        mPasswordPreference = (EditTextPreference) findPreference(PASSWORD_KEY);

        //Set their onPreferenceChange listeners
        mExchangePreference.setOnPreferenceChangeListener(this);
        mHostPreference.setOnPreferenceChangeListener(this);
        mUsernamePreference.setOnPreferenceChangeListener(this);
        mPasswordPreference.setOnPreferenceChangeListener(this);
    }

    public boolean onPreferenceChange(Preference preference, Object newValue){
        final String key = preference.getKey(); // Get the key for the change
        String putVal = (String) newValue; //All changes will be string
                                           //in my page.

        //Add the new value to the corresponding key.
        if(EXCHANGE_KEY.equals(key)) {
            Settings.System.putString(getContentResolver(), EXCHANGE_KEY, putVal);
        } else if(HOST_KEY.equals(key)) {
            Settings.System.putString(getContentResolver(), HOST_KEY, putVal);
        } else if(USERNAME_KEY.equals(key)) {
            Settings.System.putString(getContentResolver(), USERNAME_KEY, putVal);
        } else if(PASSWORD_KEY.equals(key)) {
            Settings.System.putString(getContentResolver(), PASSWORD_KEY, putVal);
        }
        return true;
    }
}



Further Reading

1. Check out the subclasses for Preference for EditTextPreference alternatives. http://developer.android.com/reference/android/preference/Preference.html

2. You can do a lot of cool things with the settings_header your settings XML. Check this link for a detailed list: http://developer.android.com/reference/android/preference/PreferenceActivity.html


Creative Commons Licence
Modifying the Settings app by Can Orhan is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.

Saturday 7 September 2013

Format source code for web - A free, online tool to generate CSS for your source code!

The first question I asked myself when starting to write this blog was 'How will I format the source code?'. Luckily, I found a great website that does it quickly and easily; hilite.me!

It allows you to tweak the CSS it generates so you can personalise it however you like.
It also comes with a whole host of languages with pre-cooked style-sheets that you can edit!

So check out hilite.me if you need to beautify your source code!