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