blob: aeab9974c1c65729ca96bd2f4679e975491126ef [file] [log] [blame]
Dirk Dougherty22558d02009-12-10 16:25:06 -08001page.title=Live Folders
Scott Main796ce772011-02-16 10:04:45 -08002parent.title=Articles
3parent.link=../browser.html?tag=article
Dirk Dougherty22558d02009-12-10 16:25:06 -08004@jd:body
5
6<p>Live folders, introduced in Android 1.5 (API Level 3), let you display any source of data
7on the Home screen without forcing the user to launch an application. A live
8folder is simply a real-time view of a {@link android.content.ContentProvider}.
9As such, a live folder can be used to display all of the user's contacts or
10bookmarks, email, playlists, an RSS feed, and so on. The possibilities are
11endless! </p>
12
13<p>The platform includes several standard folders for displaying contacts. For
14instance, the screenshot below shows the content of the live folders that
15displays all contacts with a phone number:</p>
16
17<img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 267px; height: 400px;" src="images/contacts.png" alt="" id="BLOGGER_PHOTO_ID_5323287788220889218" border="0">
18
19<p>If a contacts sync happens in the background while the user is browsing this live
20folder, the user will see the change happen in real-time. Live folders are not
21only useful, but they are also easy to add to to your application and data.
22
23This articles shows how to add a live folder to an example application called
24Shelves. To better understand how live folders work, you can <a
25href="http://code.google.com/p/shelves">download the source code of the
26application</a> and modify it by following the instructions below.</p>
27
28<p>To give the user the option to create a new live folder for an application,
29you first need to create a new activity with an intent filter whose action is
30<code>android.intent.action.CREATE_LIVE_FOLDER</code>. To do so, simply open
31<code>AndroidManifest.xml</code> and add something similar to this:</p>
32
33<pre>&lt;activity
34 android:name=".activity.BookShelfLiveFolder"
35 android:label="BookShelf"&gt;
36 &lt;intent-filter&gt;
37 &lt;action android:name="android.intent.action.CREATE_LIVE_FOLDER" /&gt;
38 &lt;category android:name="android.intent.category.DEFAULT" /&gt;
39 &lt;/intent-filter&gt;
40&lt;/activity&gt;</pre>
41
42<p>The label and icon of this activity are what the user will see on the Home
43screen when choosing a live folder to create:</p>
44
45<img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 267px; height: 400px;" src="images/device_002.png" alt="" id="BLOGGER_PHOTO_ID_5323289217773103922" border="0">
46
47<p>Since you just need an intent filter, it is possible, and sometimes advised,
48to reuse an existing activity. In the case of Shelves, we will create a new
49activity, <code>org.curiouscreature.android.shelves.activity.BookShelfLiveFolder</code>.
50The role of this activity is to send an <code>Intent</code> result to Home
51containing the description of the live folder: its name, icon, display mode and
52content URI. The content URI is very important as it describes what
53<code>ContentProvider</code> will be used to populate the live folder. The code
54of the activity is very simple as you can see here:</p>
55
56<pre>public class BookShelfLiveFolder extends Activity {
57 public static final Uri CONTENT_URI = Uri.parse("content://shelves/live_folders/books");
58
59 &#64;Override
60 protected void onCreate(Bundle savedInstanceState) {
61 super.onCreate(savedInstanceState);
62
63 final Intent intent = getIntent();
64 final String action = intent.getAction();
65
66 if (LiveFolders.ACTION_CREATE_LIVE_FOLDER.equals(action)) {
67 setResult(RESULT_OK, createLiveFolder(this, CONTENT_URI,
68 "Books", R.drawable.ic_live_folder));
69 } else {
70 setResult(RESULT_CANCELED);
71 }
72
73 finish();
74 }
75
76 private static Intent createLiveFolder(Context context, Uri uri, String name, int icon) {
77 final Intent intent = new Intent();
78
79 intent.setData(uri);
80 intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_NAME, name);
81 intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_ICON,
82 Intent.ShortcutIconResource.fromContext(context, icon));
83 intent.putExtra(LiveFolders.EXTRA_LIVE_FOLDER_DISPLAY_MODE, LiveFolders.DISPLAY_MODE_LIST);
84
85 return intent;
86 }
87}</pre>
88
89<p>This activity, when invoked with the<code>ACTION_CREATE_LIVE_FOLDER</code>
90intent, returns an intent with a URI,
91<code>content://shelves/live_folders/books</code>, and three extras to describe
92the live folder. There are other extras and constants you can use and you should
93refer to the documentation of <code>android.provider.LiveFolders</code> for more
94details. When Home receives this intent, a new live folder is created on the
95user's desktop, with the name and icon you provided. Then, when the user clicks
96on the live folder to open it, Home queries the content provider referenced by
97the provided URI.</p>
98
99<p>Live folders' content providers must obey specific naming rules. The
100<code>Cursor</code> returned by the <code>query()</code> method must have at
101least two columns named <code>LiveFolders._ID</code> and
102<code>LiveFolders.NAME</code>. The first one is the unique identifier of each
103item in the live folder and the second one is the name of the item. There are
104other column names you can use to specify an icon, a description, the intent to
105associate with the item (fired when the user clicks that item), etc. Again,
106refer to the documentation of <code>android.provider.LiveFolders</code> for more
107details.</p><p>In our example, all we need to do is modify the existing provider
108in Shelves called
109<code>org.curiouscreature.android.shelves.provider.BooksProvider</code>. First,
110we need to modify the <code>URI_MATCHER</code> to recognize our
111<code>content://shelves/live_folders/books</code> content URI:</p>
112
113<pre>private static final int LIVE_FOLDER_BOOKS = 4;
114// ...
115URI_MATCHER.addURI(AUTHORITY, "live_folders/books", LIVE_FOLDER_BOOKS);</pre>
116
117<p>Then we need to create a new projection map for the cursor. A projection map
118can be used to "rename" columns. In our case, we will replace
119<code>BooksStore.Book._ID</code>, <code>BooksStore.Book.TITLE</code> and
120<code>BooksStore.Book.AUTHORS</code> with <code>LiveFolders._ID</code>,
121<code>LiveFolders.TITLE</code> and <code>LiveFolders.DESCRIPTION</code>:</p>
122
123<pre>private static final HashMap&lt;string, string=""&gt; LIVE_FOLDER_PROJECTION_MAP;
124static {
125 LIVE_FOLDER_PROJECTION_MAP = new HashMap&lt;string, string=""&gt;();
126 LIVE_FOLDER_PROJECTION_MAP.put(LiveFolders._ID, BooksStore.Book._ID +
127 " AS " + LiveFolders._ID);
128 LIVE_FOLDER_PROJECTION_MAP.put(LiveFolders.NAME, BooksStore.Book.TITLE +
129 " AS " + LiveFolders.NAME);
130 LIVE_FOLDER_PROJECTION_MAP.put(LiveFolders.DESCRIPTION, BooksStore.Book.AUTHORS +
131 " AS " + LiveFolders.DESCRIPTION);
132}</pre>
133
134<p>Because we are providing a title and a description for each row, Home will
135automatically display each item of the live folder with two lines of text.
136Finally, we implement the <code>query()</code> method by supplying our
137projection map to the SQL query builder:</p>
138
139<pre>public Cursor query(Uri uri, String[] projection, String selection,
140 String[] selectionArgs, String sortOrder) {
141
142 SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
143
144 switch (URI_MATCHER.match(uri)) {
145 // ...
146 case LIVE_FOLDER_BOOKS:
147 qb.setTables("books");
148 qb.setProjectionMap(LIVE_FOLDER_PROJECTION_MAP);
149 break;
150 default:
151 throw new IllegalArgumentException("Unknown URI " + uri);
152 }
153
154 SQLiteDatabase db = mOpenHelper.getReadableDatabase();
155 Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, BooksStore.Book.DEFAULT_SORT_ORDER);
156 c.setNotificationUri(getContext().getContentResolver(), uri);
157
158 return c;
159}</pre>
160
161<p>You can now compile and deploy the application, go to the Home screen and
162try to add a live folder. You can add a books live folder to your Home screen
163and when you open it, see the list of all of your books, with their
164titles and authors, and all it took was a few lines of code:</p>
165
166<img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 267px; height: 400px;" src="images/device.png" alt="" id="BLOGGER_PHOTO_ID_5323293545495859234" border="0"></p>
167
168<p>The live folders API is extremely simple and relies only on intents and
169content URI. If you want to see more examples of live folders
170implementation, you can read the source code of the <a href="http://android.git.kernel.org/?p=platform/packages/apps/Contacts.git;a=tree;h=refs/heads/cupcake;hb=cupcake">Contacts application</a> and of the <a href="http://android.git.kernel.org/?p=platform/packages/providers/ContactsProvider.git;a=tree;h=refs/heads/cupcake;hb=cupcake">Contacts provider</a>.</p><p>You can also download the result of our exercise, the <a href="http://jext.free.fr/CupcakeShelves.zip">modified version of Shelves with live folders support</a>.</p>