Android SearchView tutorial: EditText with phone contacts search and autosuggestion (including source code)

15 06 2013

SearchView is available in Android 3.0+. It enables you to search for any data by means of ContentProvider. You can write your custom ContentProvider or use the existing one. For simplicity I will show how to use Android ContactsProvider.

The Goal

The goal is to buil SearchView to find contact from address book with auto suggestions:

SearchView tutorial result

SearchView tutorial result

1. Add SearchView to activity

SearchView is aViewGroup and you can add it as any other view to your activity:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".SearchViewActivity" >

    <SearchView
        android:id="@+id/searchView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true" >
    </SearchView>

    <TextView
        android:id="@+id/searchViewResult"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/searchView"
        android:layout_centerHorizontal="true"
        android:text="@string/hello_world" />

</RelativeLayout>

2. Create searchable.xml file

In this file you will specify the data source to search through as well as customize the SearchView. In my case I use default Android ContactsProvider. It is identified by URI:

com.android.contacts

Put this file in /res/xml/searchable.xml. This is how my searchable.xml looks like:

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/findContact"
    android:hint="@string/findContact"
    android:includeInGlobalSearch="true"
    android:queryAfterZeroResults="true"
    android:searchMode="queryRewriteFromText"
    android:searchSettingsDescription="@string/findContact"
    android:searchSuggestAuthority="com.android.contacts"
    android:searchSuggestIntentAction="android.provider.Contacts.SEARCH_SUGGESTION_CLICKED"
    android:searchSuggestIntentData="content://com.android.contacts/contacts/lookup" >

    <!-- allow green action key for search-bar and per-suggestion clicks -->
    <actionkey
        android:keycode="KEYCODE_CALL"
        android:queryActionMsg="call"
        android:suggestActionMsg="call" />

</searchable>

Watch out: you have to specify label and hint as a strings from strings.xml. Otherwise (if they are hardcoded or absent, the SearchView did not worked for me).

3. Add permission to read contacts

Add permission in AndroidManifest.xml file in order to gain access to user’s contact book:

<uses-permission android:name="android.permission.READ_CONTACTS" />

4. Use searchable.xml in your activity

To use searchable.xml in your activity, you need to add <meta-data/> tag to your AndroidManifest.xml:

<meta-data
    android:name="android.app.searchable"
    android:resource="@xml/searchable" />

To see my full AndroidManifest.xml, see the next points.

5. Setup the SearchView in Activity code

Here you have to provide the SearchManager to the SearchView and point tho this activity as Search Result Intent Handler. Create dedicated method for it and run it in onCreate():

private void setupSearchView() {
	SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
	final SearchView searchView = (SearchView) findViewById(R.id.searchView);
	SearchableInfo searchableInfo = searchManager.getSearchableInfo(getComponentName());
	searchView.setSearchableInfo(searchableInfo);
}

6. Filter SEARCH intent in your activity

SearchView generates the SEARCH intent and handling it in your activity is crucial. This will inform you about refreshing suggestion list, the event of chosing an item from that list or confirmation button press. So add this to intent filter in your activity:

<intent-filter>
    <action android:name="android.intent.action.SEARCH" />
</intent-filter>

As for now your application should work and suggest contacts from contact book. However, choosing the contact from suggestion will not work yet.

7. Add singleTop flag to activity

Now when you choose the contact from suggestion list, the activity is relaunched. To prevent it, set the launchMode to singleTop for your activity in AndroidManifest.xml:

android:launchMode="singleTop"

This is how the full AndroidManifest.xml should look like after all:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="pl.looksok.searchviewdemo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="11" />

    <uses-permission android:name="android.permission.READ_CONTACTS" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="pl.looksok.searchviewdemo.SearchViewActivity"
            android:label="@string/app_name"
            android:launchMode="singleTop">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
                <action android:name="android.intent.action.SEARCH" />
            </intent-filter>

            <meta-data
                android:name="android.app.searchable"
                android:resource="@xml/searchable" />
        </activity>
    </application>
</manifest>

8. Handle suggestion pick user action

To handle contact search event you have to handle intent with particular action: search suggestion picked, or return button clicked (search request):

@Override
protected void onNewIntent(Intent intent) {
	if (ContactsContract.Intents.SEARCH_SUGGESTION_CLICKED.equals(intent.getAction())) {
		//handles suggestion clicked query
		String displayName = getDisplayNameForContact(intent);
		resultText.setText(displayName);
	} else if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
		// handles a search query
		String query = intent.getStringExtra(SearchManager.QUERY);
		resultText.setText("should search for query: '" + query + "'...");
	}
}

this is how I get contact display name:

private String getDisplayNameForContact(Intent intent) {
	Cursor phoneCursor = getContentResolver().query(intent.getData(), null, null, null, null);
	phoneCursor.moveToFirst();
	int idDisplayName = phoneCursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
	String name = phoneCursor.getString(idDisplayName);
	phoneCursor.close();
	return name;
}

That’s all!

9. Download my source code if you wish!

Here it is!

Did I help you?
I manage this blog and share my knowledge for free sacrificing my time. If you appreciate it and find this information helpful, please consider making a donation in order to keep this page alive and improve quality

Donate Button with Credit Cards

Thank You!





Source code: Android custom ListView

27 04 2013

Since many of you have asked for a source code of this tutorial:

Android custom ListView tutorial

I decided to write it and share. The tutorial was witten based on a bigger project, so I could not share it. This is why I have written custom ListView source code example from scratch based on the article mentioned above. Here it is:

Source Code: Android custom ListView

 

Did I help you?
I manage this blog and share my knowledge for free sacrificing my time. If you appreciate it and find this information helpful, please consider making a donation in order to keep this page alive and improve quality

Donate Button with Credit Cards

Thank You!





Android: change desktop wallpaper programatically

16 03 2013

Here is an instruction how to change desktop background on android device rogramatically by means of WallpaperManager. For simplicity the new wallpaper is taken from project resources.

private void setDesktopWallpaper() {
	try {
		WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
		wallpaperManager.setResource(R.drawable.my_wallpaper);
	} catch (IOException e) {
		SLog.e(TAG, "Error changing wallpaper: " + e.getMessage());
	}
}

To use another image (for example downloaded from the internet, you can use setBitmap method instead of setResource.

Did I help you?
I manage this blog and share my knowledge for free sacrificing my time. If you appreciate it and find this information helpful, please consider making a donation in order to keep this page alive and improve quality

Donate Button with Credit Cards

Thank You!





Android: play default notification sound

9 03 2013

When aplication wants to notify user with sound, the best way to do it is to play notification sound that is set by the user on his device.

To do it, first you need to get Uri to that audio file from RingtoneManager. After that create a Ringtone object from that Uri and play it:

private void playDefaultNotificationSound() {
	Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
	Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
	r.play();
}

Did I help you?
I manage this blog and share my knowledge for free sacrificing my time. If you appreciate it and find this information helpful, please consider making a donation in order to keep this page alive and improve quality

Donate Button with Credit Cards

Thank You!





Android: complete widget tutorial (including source code)

15 12 2012

Tutorial will show how to build android widget with one button and image. Button press will change the image displayed. The result will look as follows

What is going to be built

What is going to be built

Source code

Hereby I share my widget tutorial source code. Feel free to download.

1. Create widget layout

layout is defined as any other layout in Android app. My layout consists of just one button and one imageView (/res/layout/widget_demo.xml).

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:layout_margin="5sp"
    android:background="@android:drawable/alert_dark_frame" >

    <ImageView
        android:id="@+id/widget_image"
        android:layout_width="110sp"
        android:layout_height="110sp"
        android:layout_gravity="center"
        android:src="@drawable/wordpress_icon" />

    <Button
        android:id="@+id/widget_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="Change it!" />

</LinearLayout>

Please remember that views that can be used in widget layout are limited. It depends on android version you are developing for. Find out more here. Make sure that view that you are using is supported by target android SDK version.

2. Create App Widget Provider declaration

This is another xml file that tells android OS that this application has a widget (display your widget in OS widget list). Create it with wizard:

File → New → Android → Android XML File
Widget Provider creation wizard

Widget Provider creation wizard

Widget size should be defined in dp. Usually widget size is related to one icon size on desktop (74 x 74 dp). Size should be mutiplication of such blocks. It may be calculated using formula:

((Number of columns or rows)* 74)

Please remember however to leave some space for margins. My widget has width and height equal to two icon blocks (146 dp x 146 dp):

<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" 
    android:initialLayout="@layout/widget_demo" 
    android:minHeight="146dp" 
    android:minWidth="146dp"
    android:updatePeriodMillis="1000000" 
    >
</appwidget-provider>

3. Create AppWidgetProvider onUpdate implementation

Create class extending AppWidgetProvider. It will be responsible for updating your widget while Android OS requests it (e.g. widget is shown to user).

Here you have to set button listener (as a PendingIntent, because RemoteViews supports only that way of communication)

public class MyWidgetProvider extends AppWidgetProvider {

	@Override
	public void onUpdate(Context context, AppWidgetManager appWidgetManager,
			int[] appWidgetIds) {

		RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_demo);
		remoteViews.setOnClickPendingIntent(R.id.widget_button, buildButtonPendingIntent(context));

		pushWidgetUpdate(context, remoteViews);
	}

	public static PendingIntent buildButtonPendingIntent(Context context) {
		Intent intent = new Intent();
	    intent.setAction("pl.looksok.intent.action.CHANGE_PICTURE");
	    return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
	}

	public static void pushWidgetUpdate(Context context, RemoteViews remoteViews) {
		ComponentName myWidget = new ComponentName(context, MyWidgetProvider.class);
	    AppWidgetManager manager = AppWidgetManager.getInstance(context);
	    manager.updateAppWidget(myWidget, remoteViews);		
	}
}

In buildButtonPendingIntent I set that after widget button press the intent will be sent. So now… it is time to catch that intent in Broadcast Receiver and handle widget update

4. Register MyWidgetProvider in AndroidManifest.xml

Add these lines in <application/> tag:

        <receiver android:name="MyWidgetProvider" >
            <intent-filter >
                <action 
                    android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/demo_widget_provider" />
        </receiver>

5. Create Broadcast Intent receiver

This will be called for pending intents updates. Widgets are built on RemoteViews (views that are managed by another process than your application). This is why they communicate with your app by PendingIntents.

public class MyWidgetIntentReceiver extends BroadcastReceiver {

	private static int clickCount = 0;

	@Override
	public void onReceive(Context context, Intent intent) {
		if(intent.getAction().equals("pl.looksok.intent.action.CHANGE_PICTURE")){
			updateWidgetPictureAndButtonListener(context);
		}
	}

	private void updateWidgetPictureAndButtonListener(Context context) {
		RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_demo);
		remoteViews.setImageViewResource(R.id.widget_image, getImageToSet());

		//REMEMBER TO ALWAYS REFRESH YOUR BUTTON CLICK LISTENERS!!!
		remoteViews.setOnClickPendingIntent(R.id.widget_button, MyWidgetProvider.buildButtonPendingIntent(context));

		MyWidgetProvider.pushWidgetUpdate(context.getApplicationContext(), remoteViews);
	}

	private int getImageToSet() {
		clickCount++;
		return clickCount % 2 == 0 ? R.drawable.me : R.drawable.wordpress_icon;
	}
}

WARNING!!!

Since you are creating RemoteViews from scratch in Broadcast Receiver (using constructor with new keyword), you must refresh widget layout from scratch. Mainly: refresh the button listeners pending intents. This will help you to avoid issues like this and that.

6. Register Broadcast Receiver in AndroidManifest.xml

Add the following lines in <application/> tag:

<receiver
    android:name="MyWidgetIntentReceiver"
    android:label="widgetBroadcastReceiver" >
    <intent-filter>
        <action android:name="pl.looksok.intent.action.CHANGE_PICTURE" />
    </intent-filter>

    <meta-data
        android:name="android.appwidget.provider"
        android:resource="@xml/demo_widget_provider" />
</receiver>

7. That’s all!

Download the source code and enjoy! Feel free to customize widget according to your needs!

Here you will find some another widget tutorial. Maybe it will also be useful for you.

Did I help you?
I manage this blog and share my knowledge for free sacrificing my time. If you appreciate it and find this information helpful, please consider making a donation in order to keep this page alive and improve quality

Donate Button with Credit Cards

Thank You!





Android custom button layout tutorial

1 12 2012

The goal of this tutorial is to create customized button with your own background. The result will be something like that:

Normal button state

Button in normal state

Button pressed state

Button in pressed state

There are two ways of creating such background. The common one is to define it in XML file – this way I will describe here. The other, more elastic way is to define it at runtime in Java Code. And this is described in another tutorial.

1. Create background graphics files
First you need to create backround image for each button state (normal, pressed, focused etc.). For simplicity it is enough just to provide graphics for pressed and normal state. My files looks like that:

button_left_normal.png

button_left_normal.png

button_left_pressed.png

button_left_pressed.png

2. Create scalable 9patches from background files

9 patch is common format that allows button background to scale to fit desired size (width and height). You have to define which fragments should be resized while scaling (middle part of image) and which not (image corners), in order not to change background image shape.

Android SDK provides 9patch tool to create these. It is in your Andrid directory in \Android\android-sdk\tools\draw9patch.exe. Run it and draw line on the left and top edge, then save it to drawables directory (it will have extension like <file_name>.9.png. My files are as follows:

button_left_normal.9.png

button_left_normal.9.png

button_left_pressed.9.png

button_left_pressed.9.png

Some draw9patch.exe tutrials and instructions are available on the internet. I hope these three will be helpful: Android developers tutorial or YouTube tutorial.

3. Define xml selector
Selector is responsible for reacting on button press changes and showing proper backgrounds to the user. Here you define background files for each state (use 9patch images! These with *.9.png extension!), and put it in your /res/drawable folder. Example:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:state_pressed="true" android:drawable="@drawable/button_left_pressed"/>
    <item android:state_focused="true" android:drawable="@drawable/button_left_pressed"/>
    <item android:drawable="@drawable/button_left_normal"/>
</selector>

4. Create UI button and define its background
Now all you need to do is to define a button and set its background to selector file defined earlier. In my case this is button_bgnd_left.xml file:

<Button
    android:id="@+id/calc_addPerson_button"
    android:layout_width="@dimen/height_button_big"
    android:layout_height="fill_parent"
    android:background="@drawable/button_bgnd_left"
    android:drawableLeft="@drawable/add_person"
    android:text="@string/calculation_button_addPerson_text" />

Did I help you?
I manage this blog and share my knowledge for free sacrificing my time. If you appreciate it and find this information helpful, please consider making a donation in order to keep this page alive and improve quality

Donate Button with Credit Cards

Thank You!





Android custom ListView tutorial (including source code)

3 11 2012

This tutorial will show how to prepare ListView with custom items and buttons (with click handlers) like this:

Android ListView with custom items

Source Code

The source code for this tutorial is in this post. You can download it and use as You like.

1. Create single list item layout

Item consists of three views: name, value (EtitTexts) and delete (Button). Its atom_pay_list_item.xml layout is as follows:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" >

    <EditText
        android:id="@+id/atomPay_name"
        android:layout_width="0sp"
        android:layout_height="fill_parent"
        android:layout_weight="2"
        android:gravity="center_vertical"
        android:hint="@string/EnterPays_atomPay_nameHint"
        android:textAppearance="?android:attr/textAppearanceSmall" />

    <EditText
        android:id="@+id/atomPay_value"
        android:layout_width="0sp"
        android:layout_height="fill_parent"
        android:layout_weight="1"
        android:gravity="center"
        android:inputType="numberDecimal"
        android:text="@string/EnterPays_TextView_ZeroValue"
        android:textAppearance="?android:attr/textAppearanceSmall" />

    <ImageButton
        android:id="@+id/atomPay_removePay"
        android:layout_width="@dimen/width_button"
        android:layout_height="fill_parent"
        android:contentDescription="@string/emptyText"
        android:onClick="removeAtomPayOnClickHandler"
        android:src="@android:drawable/ic_menu_delete" />
</LinearLayout>

Notice, that the button has onClick handler defined in xml layout file. This is because I want to refer its action to specific list item, so handler will be implemented in Activity file and each button will know to which list item it belongs. Later I will show how to do it in detail.

2. Create list item adapter

This is java class that is a controller for atom_pay_list_item.xml. It keeps references for all of its views and puts these references in tags and extends ArrayAdapter. My adapter looks as follows:

public class AtomPayListAdapter extends ArrayAdapter<AtomPayment> {

	private List<AtomPayment> items;
	private int layoutResourceId;
	private Context context;

	public AtomPayListAdapter(Context context, int layoutResourceId, List<AtomPayment> items) {
		super(context, layoutResourceId, items);
		this.layoutResourceId = layoutResourceId;
		this.context = context;
		this.items = items;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		View row = convertView;
		AtomPaymentHolder holder = null;

		LayoutInflater inflater = ((Activity) context).getLayoutInflater();
		row = inflater.inflate(layoutResourceId, parent, false);

		holder = new AtomPaymentHolder();
		holder.atomPayment = items.get(position);
		holder.removePaymentButton = (ImageButton)row.findViewById(R.id.atomPay_removePay);
		holder.removePaymentButton.setTag(holder.atomPayment);

		holder.name = (TextView)row.findViewById(R.id.atomPay_name);
		holder.value = (TextView)row.findViewById(R.id.atomPay_value);

		row.setTag(holder);

		setupItem(holder);
		return row;
	}

	private void setupItem(AtomPaymentHolder holder) {
		holder.name.setText(holder.atomPayment.getName());
		holder.value.setText(String.valueOf(holder.atomPayment.getValue()));
	}

	public static class AtomPaymentHolder {
		AtomPayment atomPayment;
		TextView name;
		TextView value;
		ImageButton removePaymentButton;
	}
}

Here I display list of AtomPayment class items. There are three most important elements here:

  • AtomPayListAdapter constructor: sets some private fields and calls superclass constructor. It also gets the List of AtomPayment objects. Its implementation is obligatory.
  • AtomPaymentHolder: static class that holds references to all views that I have to set in this list item. I also keep the AtomPayment object that references to this particular item in list. I set it as tag for ImageButton, that will help me to find the AtomPayment item on list, that user wanted to remove
  • Overriden getView method: called by superclass. Its goal is to return the single List row. I create its fields and setup their values and store them in static holder. Holder then is put in row’s tag element. Note that there is an performance issue: row is being recreated each time it is displayed. I used to add some flag in holder like isCreated, and set it to true after row was already created. then you can add if statement and read tag’s holder instead of creating it from scratch.

My AtomPayment.java is quite simple as for now and it looks a bit like BasicNameValuePair:

public class AtomPayment implements Serializable {
	private String name = "";
	private double value = 0;

	public AtomPayment(String name, double value) {
		this.setName(name);
		this.setValue(value);
	}
...
}

Of course there are additionally getters and setters for each private field.

3. add ListView in your activity layout xml file

In the simpliest form, it is enough to add this view to activity layout:

<ListView 
	android:id="@+id/EnterPays_atomPaysList"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content">
</ListView>

4. Set up adapter to this list view in Activity Java code

In order to display items in ListView you need to set up its adapter and map it to some other ArrayList of AtomPayment objects (as I am extending an Array adapter here). Here is code that is responsible for binding adapter to editPersonData.getAtomPayments() ArrayList:

AtomPayListAdapter adapter = new AtomPayListAdapter(AddNewPerson.this, R.layout.atom_pay_list_item, editPersonData.getAtomPayments());
ListView atomPaysListView = (ListView)findViewById(R.id.EnterPays_atomPaysList);
atomPaysListView.setAdapter(adapter);

5. Adding / removing items to ListView (and its adapter)

Adapter is handled just like any other ArrayList, so adding new element to it is as simple as:

AtomPayment testAtomPayment = new AtomPayment("Test", 13);
adapter.add(testAtomPayment);
adapter.remove(testAtomPayment);

6. Handle Remove Payment button click event

In an activity’s code, where ListView is displayed, add public method that will handle remove button click action. The method name has to be exactly the same as it was in atom_pay_list_item.xml:

android:onClick="removeAtomPayOnClickHandler"

The method body is as follows:

public void removeAtomPayOnClickHandler(View v) {
	AtomPayment itemToRemove = (AtomPayment)v.getTag();
	adapter.remove(itemToRemove);
}

The AtomPayment object was stored in ImageButton’s Tag element. Now it is enough to read it from Tag, and remove this item from the adapter.

7. Incorporate remove confirmation dialog window

Probably you need also make sure that user intentionally pressed the remove button by asking him additional question in confirmation dialog like this one:

Confirmation dialog sample

Please go to my post that contains tutorial how to show and handle confirmation dialog to find out how to do it.

Source Code

The source code for this tutorial is in this post. You can download it and use as You like.

Did I help you?
I manage this blog and share my knowledge for free sacrificing my time. If you appreciate it and find this information helpful, please consider making a donation in order to keep this page alive and improve quality

Donate Button with Credit Cards

Thank You!








%d bloggers like this: