2010-01-29

Provide PhonePreference from RingtonePreference

As you know, Android's preference package provides RingtonePreference for you to bring up ringtone selection activity and you can get selected ringtone URI back without much coding.

But, what if I want to have a preference that can pick up people from contacts? Unfortunately, Android's preference package doesn't have one. But, fortunately, you can write your own based on RingtonePreference or VolumePreference. I will use RingtonePreference as an example.

Look into RingtonePreference's source code, you see code below.

protected void onClick() {
    // Launch the ringtone picker
    Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
    onPrepareRingtonePickerIntent(intent);
    getPreferenceManager().getActivity().startActivityForResult(intent, mRequestCode);
}

@Override
protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
    super.onAttachedToHierarchy(preferenceManager);
        
    preferenceManager.registerOnActivityResultListener(this);
    mRequestCode = preferenceManager.getNextRequestCode();
}

public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == mRequestCode) {
        if (data != null) {
            Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
                
            if (callChangeListener(uri != null ? uri.toString() : "")) {
                onSaveRingtone(uri);
            }
        }
        return true;
    }
    return false;
}

Things we can't touch are important to us, in this case,
  1. mRequestCode
  2. preferenceManger.registerOnActivityResultListener(this);
The mRequestCode is the ID to identify the result is sent for our request. In other words, it is important for us. But, it is maintained and kept privately. The registerOnActivityResultListener() has package access right and it adds a callback inside PreferenceManager to get result from subactivity started by startActivityForResult.

Here is my PhonePreference that extends RingtonePreference,
package org.startsmall.openalarm;

import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.preference.RingtonePreference;
import android.preference.PreferenceManager;
import android.provider.Contacts;
import android.text.TextUtils;
import android.util.Log;

public class PhonePreference extends RingtonePreference {
    public interface OnPhonePickedListener {
        void onPhonePicked(Uri uri);
    }

    private OnPhonePickedListener mPhonePickedListener;

    public PhonePreference(Context context) {
        super(context);
    }

    public void setOnPhonePickedListener(OnPhonePickedListener listener) {
        if (listener != null) {
            mPhonePickedListener = listener;
        }
    }
    
    @Override 
    protected void onPrepareRingtonePickerIntent(Intent intent) {
        // Override this intent to start ContactsListActivity
        intent.setAction(Intent.ACTION_GET_CONTENT);
        intent.setType(Contacts.Phones.CONTENT_ITEM_TYPE);
        intent.setFlags(Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP);
    }
   
    @Override                                                                       
    public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
        if (super.onActivityResult(requestCode, resultCode, data)) {
            if (data != null) {
                Uri uri = data.getData();
                if (callChangeListener(uri != null ? uri.toString() : "")) {
                    onSaveRingtone(uri);
                }
            }
            return true;
        }
    }

    protected void onSaveRingtone(Uri uri) {
        super.onSaveRingtone(uri);
        if (mPhonePickedListener != null) {
            mPhonePickedListener.onPhonePicked(uri);
        }
    }
}

To use this PhonePreference, just new an instance of it and add it into your preference hierarchy. If you want to use it in resource file, please add more constructors with attributes just like other system's Preference.