Sunday 27 April 2014

ListPreference: how to load data dinamically

In this tutorial I'll explain how to populate a ListPreference programmatically. It is easier than you might expect; however I haven't found any official tutorial about it. If you need more information about Settings in Android you can read the official documentation.


Generally speaking, if you want to add a ListPreference to your app's settings, you have to add a ListPreference object in the preference XML file, like in the following example:
<ListPreference
        android:dependency="pref_sync"
        android:key="pref_syncConnectionType"
        android:title="@string/pref_syncConnectionType"
        android:dialogTitle="@string/pref_syncConnectionType"
        android:entries="@array/pref_syncConnectionTypes_entries"
        android:entryValues="@array/pref_syncConnectionTypes_values"
        android:defaultValue="@string/pref_syncConnectionTypes_default" />

As you can see you provide the entries of the ListPreference, and the corresponding values, with the items android:entries and android:entryValues, that refer to an array loaded in the res/values folder.
But if you want to load the data programmatically (for example, if you want to fetch the data from a local database or from an online service), you must create a custom ListPreference class.

To begin with, let's see the new preference XML file with a reference to our custom ListPreference:
<com.androidthetechnicalblog.preference.MyCustomPreference
            android:key="pref_mycustompreference"
            android:title="@string/pref_mycustompreference"
            android:summary="@string/pref_mycustompreference_summary"/>

We are creating a custom ListPreference class, so we must provide the full path of the class. As you can also see, we omitted android:entries and android:entryValues, because we want to load the data programmatically.

Now let's see how MyCustomPreference class looks like (not relevant code omitted for brevity):
public class MyCustomPreference extends ListPreference {  
    // ...  

    public MyCustomPreference (Context context, AttributeSet attrs) {      
        super(context, attrs);      
    
        setEntries(entries());         
        setEntryValues(entryValues());         
        setValueIndex(initializeIndex());       
    }  

    public MyCustomPreference (Context context) {      
        this(context, null);  
    }  

    private CharSequence[] entries() {      
        //action to provide entry data in char sequence array for list          
        String myEntries[] = {"one", "two", "three", "four", "five"};         

        return myEntries;  
    }  

    private CharSequence[] entryValues() {      
        //action to provide value data for list           
     
        String myEntryValues[] = {"ten", "twenty", "thirty", "forty", "fifty"};
        return myEntryValues;
   }

   private int initializeIndex() {
        //here you can provide the value to set (typically retrieved from the SharedPreferences)
        //...

        int i = 2;
        return i;
    }
}

The code is quite simple. You just have to provide the entries and entryValues through the methods setEntries and setEntryValues, that accept a CharSequence (or String) array as a parameter. You can also set the default initial value, typically retrieved from the SharedPreferences, through the method setValueIndex

For everything that is not explicitly covered in this tutorial you can refer to the official documentation aboud Android settings.

Wednesday 23 April 2014

Animating a ProgressBar to a specific value

In this tutorial I'll explain how to set a specific progress value to a ProgressBar using a smooth animation.



ObjectAnimator animation = ObjectAnimator.ofInt(pbBudget, "progress", 0, 50);
To begin with we create an ObjectAnimator object: this is a subclass of ValueAnimator that provides support for animating properties on target objects. The constructors of this class take parameters to define the target object that will be animated as well as the name of the property that will be animated. In this example we used the following parameters:
  • pbBudget: reference to the ProgressBar in the layout;
  • "progress": the name of the property to be animated;
  • 0: starting point of the animation;
  • 50: ending point of the animation.

animation.setDuration(1500);
The code is self-explanatory: the animation lasts 1,5 seconds.


animation.setInterpolator(new DecelerateInterpolator());
It is possible to use different interpolators for our animation, like for example:

animation.start();

You can also create an animation for the ProgressBar from scratch, using the method setProgress(int progress) with a delay (android.os.SystemClock.sleep(long ms)).
If you decide to do so, I suggest to use an AsyncTask and update the ProgressBar using the onProgressUpdate method.
Avoid animating the ProgressBar in the UI thread with a loop:
  1. because this will freeze the UI until the animation is completed;
  2. because even though you are in the UI thread, you don't release the UI thread until the animation is completed, thus preventing the system to update the UI (and the ProgressBar) itself.

Wednesday 16 April 2014

Hidden Android APIs: hiding SMS messages to the default SMS receiver

In a previous tutorial we saw how to take advantage of the hidden Android APIs to listen to incoming SMS messages.
Now suppose that we want to listen to specific SMS messages (messages coming from a particular number or containing specific strings) and to hide them to the default SMS receiver (like Google Hangout or other client).

This is a private message!

As in the previous tutorial we have to declare a BroadcastReceiver in the AndroidManifest.xml file. However, in order to take priority over the default SMS receiver, we also have to set a high priority for our SMS BroadcastReceiver:
<receiver android:name="com.androidthetechinalblog.SMSReceiver">
   <intent-filter android:priority="9999">
      <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
   </intent-filter>
</receiver>

Now we just have to make some minor changes to the SMSReceiver class:
public class SMSReceiver extends BroadcastReceiver {
 
 public void onReceive(Context context, Intent intent) {
  String action = intent.getAction();
 
  if(action.equals(“android.provider.Telephony.SMS_RECEIVED”)) {
   Object incomingSMSs[] = (Object[]) intent.getSerializableExtra(“pdus”);
    
   for(Object tmp : incomingSMSs) {
    byte message[] = (byte[]) tmp;
    SmsMessage smsMessage = SmsMessage.createFromPdu(message);

    String phoneNumber = smsMessage.getOriginatingAddress();
    String messageBody = smsMessage.getMessageBody();

    if(phoneNumber.equals("3457148596") || messageBody.contains("test")) {
       //we have found our SMS! here you can perform some actions
       abortBroadcast();
       setResultData(null);
    }
   }
  }
 }
}

As you can see, by calling abortBroadcast() and setResultData(null) (methods working only for "ordered broadcasts", like in our example) we make sure that our SMS won't be propagated to any other receiver.

Wednesday 2 April 2014

Quick trick of the week: EditText inside a ListView



Let's suppose you want to create a layout with a ListView containing an EditText in each item, like in the following picture:


If yout set up your layout like the one showed above you'll soon have to deal with an annoying problem: for some unknown reason the EditText immediately loses focus, and it's almost impossible to write anything.
For some devices a good workaround would be to double tap on the EditText, but this is not an accetable solution for the end users of our apps.

Fortunately the solution is quite easy: you just have to add the following lines of code:
  1. in the AndroidManifest.xml file add the following line for the Activity containing the ListView: android:windowSoftInputMode="adjustPan";
  2. in the layout file of the Activity add the following line to your ListView: android:descendantFocusability="beforeDescendants".


Now the EditText(s) should behave as expected...

 <activity android:name="com.androidthetechnicalblog.MyActivity"
   android:configChanges="keyboardHidden|orientation|screenSize"     
   android:windowSoftInputMode="stateHidden|adjustPan"
   android:label="@string/app_name"></activity>         

<ListView
   android:id="@android:id/list"             
   android:descendantFocusability="beforeDescendants"       
   android:layout_width="match_parent"
   android:layout_height="wrap_content"            
   android:layout_marginTop="10dp" />