Wireframe email widget

* Formatting and assets are preliminary
* Functionality correct
* Needs cleanup, etc.

Change-Id: I75051df93d233ef529a616c7a9efae403d320bd2
This commit is contained in:
Marc Blank 2010-09-23 09:19:44 -07:00
parent 4f4c902916
commit 1b9337ea4f
19 changed files with 991 additions and 89 deletions

View File

@ -14,47 +14,70 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.email">
<original-package android:name="com.android.email" />
<original-package
android:name="com.android.email" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.READ_OWNER_DATA"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission
android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission
android:name="android.permission.READ_CONTACTS"/>
<uses-permission
android:name="android.permission.READ_OWNER_DATA"/>
<uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission
android:name="android.permission.INTERNET"/>
<uses-permission
android:name="android.permission.VIBRATE"/>
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.GET_ACCOUNTS" />
<uses-permission
android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission
android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission
android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission
android:name="android.permission.WRITE_SYNC_SETTINGS" />
<!-- For EAS purposes; could be removed when EAS has a permanent home -->
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CALENDAR"/>
<uses-permission android:name="android.permission.READ_CALENDAR"/>
<uses-permission
android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission
android:name="android.permission.WRITE_CALENDAR"/>
<uses-permission
android:name="android.permission.READ_CALENDAR"/>
<!-- Only required if a store implements push mail and needs to keep network open -->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission
android:name="android.permission.WAKE_LOCK"/>
<uses-permission
android:name="android.permission.READ_PHONE_STATE"/>
<!-- Grant permission to other apps to view attachments -->
<permission android:name="com.android.email.permission.READ_ATTACHMENT"
android:permissionGroup="android.permission-group.MESSAGES"
android:protectionLevel="dangerous"
android:label="@string/read_attachment_label"
android:description="@string/read_attachment_desc"/>
<uses-permission android:name="com.android.email.permission.READ_ATTACHMENT"/>
<permission
android:name="com.android.email.permission.READ_ATTACHMENT"
android:permissionGroup="android.permission-group.MESSAGES"
android:protectionLevel="dangerous"
android:label="@string/read_attachment_label"
android:description="@string/read_attachment_desc"/>
<uses-permission
android:name="com.android.email.permission.READ_ATTACHMENT"/>
<!-- Grant permission to system apps to access provider (see provider below) -->
<permission android:name="com.android.email.permission.ACCESS_PROVIDER"
android:protectionLevel="signatureOrSystem"
android:label="@string/permission_access_provider_label"
android:description="@string/permission_access_provider_desc"/>
<uses-permission android:name="com.android.email.permission.ACCESS_PROVIDER"/>
<permission
android:name="com.android.email.permission.ACCESS_PROVIDER"
android:protectionLevel="signatureOrSystem"
android:label="@string/permission_access_provider_label"
android:description="@string/permission_access_provider_desc"/>
<uses-permission
android:name="com.android.email.permission.ACCESS_PROVIDER"/>
<application
android:icon="@mipmap/icon"
@ -68,9 +91,12 @@
android:name=".activity.Welcome"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
<action
android:name="android.intent.action.MAIN" />
<category
android:name="android.intent.category.DEFAULT" />
<category
android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
@ -124,8 +150,10 @@
android:label="@string/account_settings_action"
>
<intent-filter>
<action android:name="com.android.email.activity.setup.ACCOUNT_MANAGER_ENTRY" />
<category android:name="android.intent.category.DEFAULT" />
<action
android:name="com.android.email.activity.setup.ACCOUNT_MANAGER_ENTRY" />
<category
android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
@ -152,10 +180,11 @@
android:theme="@android:style/Theme.Holo.DialogWhenLarge"
>
<intent-filter
android:label="@string/account_shortcut_picker_name"
>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
<category android:name="android.intent.category.DEFAULT" />
android:label="@string/account_shortcut_picker_name">
<action
android:name="android.intent.action.CREATE_SHORTCUT" />
<category
android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
@ -186,7 +215,8 @@
>
<intent-filter>
<!-- This action is only to allow an entry point for launcher shortcuts -->
<action android:name="android.intent.action.MAIN" />
<action
android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
@ -197,11 +227,16 @@
<activity
android:name=".activity.MessageFileView"
>
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.VIEW" />
<data android:mimeType="application/eml" />
<data android:mimeType="message/rfc822" />
<category android:name="android.intent.category.DEFAULT" />
<intent-filter
android:label="@string/app_name">
<action
android:name="android.intent.action.VIEW" />
<data
android:mimeType="application/eml" />
<data
android:mimeType="message/rfc822" />
<category
android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
@ -210,45 +245,74 @@
android:enabled="false"
>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="mailto" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<action
android:name="android.intent.action.VIEW" />
<action
android:name="android.intent.action.SENDTO" />
<data
android:scheme="mailto" />
<category
android:name="android.intent.category.DEFAULT" />
<category
android:name="android.intent.category.BROWSABLE" />
</intent-filter>
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.SEND" />
<data android:mimeType="*/*" />
<category android:name="android.intent.category.DEFAULT" />
<intent-filter
android:label="@string/app_name">
<action
android:name="android.intent.action.SEND" />
<data
android:mimeType="*/*" />
<category
android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter android:label="@string/app_name">
<action android:name="android.intent.action.SEND_MULTIPLE" />
<data android:mimeType="*/*" />
<category android:name="android.intent.category.DEFAULT" />
<intent-filter
android:label="@string/app_name">
<action
android:name="android.intent.action.SEND_MULTIPLE" />
<data
android:mimeType="*/*" />
<category
android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action
android:name="com.android.email.intent.action.REPLY" />
</intent-filter>
</activity>
<!--EXCHANGE-REMOVE-SECTION-START-->
<receiver android:name="com.android.exchange.EmailSyncAlarmReceiver"/>
<receiver android:name="com.android.exchange.MailboxAlarmReceiver"/>
<receiver
android:name="com.android.exchange.EmailSyncAlarmReceiver"/>
<receiver
android:name="com.android.exchange.MailboxAlarmReceiver"/>
<!--EXCHANGE-REMOVE-SECTION-END-->
<receiver android:name=".service.AttachmentDownloadService$Watchdog"
<receiver
android:name=".service.AttachmentDownloadService$Watchdog"
android:enabled="true"/>
<receiver android:name=".service.EmailBroadcastReceiver" android:enabled="true">
<receiver
android:name=".service.EmailBroadcastReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.DEVICE_STORAGE_LOW" />
<action android:name="android.intent.action.DEVICE_STORAGE_OK" />
<action
android:name="android.intent.action.BOOT_COMPLETED" />
<action
android:name="android.intent.action.DEVICE_STORAGE_LOW" />
<action
android:name="android.intent.action.DEVICE_STORAGE_OK" />
</intent-filter>
<!-- To handle secret code to activate the debug screen. -->
<intent-filter>
<action android:name="android.provider.Telephony.SECRET_CODE" />
<action
android:name="android.provider.Telephony.SECRET_CODE" />
<!-- "36245" = "email" -->
<data android:scheme="android_secret_code" android:host="36245" />
<data
android:scheme="android_secret_code"
android:host="36245" />
</intent-filter>
</receiver>
<service android:name=".service.EmailBroadcastProcessorService" />
<service
android:name=".service.EmailBroadcastProcessorService" />
<!-- Support for DeviceAdmin / DevicePolicyManager. See SecurityPolicy class for impl. -->
<receiver
@ -260,7 +324,8 @@
android:name="android.app.device_admin"
android:resource="@xml/device_admin" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
<action
android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
@ -290,7 +355,8 @@
android:enabled="true"
>
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
<action
android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
@ -303,7 +369,8 @@
android:name="com.android.email.service.PopImapSyncAdapterService"
android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
<action
android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter_pop_imap" />
@ -315,7 +382,8 @@
android:name="com.android.exchange.EmailSyncAdapterService"
android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
<action
android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter_email" />
@ -326,7 +394,8 @@
android:name="com.android.exchange.ContactsSyncAdapterService"
android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
<action
android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter_contacts" />
@ -337,7 +406,8 @@
android:name="com.android.exchange.CalendarSyncAdapterService"
android:exported="true">
<intent-filter>
<action android:name="android.content.SyncAdapter" />
<action
android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter_calendar" />
@ -357,7 +427,8 @@
android:enabled="true"
>
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
<action
android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
@ -365,7 +436,7 @@
/>
</service>
<!--
EasAuthenticatorService with the altenative label. Disabled by default,
EasAuthenticatorService with the alternative label. Disabled by default,
and OneTimeInitializer enables it if the vendor policy tells so.
-->
<service
@ -374,7 +445,8 @@
android:enabled="false"
>
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
<action
android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
@ -395,7 +467,7 @@
it exposes user passwords and other confidential information. -->
<provider
android:name=".provider.EmailProvider"
android:authorities="com.android.email.provider"
android:authorities="com.android.email.provider; com.android.email.notifier"
android:multiprocess="true"
android:permission="com.android.email.permission.ACCESS_PROVIDER"
android:label="@string/app_name"
@ -408,9 +480,27 @@
android:readPermission="android.permission.READ_CONTACTS"
android:multiprocess="false"
>
<meta-data android:name="android.content.ContactDirectory" android:value="true"/>
<meta-data
android:name="android.content.ContactDirectory"
android:value="true"/>
</provider>
<!--EXCHANGE-REMOVE-SECTION-END-->
<!-- Email AppWidget definitions -->
<service
android:name=".provider.WidgetProvider$WidgetService"
android:enabled="true"
android:exported="true"
/>
<receiver
android:name=".provider.WidgetProvider" >
<intent-filter>
<action
android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_info" />
</receiver>
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false" android:drawable="@drawable/widget_bg" />
<item android:state_pressed="true" android:drawable="@drawable/widget_bg_press" />
<item android:state_focused="true" android:drawable="@drawable/widget_bg_focus" />
<item android:drawable="@drawable/widget_bg" />
</selector>

68
res/layout/widget.xml Normal file
View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget_foo"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_margin="5dip"
android:background="@drawable/widget_background"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/widget_header"
android:layout_width="fill_parent"
android:layout_height="60dip"
android:background="@drawable/widget_bg_top"
android:orientation="horizontal">
<ImageView
android:id="@+id/widget_logo"
android:layout_width="60dip"
android:layout_height="60dip"
android:layout_marginLeft="5dip"
android:layout_marginRight="2dip"
android:layout_marginBottom="5dip"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:src="@mipmap/icon"/>
<ImageButton
android:id="@+id/widget_compose"
android:layout_width="56dip"
android:layout_height="56dip"
android:layout_marginRight="5dip"
android:layout_marginTop="3dip"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:src="@drawable/ic_menu_compose"/>
<TextView
android:id="@+id/widget_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/widget_logo"
android:layout_marginTop="10dip"
android:textSize="18sp"
android:textColor="#F000"
android:textStyle="bold"
android:text="@string/widget_unread" />
</RelativeLayout>
<ListView
android:id="@+id/message_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="@id/widget_header"
android:cacheColorHint="#00000000"
/>
</RelativeLayout>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/widget_date"
android:layout_width="64dip"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:textColor="#F000"
android:textSize="14sp"
android:paddingLeft="10dip"
android:drawablePadding="4dip"
/>
<TextView
android:id="@+id/widget_from"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@id/widget_date"
android:textColor="#F000"
android:ellipsize="end"
android:singleLine="true"
android:textSize="16sp"
android:paddingLeft="10dip"
android:drawablePadding="4dip"
/>
<TextView
android:id="@+id/widget_subject"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/widget_from"
android:minLines="2"
android:textSize="14sp"
android:textColor="#F000"
android:paddingLeft="10dip"
/>
</RelativeLayout>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="50dip"
android:orientation="horizontal">
<TextView
android:id="@+id/loading_text"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:gravity="center"
android:textColor="#60FFFFFF"
android:textSize="20sp"
android:shadowColor="#FF000000"
android:shadowDx="0.0"
android:shadowDy="1.0"
android:shadowRadius="2.0" />
</LinearLayout>

View File

@ -1012,6 +1012,19 @@ save attachment.</string>
<!-- Generic string for "current position" / "total number" [CHAR LIMIT=12] -->
<string name="position_of_count"><xliff:g example="1">%1$d</xliff:g> of <xliff:g
example="12">%2$s</xliff:g></string>
<!-- Widget -->
<!-- Instruction for how to move to different widget views [CHAR LIMIT=50] -->
<string name="widget_other_views">Tap email icon for other views</string>
<!-- Header for the "All Mail" widget view (showing all of the user's mail) [CHAR LIMIT=20] -->
<string name="widget_all_mail">All Mail</string>
<!-- Header for the "Unread" widget view (showing all unread mail) [CHAR LIMIT=20] -->
<string name="widget_unread">All Unread</string>
<!-- Header for the "Starred" widget view (showing all starred mail) [CHAR LIMIT=20] -->
<string name="widget_starred">All Starred</string>
<!-- Shown when waiting for mail data to be loaded into the widget list view [CHAR LIMIT=20] -->
<string name="widget_loading">Loading\u2026</string>
<!--
Strings for temporary UI
STOPSHIP Remove them or move them up and make translatable

22
res/xml/widget_info.xml Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="220dp"
android:minHeight="220dp"
android:updatePeriodMillis="0"
android:initialLayout="@layout/widget" >
</appwidget-provider>

View File

@ -62,13 +62,17 @@ public class MessageView extends MessageViewBase implements View.OnClickListener
* @param mailboxId identifies the sequence of messages used for newer/older navigation.
*/
public static void actionView(Context context, long messageId, long mailboxId) {
context.startActivity(getActionViewIntent(context, messageId, mailboxId));
}
public static Intent getActionViewIntent(Context context, long messageId, long mailboxId) {
if (messageId < 0) {
throw new IllegalArgumentException("MessageView invalid messageId " + messageId);
}
Intent i = new Intent(context, MessageView.class);
i.putExtra(EXTRA_MESSAGE_ID, messageId);
i.putExtra(EXTRA_MAILBOX_ID, mailboxId);
context.startActivity(i);
return i;
}
@Override

View File

@ -63,13 +63,16 @@ import java.util.UUID;
*/
public abstract class EmailContent {
public static final String AUTHORITY = EmailProvider.EMAIL_AUTHORITY;
public static final String NOTIFIER_AUTHORITY = EmailProvider.EMAIL_NOTIFIER_AUTHORITY;
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
public static final String PARAMETER_LIMIT = "limit";
public static final Uri CONTENT_NOTIFIER_URI = Uri.parse("content://" + NOTIFIER_AUTHORITY);
// All classes share this
public static final String RECORD_ID = "_id";
private static final String[] COUNT_COLUMNS = new String[]{"count(*)"};
public static final String[] COUNT_COLUMNS = new String[]{"count(*)"};
/**
* This projection can be used with any of the EmailContent classes, when all you need
@ -448,7 +451,6 @@ public abstract class EmailContent {
public static final String DELETED_TABLE_NAME = "Message_Deletes";
// To refer to a specific message, use ContentUris.withAppendedId(CONTENT_URI, id)
@SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/message");
public static final Uri CONTENT_URI_LIMIT_1 = uriWithLimit(CONTENT_URI, 1);
public static final Uri SYNCED_CONTENT_URI =
@ -457,6 +459,8 @@ public abstract class EmailContent {
Uri.parse(EmailContent.CONTENT_URI + "/deletedMessage");
public static final Uri UPDATED_CONTENT_URI =
Uri.parse(EmailContent.CONTENT_URI + "/updatedMessage");
public static final Uri NOTIFIER_URI =
Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/message");
public static final String KEY_TIMESTAMP_DESC = MessageColumns.TIMESTAMP + " desc";
@ -925,7 +929,6 @@ public abstract class EmailContent {
public static final class Account extends EmailContent implements AccountColumns, Parcelable {
public static final String TABLE_NAME = "Account";
@SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/account");
public static final Uri ADD_TO_FIELD_URI =
Uri.parse(EmailContent.CONTENT_URI + "/accountIdAddToField");

View File

@ -37,6 +37,7 @@ import android.accounts.AccountManager;
import android.content.ContentProvider;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
@ -116,6 +117,10 @@ public class EmailProvider extends ContentProvider {
public static final int BODY_DATABASE_VERSION = 6;
public static final String EMAIL_AUTHORITY = "com.android.email.provider";
// The notifier authority is used to send notifications regarding changes to messages (insert,
// delete, or update) and is intended as an optimization for use by clients of message list
// cursors (initially, the email AppWidget).
public static final String EMAIL_NOTIFIER_AUTHORITY = "com.android.email.notifier";
private static final int ACCOUNT_BASE = 0;
private static final int ACCOUNT = ACCOUNT_BASE;
@ -911,6 +916,7 @@ public class EmailProvider extends ContentProvider {
int table = match >> BASE_SHIFT;
String id = "0";
boolean messageDeletion = false;
ContentResolver resolver = context.getContentResolver();
if (Email.LOGD) {
Log.v(TAG, "EmailProvider.delete: uri=" + uri + ", match is " + match);
@ -940,6 +946,7 @@ public class EmailProvider extends ContentProvider {
// Bodies are auto-deleted here; Attachments are auto-deleted via trigger
messageDeletion = true;
db.beginTransaction();
resolver.notifyChange(Message.NOTIFIER_URI, null);
break;
}
switch (match) {
@ -1045,7 +1052,7 @@ public class EmailProvider extends ContentProvider {
}
// Notify all existing cursors.
getContext().getContentResolver().notifyChange(EmailContent.CONTENT_URI, null);
resolver.notifyChange(EmailContent.CONTENT_URI, null);
return result;
}
@ -1101,6 +1108,8 @@ public class EmailProvider extends ContentProvider {
if (Email.DEBUG_THREAD_CHECK) Email.warnIfUiThread();
int match = sURIMatcher.match(uri);
Context context = getContext();
ContentResolver resolver = context.getContentResolver();
// See the comment at delete(), above
SQLiteDatabase db = getDatabase(context);
int table = match >> BASE_SHIFT;
@ -1121,10 +1130,12 @@ public class EmailProvider extends ContentProvider {
try {
switch (match) {
case MESSAGE:
resolver.notifyChange(Message.NOTIFIER_URI, null);
//$FALL-THROUGH$
case UPDATED_MESSAGE:
case DELETED_MESSAGE:
case BODY:
case MESSAGE:
case ATTACHMENT:
case MAILBOX:
case ACCOUNT:
@ -1175,7 +1186,7 @@ public class EmailProvider extends ContentProvider {
}
// Notify all existing cursors.
getContext().getContentResolver().notifyChange(EmailContent.CONTENT_URI, null);
resolver.notifyChange(EmailContent.CONTENT_URI, null);
return resultUri;
}
@ -1360,6 +1371,7 @@ public class EmailProvider extends ContentProvider {
int match = sURIMatcher.match(uri);
Context context = getContext();
ContentResolver resolver = context.getContentResolver();
// See the comment at delete(), above
SQLiteDatabase db = getDatabase(context);
int table = match >> BASE_SHIFT;
@ -1411,10 +1423,12 @@ public class EmailProvider extends ContentProvider {
db.setTransactionSuccessful();
db.endTransaction();
break;
case BODY_ID:
case MESSAGE_ID:
case SYNCED_MESSAGE_ID:
resolver.notifyChange(Message.NOTIFIER_URI, null);
//$FALL-THROUGH$
case UPDATED_MESSAGE_ID:
case MESSAGE_ID:
case BODY_ID:
case ATTACHMENT_ID:
case MAILBOX_ID:
case ACCOUNT_ID:
@ -1490,7 +1504,7 @@ public class EmailProvider extends ContentProvider {
throw e;
}
context.getContentResolver().notifyChange(notificationUri, null);
resolver.notifyChange(notificationUri, null);
return result;
}

View File

@ -0,0 +1,577 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.email.provider;
import com.android.email.Email;
import com.android.email.R;
import com.android.email.activity.MessageCompose;
import com.android.email.activity.MessageView;
import com.android.email.data.ThrottlingCursorLoader;
import com.android.email.provider.EmailContent.Message;
import com.android.email.provider.EmailContent.MessageColumns;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.graphics.Paint.Align;
import android.graphics.Typeface;
import android.net.Uri;
import android.net.Uri.Builder;
import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.TextUtils.TruncateAt;
import android.text.format.DateUtils;
import android.text.style.StyleSpan;
import android.util.Log;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;
import java.util.HashMap;
import java.util.List;
public class WidgetProvider extends AppWidgetProvider {
private static final String TAG = "WidgetProvider";
/**
* When handling clicks in a widget ListView, a single PendingIntent template is provided to
* RemoteViews, and the individual "on click" actions are distinguished via a "fillInIntent"
* on each list element; when a click is received, this "fillInIntent" is merged with the
* PendingIntent using Intent.fillIn(). Since this mechanism does NOT preserve the Extras
* Bundle, we instead encode information about the action (e.g. view, reply, etc.) and its
* arguments (e.g. messageId, mailboxId, etc.) in an Uri which is added to the Intent via
* Intent.setDataAndType()
*
* The mime type MUST be set in the Intent, even though we do not use it; therefore, it's value
* is entirely arbitrary.
*
* Our "command" Uri is NOT used by the system in any manner, and is therefore constrained only
* in the requirement that it be syntactically valid.
*
* We use the following convention for our commands:
* widget://command/<command>/<arg1>[/<arg2>]
*/
private static final String WIDGET_DATA_MIME_TYPE = "com.android.email/widget_data";
private static final Uri COMMAND_URI = Uri.parse("widget://command");
// Command names and Uri's built upon COMMAND_URI
private static final String COMMAND_NAME_SWITCH_LIST_VIEW = "switch_list_view";
private static final Uri COMMAND_URI_SWITCH_LIST_VIEW =
COMMAND_URI.buildUpon().appendPath(COMMAND_NAME_SWITCH_LIST_VIEW).build();
private static final String COMMAND_NAME_VIEW_MESSAGE = "view_message";
private static final Uri COMMAND_URI_VIEW_MESSAGE =
COMMAND_URI.buildUpon().appendPath(COMMAND_NAME_VIEW_MESSAGE).build();
private static final int TOTAL_COUNT_UNKNOWN = -1;
private static final int MAX_MESSAGE_LIST_COUNT = 25;
private static final String SORT_DESCENDING = MessageColumns.TIMESTAMP + " DESC";
// Map holding our instantiated widgets, accessed by widget id
private static HashMap<Integer, EmailWidget> sWidgetMap = new HashMap<Integer, EmailWidget>();
private static AppWidgetManager sWidgetManager;
private static Context sContext;
private static ContentResolver sResolver;
private static TextPaint sDatePaint = new TextPaint();
/**
* Types of views that we're prepared to show in the widget - all mail, unread mail, and starred
* mail; we rotate between them. Each ViewType is composed of a selection string and a title.
*/
public enum ViewType {
ALL_MAIL(null, R.string.widget_all_mail),
UNREAD(MessageColumns.FLAG_READ + "=0", R.string.widget_unread),
STARRED(MessageColumns.FLAG_FAVORITE + "=1", R.string.widget_starred);
private final String selection;
private final int titleResource;
private String title;
ViewType(String _selection, int _titleResource) {
selection = _selection;
titleResource = _titleResource;
}
public String getTitle(Context context) {
if (title == null) {
title = context.getString(titleResource);
}
return title;
}
}
static class EmailWidget implements RemoteViewsService.RemoteViewsFactory {
// The widget identifier
private final int mWidgetId;
// The cursor underlying the message list for this widget; this must only be modified while
// holding mCursorLock
private volatile Cursor mCursor;
// A lock on our cursor, which is used in the UI thread while inflating views, and by
// our Loader in the background
private final Object mCursorLock = new Object();
// Number of records in the cursor
private int mCursorCount = TOTAL_COUNT_UNKNOWN;
// The widget's loader (derived from ThrottlingCursorLoader)
private WidgetLoader mLoader;
// The current view type (all mail, unread, or starred for now)
private ViewType mViewType = ViewType.ALL_MAIL;
// The projection to be used by the WidgetLoader
public static final String[] WIDGET_PROJECTION = new String[] {
EmailContent.RECORD_ID, MessageColumns.DISPLAY_NAME, MessageColumns.TIMESTAMP,
MessageColumns.SUBJECT, MessageColumns.FLAG_READ, MessageColumns.FLAG_FAVORITE,
MessageColumns.FLAG_ATTACHMENT, MessageColumns.MAILBOX_KEY, MessageColumns.SNIPPET,
MessageColumns.ACCOUNT_KEY
};
public static final int WIDGET_COLUMN_ID = 0;
public static final int WIDGET_COLUMN_DISPLAY_NAME = 1;
public static final int WIDGET_COLUMN_TIMESTAMP = 2;
public static final int WIDGET_COLUMN_SUBJECT = 3;
public static final int WIDGET_COLUMN_FLAG_READ = 4;
public static final int WIDGET_COLUMN_FLAG_FAVORITE = 5;
public static final int WIDGET_COLUMN_FLAG_ATTACHMENT = 6;
public static final int WIDGET_COLUMN_MAILBOX_KEY = 7;
public static final int WIDGET_COLUMN_SNIPPET = 8;
public static final int WIDGET_COLUMN_ACCOUNT_KEY = 9;
public EmailWidget(int _widgetId) {
super();
if (Email.DEBUG) {
Log.d(TAG, "Creating EmailWidget with id = " + _widgetId);
}
mWidgetId = _widgetId;
mLoader = new WidgetLoader();
if (sDatePaint == null) {
sDatePaint = new TextPaint();
sDatePaint.setTypeface(Typeface.DEFAULT);
sDatePaint.setTextSize(14);
sDatePaint.setAntiAlias(true);
sDatePaint.setTextAlign(Align.RIGHT);
}
}
/**
* The ThrottlingCursorLoader does all of the heavy lifting in managing the data loading
* task; all we need is to register a listener so that we're notified when the load is
* complete.
*/
final class WidgetLoader extends ThrottlingCursorLoader {
protected WidgetLoader() {
super(sContext, Message.CONTENT_URI, WIDGET_PROJECTION, mViewType.selection, null,
SORT_DESCENDING);
registerListener(0, new OnLoadCompleteListener<Cursor>() {
@Override
public void onLoadComplete(Loader<Cursor> loader, Cursor cursor) {
synchronized (mCursorLock) {
// Save away the cursor
mCursor = cursor;
// Reset the notification Uri to our Message table notifier URI
mCursor.setNotificationUri(sResolver, Message.NOTIFIER_URI);
// Save away the count (for display)
mCursorCount = mCursor.getCount();
if (Email.DEBUG) {
Log.d(TAG, "onLoadComplete, count = " + cursor.getCount());
}
}
RemoteViews views =
new RemoteViews(sContext.getPackageName(), R.layout.widget);
views.setTextViewText(R.id.widget_title,
mViewType.getTitle(sContext) + " (" + mCursorCount + ")");
sWidgetManager.partiallyUpdateAppWidget(mWidgetId, views);
sWidgetManager.notifyAppWidgetViewDataChanged(mWidgetId, R.id.message_list);
}
});
startLoading();
}
/**
* Convenience method that stops existing loading (if any), sets a (possibly new)
* selection criterion, and starts loading
*
* @param selection a valid query selection argument
*/
void startLoadingWithSelection(String selection) {
stopLoading();
setSelection(selection);
startLoading();
}
}
/**
* Switch to the next widget view (cycles all -> unread -> starred)
*/
public void switchToNextView() {
switch(mViewType) {
case ALL_MAIL:
mViewType = ViewType.UNREAD;
break;
case UNREAD:
mViewType = ViewType.STARRED;
break;
case STARRED:
mViewType = ViewType.ALL_MAIL;
break;
}
synchronized(mCursorLock) {
mCursorCount = TOTAL_COUNT_UNKNOWN;
invalidateCursorLocked();
mLoader.startLoadingWithSelection(mViewType.selection);
}
}
/**
* Invalidates the current cursor and tells the UI that the underlying data has changed.
* This method must be called while holding mCursorLock
*/
private void invalidateCursorLocked() {
mCursor = null;
sWidgetManager.notifyAppWidgetViewDataChanged(mWidgetId, R.id.message_list);
}
private void setStyleSpan(SpannableString str, int typeface) {
int length = str.length();
str.setSpan(new StyleSpan(typeface), 0, length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
private CharSequence formattedText(String str, int typeface) {
if (str == null) {
return "";
}
SpannableString ss = new SpannableString(str);
setStyleSpan(ss, typeface);
return ss;
}
private CharSequence formattedTextFromCursor(Cursor c, int column, int typeface) {
return formattedText(mCursor.getString(column), typeface);
}
/**
* Convenience method for creating an onClickPendingIntent that executes a command via
* our command Uri. Used for the "next view" command; appends the widget id to the command
* Uri.
*
* @param views The RemoteViews we're inflating
* @param buttonId the id of the button view
* @param data the command Uri
*/
private void setCommandIntent(RemoteViews views, int buttonId, Uri data) {
Intent intent = new Intent(sContext, WidgetService.class);
intent.setDataAndType(ContentUris.withAppendedId(data, mWidgetId),
WIDGET_DATA_MIME_TYPE);
PendingIntent pendingIntent = PendingIntent.getService(sContext, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(buttonId, pendingIntent);
}
/**
* Convenience method for creating an onClickPendingIntent that launches another activity
* directly. Used for the "Compose" button
*
* @param views The RemoteViews we're inflating
* @param buttonId the id of the button view
* @param activityClass the class of the activity to be launched
*/
private void setActivityIntent(RemoteViews views, int buttonId,
Class<? extends Activity> activityClass) {
Intent intent = new Intent(sContext, activityClass);
PendingIntent pendingIntent = PendingIntent.getActivity(sContext, 0, intent, 0);
views.setOnClickPendingIntent(buttonId, pendingIntent);
}
/**
* Convenience method for constructing a fillInIntent for a given list view element.
* Appends the command and any arguments to a base Uri.
*
* @param views the RemoteViews we are inflating
* @param viewId the id of the view
* @param baseUri the base uri for the command
* @param args any arguments to the command
*/
private void setFillInIntent(RemoteViews views, int viewId, Uri baseUri, String ... args) {
Intent intent = new Intent();
Builder builder = baseUri.buildUpon();
for (String arg: args) {
builder.appendPath(arg);
}
intent.setDataAndType(builder.build(), WIDGET_DATA_MIME_TYPE);
views.setOnClickFillInIntent(viewId, intent);
}
/**
* Update the "header" of the widget (i.e. everything that doesn't include the scrolling
* message list)
*/
private void updateHeader() {
if (Email.DEBUG) {
Log.d(TAG, "updateWidget " + mWidgetId);
}
// Get the widget layout
RemoteViews views = new RemoteViews(sContext.getPackageName(), R.layout.widget);
// Set up the list with an adapter
Intent intent = new Intent(sContext, WidgetService.class);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mWidgetId);
views.setRemoteAdapter(R.id.message_list, intent);
// Set up the title (view type + count of messages)
views.setTextViewText(R.id.widget_title,
mViewType.getTitle(sContext) + " (" + mCursorCount + ")");
// Set up "new" button (compose new message) and "next view" button
setActivityIntent(views, R.id.widget_compose, MessageCompose.class);
setCommandIntent(views, R.id.widget_logo, COMMAND_URI_SWITCH_LIST_VIEW);
// Use a bare intent for our template; we need to fill everything in
intent = new Intent(sContext, WidgetService.class);
PendingIntent pendingIntent =
PendingIntent.getService(sContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
views.setPendingIntentTemplate(R.id.message_list, pendingIntent);
// And finally update the widget
sWidgetManager.updateAppWidget(mWidgetId, views);
}
/* (non-Javadoc)
* @see android.widget.RemoteViewsService.RemoteViewsFactory#getViewAt(int)
*/
public RemoteViews getViewAt(int position) {
// Use the cursor to set up the widget
synchronized (mCursorLock) {
if (mCursor == null || !mCursor.moveToPosition(position)) {
return getLoadingView();
}
RemoteViews views =
new RemoteViews(sContext.getPackageName(), R.layout.widget_list_item);
// Typeface for from, subject, and date (normal/bold) depends on whether the message
// is read/unread
int typeface = (mCursor.getInt(WIDGET_COLUMN_FLAG_READ) == 0) ? Typeface.BOLD
: Typeface.NORMAL;
views.setTextViewText(R.id.widget_from,
formattedTextFromCursor(mCursor, WIDGET_COLUMN_DISPLAY_NAME, typeface));
views.setTextViewText(R.id.widget_subject,
formattedTextFromCursor(mCursor, WIDGET_COLUMN_SUBJECT, typeface));
long timestamp = mCursor.getLong(WIDGET_COLUMN_TIMESTAMP);
// Get a nicely formatted date string (relative to today)
String date = DateUtils.getRelativeTimeSpanString(sContext, timestamp).toString();
views.setTextViewText(R.id.widget_date, TextUtils.ellipsize(date, sDatePaint, 64,
TruncateAt.END));
// Set button intents for view, reply, and delete
String messageId = mCursor.getString(WIDGET_COLUMN_ID);
String mailboxId = mCursor.getString(WIDGET_COLUMN_MAILBOX_KEY);
setFillInIntent(views, R.id.widget_message, COMMAND_URI_VIEW_MESSAGE, messageId,
mailboxId);
return views;
}
}
@Override
public int getCount() {
if (mCursor == null) return 0;
return Math.min(mCursor.getCount(), MAX_MESSAGE_LIST_COUNT);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public RemoteViews getLoadingView() {
RemoteViews view = new RemoteViews(sContext.getPackageName(), R.layout.widget_loading);
view.setTextViewText(R.id.loading_text, sContext.getString(R.string.widget_loading));
return view;
}
@Override
public int getViewTypeCount() {
// Regular list view and the "loading" view
return 2;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public void onDataSetChanged() {
}
@Override
public void onDestroy() {
if (mLoader != null) {
mLoader.stopLoading();
}
sWidgetMap.remove(mWidgetId);
}
@Override
public void onCreate() {
}
}
private static synchronized void update(Context context, AppWidgetManager widgetManager,
int[] appWidgetIds) {
for (int widgetId: appWidgetIds) {
getOrCreateWidget(widgetId).updateHeader();
}
}
private static EmailWidget getOrCreateWidget(int widgetId) {
EmailWidget widget = sWidgetMap.get(widgetId);
if (widget == null) {
if (Email.DEBUG) {
Log.d(TAG, "Creating EmailWidget for id #" + widgetId);
}
widget = new EmailWidget(widgetId);
sWidgetMap.put(widgetId, widget);
}
return widget;
}
@Override
public void onDisabled(Context context) {
super.onDisabled(context);
if (Email.DEBUG) {
Log.d(TAG, "onDisabled");
}
context.stopService(new Intent(context, WidgetService.class));
}
@Override
public void onEnabled(final Context context) {
super.onEnabled(context);
if (Email.DEBUG) {
Log.d(TAG, "onEnabled");
}
context.startService(new Intent(context, WidgetService.class));
}
@Override
public void onReceive(final Context context, Intent intent) {
String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null) {
final int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
if (appWidgetIds != null && appWidgetIds.length > 0) {
if (sWidgetManager == null) {
sWidgetManager = AppWidgetManager.getInstance(context);
sContext = context.getApplicationContext();
sResolver = sContext.getContentResolver();
}
context.startService(new Intent(context, WidgetService.class));
update(sContext, sWidgetManager, appWidgetIds);
}
}
} else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
final int widgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
// Find the widget in the map
EmailWidget widget = sWidgetMap.get(widgetId);
if (widget != null) {
// Stop loading and remove the widget from the map
widget.onDestroy();
}
}
}
}
/**
* We use the WidgetService for two purposes:
* 1) To provide a widget factory for RemoteViews, and
* 2) To process our command Uri's (i.e. take actions on user clicks)
*/
public static class WidgetService extends RemoteViewsService {
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
// Which widget do we want (nice alliteration, huh?)
int widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
if (widgetId == -1) return null;
// Find the existing widget or create it
EmailWidget widget = sWidgetMap.get(widgetId);
if (widget == null) {
throw new IllegalStateException("onGetViewFactory, widget does not exist");
}
return widget;
}
@Override
public void startActivity(Intent intent) {
// Since we're not calling startActivity from an Activity, we need the new task flag
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
super.startActivity(intent);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Uri data = intent.getData();
if (Email.DEBUG) {
Log.d(TAG, "Executing: " + data);
}
if (data == null) return Service.START_NOT_STICKY;
List<String> pathSegments = data.getPathSegments();
// Our path segments are <command>, <arg1> [, <arg2>]
// First, a quick check of Uri validity
if (pathSegments.size() < 2) {
throw new IllegalArgumentException();
}
String command = pathSegments.get(0);
// Ignore unknown action names
try {
long arg1 = Long.parseLong(pathSegments.get(1));
if (COMMAND_NAME_VIEW_MESSAGE.equals(command)) {
// "view", <message id>, <mailbox id>
Intent i = MessageView.getActionViewIntent(this, arg1,
Long.parseLong(pathSegments.get(2)));
startActivity(i);
} else if (COMMAND_NAME_SWITCH_LIST_VIEW.equals(command)) {
// "next_view", <widget id>
EmailWidget widget = sWidgetMap.get((int)arg1);
if (widget != null) {
widget.switchToNextView();
}
}
} catch (NumberFormatException e) {
// Shouldn't happen as we construct all of the Uri's
}
return Service.START_NOT_STICKY;
}
}
}