| Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 1 | page.title=Live Folders |
| Scott Main | 796ce77 | 2011-02-16 10:04:45 -0800 | [diff] [blame] | 2 | parent.title=Articles |
| 3 | parent.link=../browser.html?tag=article |
| Dirk Dougherty | 22558d0 | 2009-12-10 16:25:06 -0800 | [diff] [blame] | 4 | @jd:body |
| 5 | |
| 6 | <p>Live folders, introduced in Android 1.5 (API Level 3), let you display any source of data |
| 7 | on the Home screen without forcing the user to launch an application. A live |
| 8 | folder is simply a real-time view of a {@link android.content.ContentProvider}. |
| 9 | As such, a live folder can be used to display all of the user's contacts or |
| 10 | bookmarks, email, playlists, an RSS feed, and so on. The possibilities are |
| 11 | endless! </p> |
| 12 | |
| 13 | <p>The platform includes several standard folders for displaying contacts. For |
| 14 | instance, the screenshot below shows the content of the live folders that |
| 15 | displays 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 |
| 20 | folder, the user will see the change happen in real-time. Live folders are not |
| 21 | only useful, but they are also easy to add to to your application and data. |
| 22 | |
| 23 | This articles shows how to add a live folder to an example application called |
| 24 | Shelves. To better understand how live folders work, you can <a |
| 25 | href="http://code.google.com/p/shelves">download the source code of the |
| 26 | application</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, |
| 29 | you 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><activity |
| 34 | android:name=".activity.BookShelfLiveFolder" |
| 35 | android:label="BookShelf"> |
| 36 | <intent-filter> |
| 37 | <action android:name="android.intent.action.CREATE_LIVE_FOLDER" /> |
| 38 | <category android:name="android.intent.category.DEFAULT" /> |
| 39 | </intent-filter> |
| 40 | </activity></pre> |
| 41 | |
| 42 | <p>The label and icon of this activity are what the user will see on the Home |
| 43 | screen 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, |
| 48 | to reuse an existing activity. In the case of Shelves, we will create a new |
| 49 | activity, <code>org.curiouscreature.android.shelves.activity.BookShelfLiveFolder</code>. |
| 50 | The role of this activity is to send an <code>Intent</code> result to Home |
| 51 | containing the description of the live folder: its name, icon, display mode and |
| 52 | content 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 |
| 54 | of 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 | @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> |
| 90 | intent, returns an intent with a URI, |
| 91 | <code>content://shelves/live_folders/books</code>, and three extras to describe |
| 92 | the live folder. There are other extras and constants you can use and you should |
| 93 | refer to the documentation of <code>android.provider.LiveFolders</code> for more |
| 94 | details. When Home receives this intent, a new live folder is created on the |
| 95 | user's desktop, with the name and icon you provided. Then, when the user clicks |
| 96 | on the live folder to open it, Home queries the content provider referenced by |
| 97 | the 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 |
| 101 | least two columns named <code>LiveFolders._ID</code> and |
| 102 | <code>LiveFolders.NAME</code>. The first one is the unique identifier of each |
| 103 | item in the live folder and the second one is the name of the item. There are |
| 104 | other column names you can use to specify an icon, a description, the intent to |
| 105 | associate with the item (fired when the user clicks that item), etc. Again, |
| 106 | refer to the documentation of <code>android.provider.LiveFolders</code> for more |
| 107 | details.</p><p>In our example, all we need to do is modify the existing provider |
| 108 | in Shelves called |
| 109 | <code>org.curiouscreature.android.shelves.provider.BooksProvider</code>. First, |
| 110 | we 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 | // ... |
| 115 | URI_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 |
| 118 | can 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<string, string=""> LIVE_FOLDER_PROJECTION_MAP; |
| 124 | static { |
| 125 | LIVE_FOLDER_PROJECTION_MAP = new HashMap<string, string="">(); |
| 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 |
| 135 | automatically display each item of the live folder with two lines of text. |
| 136 | Finally, we implement the <code>query()</code> method by supplying our |
| 137 | projection 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 |
| 162 | try to add a live folder. You can add a books live folder to your Home screen |
| 163 | and when you open it, see the list of all of your books, with their |
| 164 | titles 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 |
| 169 | content URI. If you want to see more examples of live folders |
| 170 | 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> |