Merge Email1 into MR1

Change-Id: I45289d46b65faffc7a3a3dd46382899162f3aaab
This commit is contained in:
Paul Westbrook 2012-09-24 15:15:24 -07:00
parent 335ba2c4ea
commit bc47398187
762 changed files with 73554 additions and 23226 deletions

View File

@ -14,42 +14,38 @@
LOCAL_PATH := $(call my-dir) LOCAL_PATH := $(call my-dir)
# Build the Email application itself, along with its tests and tests for the emailcommon # Build the Email application itself, along with its tests and the tests for the emailcommon
# static library. All tests can be run via runtest email # static library. All tests can be run via runtest email
include $(CLEAR_VARS) include $(CLEAR_VARS)
# Include res dir from chips
# Include res dir from chips, unified, and photoviewer
chips_dir := ../../../frameworks/ex/chips/res chips_dir := ../../../frameworks/ex/chips/res
unified_email_dir := ../UnifiedEmail mail_common_dir := ../../../frameworks/opt/mailcommon/res
photo_dir := ../../../frameworks/ex/photoviewer/res res_dir := $(chips_dir) $(mail_common_dir) res
res_dir := $(chips_dir) res $(unified_email_dir)/res $(photo_dir) build/res
LOCAL_MODULE_TAGS := optional LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, $(unified_email_dir)/src) LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/email)
LOCAL_SRC_FILES += $(call all-java-files-under, src/com/android)
LOCAL_SRC_FILES += $(call all-java-files-under, src/com/beetstra) LOCAL_SRC_FILES += $(call all-java-files-under, src/com/beetstra)
LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dir)) LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dir))
# Use assets dir from UnifiedEmail
# (the default package target doesn't seem to deal with multiple asset dirs)
LOCAL_ASSET_DIR := $(LOCAL_PATH)/$(unified_email_dir)/assets
LOCAL_AAPT_FLAGS := --auto-add-overlay LOCAL_AAPT_FLAGS := --auto-add-overlay
LOCAL_AAPT_FLAGS += --extra-packages com.android.ex.chips:com.android.mail:com.android.email:com.android.ex.photo LOCAL_AAPT_FLAGS += --extra-packages com.android.ex.chips
LOCAL_STATIC_JAVA_LIBRARIES := android-common com.android.emailcommon2 com.android.emailsync guava android-common-chips android-common-photoviewer LOCAL_STATIC_JAVA_LIBRARIES := android-common com.android.emailcommon guava android-common-chips
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v13
LOCAL_PACKAGE_NAME := Email2 LOCAL_PACKAGE_NAME := Email
LOCAL_OVERRIDES_PACKAGES := Email
LOCAL_PROGUARD_FLAG_FILES := proguard.flags $(unified_email_dir)/proguard.flags LOCAL_PROGUARD_FLAG_FILES := proguard.flags
LOCAL_SDK_VERSION := current # TODO change this to "17" once the MR1 sdk version is set to 17 LOCAL_SDK_VERSION := 16
# The Emma tool analyzes code coverage when running unit tests on the
# application. This configuration line selects which packages will be analyzed,
# leaving out code which is tested by other means (e.g. static libraries) that
# would dilute the coverage results. These options do not affect regular
# production builds.
LOCAL_EMMA_COVERAGE_FILTER := +com.android.emailcommon.*,+com.android.email.*, \
+org.apache.james.mime4j.*,+com.beetstra.jutf7.*,+org.apache.commons.io.*
include $(BUILD_PACKAGE) include $(BUILD_PACKAGE)

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!-- Copyright (C) 2008 The Android Open Source Project
Copyright (C) 2012 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -14,35 +13,56 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.email"
android:versionCode="500014"
android:versionName="4.2-014" >
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <manifest
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> xmlns:android="http://schemas.android.com/apk/res/android"
<uses-permission android:name="android.permission.INTERNET"/> package="com.android.email"
<uses-permission android:name="android.permission.VIBRATE"/> android:versionCode="410000"
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> android:versionName="4.1"
<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.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<uses-permission android:name="android.permission.READ_CALENDAR"/>
<uses-permission android:name="android.permission.WRITE_CALENDAR"/>
<uses-permission android:name="android.permission.READ_PROFILE"/>
<uses-permission android:name="android.permission.NFC"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<!-- This needs to be present when we are doing unbundled releases. --> <!-- This needs to be present when we are doing unbundled releases. -->
<uses-sdk android:targetSdkVersion="17" android:minSdkVersion="14" /> <uses-sdk android:targetSdkVersion="16" android:minSdkVersion="14" />
<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.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.WRITE_CONTACTS"/>
<uses-permission
android:name="android.permission.READ_CONTACTS"/>
<uses-permission
android:name="android.permission.READ_PROFILE"/>
<uses-permission
android:name="android.permission.NFC"/>
<!-- 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"/>
<!-- Grant permission to other apps to view attachments --> <!-- Grant permission to other apps to view attachments -->
<permission <permission
@ -58,27 +78,160 @@
android:name="android.permission.USE_CREDENTIALS"/> android:name="android.permission.USE_CREDENTIALS"/>
<!-- Grant permission to system apps to access provider (see provider below) --> <!-- Grant permission to system apps to access provider (see provider below) -->
<!-- STOPSHIP: Temporarily set protection level to "dangerous" (from "signature") -->
<permission <permission
android:name="com.android.email.permission.ACCESS_PROVIDER" android:name="com.android.email.permission.ACCESS_PROVIDER"
android:protectionLevel="dangerous" android:protectionLevel="signature"
android:label="@string/permission_access_provider_label" android:label="@string/permission_access_provider_label"
android:description="@string/permission_access_provider_desc"/> android:description="@string/permission_access_provider_desc"/>
<uses-permission <uses-permission
android:name="com.android.email.permission.ACCESS_PROVIDER"/> android:name="com.android.email.permission.ACCESS_PROVIDER"/>
<!-- Note: Actually, android:hardwareAccelerated could be "true", but in order to switch it
on/off in the debug screen, we have to set it "false" here and enable it at runtime. -->
<application <application
android:icon="@mipmap/ic_launcher_mail" android:icon="@mipmap/ic_launcher_email"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/UnifiedEmailTheme" android:name="Email"
android:hardwareAccelerated="true" > android:theme="@style/EmailTheme"
<!-- Enable search in all activities --> android:hardwareAccelerated="false"
<meta-data android:name="android.app.default_searchable" >
android:value="com.android.email2.ui.MailActivityEmail" />
<activity <activity
android:name="com.android.mail.compose.ComposeActivity" android:name=".activity.Welcome"
android:label="@string/app_name" >
android:theme="@android:style/Theme.Holo.Light"> <intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.APP_EMAIL" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
<data
android:scheme="content"
android:host="ui.email.android.com"
android:path="/view/mailbox"
/>
</intent-filter>
</activity>
<!-- Must be exported in order for the AccountManager to launch it -->
<!-- Also available for continuous test systems to force account creation -->
<activity
android:name=".activity.setup.AccountSetupBasics"
android:label="@string/account_setup_basics_title"
android:exported="true"
>
<intent-filter>
<action
android:name="com.android.email.CREATE_ACCOUNT" />
<category
android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".activity.setup.AccountSetupAccountType"
android:label="@string/account_setup_account_type_title"
>
</activity>
<activity
android:name=".activity.setup.AccountSetupIncoming"
android:label="@string/account_setup_incoming_title"
>
</activity>
<activity
android:name=".activity.setup.AccountSetupOutgoing"
android:label="@string/account_setup_outgoing_title"
>
</activity>
<activity
android:name=".activity.setup.AccountSetupExchange"
android:label="@string/account_setup_exchange_title"
>
</activity>
<activity
android:name=".activity.setup.AccountSetupOptions"
android:label="@string/account_setup_options_title"
>
</activity>
<activity
android:name=".activity.setup.AccountSetupNames"
android:label="@string/account_setup_names_title"
>
</activity>
<activity
android:name=".activity.setup.AccountSettings"
android:label="@string/settings_activity_title"
android:theme="@android:style/Theme.Holo.Light"
>
<intent-filter>
<action
android:name="com.android.email.activity.setup.ACCOUNT_MANAGER_ENTRY" />
<category
android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.EDIT" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:scheme="content"
android:host="ui.email.android.com"
android:path="/settings"
/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".activity.setup.AccountSecurity"
android.label="@string/account_security_title"
>
</activity>
<!-- Don't need to set the title; it will be set programatically -->
<activity
android:name=".activity.ShortcutPicker"
android:enabled="false"
android:theme="@android:style/Theme.Holo.Light.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" />
</intent-filter>
</activity>
<activity
android:name=".activity.EmailActivity"
android:uiOptions="splitActionBarWhenNarrow"
>
</activity>
<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>
</activity>
<activity
android:name=".activity.MessageCompose"
android:label="@string/compose_title"
android:enabled="false"
android:theme="@android:style/Theme.Holo.Light"
>
<intent-filter> <intent-filter>
<action <action
android:name="android.intent.action.VIEW" /> android:name="android.intent.action.VIEW" />
@ -113,189 +266,15 @@
<action <action
android:name="com.android.email.intent.action.REPLY" /> android:name="com.android.email.intent.action.REPLY" />
</intent-filter> </intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category
android:name="android.intent.category.DEFAULT" />
<data
android:scheme="content"
android:host="ui.email2.android.com"
android:pathPrefix="/compose"
/>
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="mailto" />
</intent-filter>
</activity> </activity>
<!-- Only used to support pre-HC shortcuts -->
<activity <activity
android:name=".activity.EventViewer" android:name=".activity.MessageList"
android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light"
> >
<intent-filter> <intent-filter>
<action android:name="android.intent.action.VIEW" />
<category
android:name="android.intent.category.DEFAULT" />
<data
android:scheme="content"
android:host="ui.email2.android.com"
android:pathPrefix="/event"
/>
</intent-filter>
</activity>
<!-- TODO: this activity doesn't exist. Determine what to do here -->
<activity android:name=".ui.CreateShortcutActivity"
android:label="@string/activity_folder_selection" />
<activity android:name="com.android.mail.ui.FolderSelectionActivity"
android:label="@string/activity_folder_selection" />
<activity android:name="com.android.email2.ui.MailboxSelectionActivityEmail"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
<activity android:name="com.android.mail.ui.ShortcutNameActivity"
android:label="@string/shortcut_name_title"
android:theme="@style/ShortcutWidgetTheme">
</activity>
<activity android:name="com.android.mail.ui.MailboxSelectionActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
<activity android:name="com.android.email2.ui.CreateShortcutActivityEmail"
android:theme="@style/ShortcutWidgetTheme"
android:label="@string/activity_folder_selection">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:label="@string/app_name"
android:name="com.android.email2.ui.MailActivityEmail"
android:theme="@style/PlainUnifiedEmailTheme"
android:uiOptions="splitActionBarWhenNarrow">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content"/>
<data android:mimeType="@string/application_mime_type" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="android.app.searchable" android:resource="@xml/searchable" />
</activity>
<activity-alias android:name="com.android.email.activity.Welcome"
android:targetActivity="com.android.email2.ui.MailActivityEmail"
android:label="@string/app_name" >
<intent-filter >
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.APP_EMAIL" />
</intent-filter> </intent-filter>
</activity-alias>
<!-- Must be exported in order for the AccountManager to launch it -->
<!-- Also available for continuous test systems to force account creation -->
<activity
android:name=".activity.setup.AccountSetupBasics"
android:label="@string/account_setup_basics_title"
android:exported="true"
>
<intent-filter>
<action
android:name="com.android.email.CREATE_ACCOUNT" />
<category
android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".activity.setup.AccountSetupType"
android:label="@string/account_setup_account_type_title"
>
</activity>
<activity
android:name=".activity.setup.AccountSetupIncoming"
android:label="@string/account_setup_incoming_title"
>
</activity>
<activity
android:name=".activity.setup.AccountSetupOutgoing"
android:label="@string/account_setup_outgoing_title"
>
</activity>
<activity
android:name=".activity.setup.AccountSetupOptions"
android:label="@string/account_setup_options_title"
>
</activity>
<activity
android:name=".activity.setup.AccountSetupNames"
android:label="@string/account_setup_names_title"
>
</activity>
<activity
android:name=".activity.setup.AccountSettings"
android:label="@string/settings_activity_title"
android:theme="@android:style/Theme.Holo.Light"
>
<intent-filter>
<action
android:name="com.android.email.activity.setup.ACCOUNT_MANAGER_ENTRY" />
<category
android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.EDIT" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:scheme="content"
android:host="ui.email.android.com"
android:pathPrefix="/settings"
/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".provider.FolderPickerActivity"
android:label="@string/folder_picker_title"
android:theme="@android:style/Theme.Holo.Light"
>
<intent-filter>
<action android:name="android.intent.action.EDIT" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:scheme="content"
android:host="ui.email.android.com"
android:pathPrefix="/setup"
/>
</intent-filter>
</activity>
<activity
android:name=".activity.setup.AccountSecurity"
android.label="@string/account_security_title"
>
</activity> </activity>
<activity <activity
@ -304,68 +283,6 @@
> >
</activity> </activity>
<activity
android:name="com.android.mail.photo.MailPhotoViewActivity"
android:label="@string/app_name"
android:theme="@style/PhotoViewTheme" >
</activity>
<provider
android:authorities="com.android.email2.conversation.provider"
android:label="@string/conversation_content_provider"
android:multiprocess="false"
android:exported="true"
android:name="com.android.mail.browse.EmailConversationProvider" >
<grant-uri-permission android:pathPattern=".*" />
</provider>
<provider
android:authorities="com.android.email2.accountcache"
android:label="@string/account_cache_provider"
android:multiprocess="false"
android:exported="true"
android:name="com.android.mail.providers.EmailAccountCacheProvider" >
<grant-uri-permission android:pathPattern=".*" />
</provider>
<!-- The android:name is the name of the Provider class which is stored in
UnifiedEmail, and has package name com.android.mail.providers and the class is
called SuggestionsProvider. The authority name is specified in the MailAppProvider
which is specific to the two apps separately. -->
<provider android:name="com.android.mail.providers.SuggestionsProvider"
android:authorities="com.android.email.suggestionsprovider"
android:exported="true" />
<receiver android:name="com.android.mail.providers.protos.boot.AccountReceiver">
<intent-filter>
<action android:name="com.android.email2.providers.protos.boot.intent.ACTION_PROVIDER_CREATED" />
<action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED" />
</intent-filter>
</receiver>
<service android:name="com.android.mail.compose.EmptyService"/>
<!-- Widget -->
<receiver android:name=".provider.WidgetProvider" android:label="@string/app_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<intent-filter>
<action android:name="com.android.mail.ACTION_NOTIFY_DATASET_CHANGED" />
<data android:mimeType="@string/application_mime_type" />
</intent-filter>
<intent-filter>
<action android:name="com.android.mail.ACTION_UPDATE_WIDGET" />
<data android:mimeType="@string/application_mime_type" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/widget_info" />
</receiver>
<service android:name="com.android.mail.widget.WidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS"
android:exported="false" />
<!-- From Email application -->
<receiver <receiver
android:name=".service.AttachmentDownloadService$Watchdog" android:name=".service.AttachmentDownloadService$Watchdog"
android:enabled="true"/> android:enabled="true"/>
@ -374,16 +291,14 @@
android:name=".service.EmailBroadcastReceiver" android:name=".service.EmailBroadcastReceiver"
android:enabled="true"> android:enabled="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action
<action android:name="android.intent.action.DEVICE_STORAGE_LOW" /> android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.DEVICE_STORAGE_OK" /> <action
<action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED" /> android:name="android.intent.action.DEVICE_STORAGE_LOW" />
</intent-filter> <action
<!-- To handle new message notifications --> android:name="android.intent.action.DEVICE_STORAGE_OK" />
<intent-filter> <action
<action android:name="com.android.mail.action.update_notification" android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED" />
android:priority="-10" />
<data android:mimeType="@string/application_mime_type" />
</intent-filter> </intent-filter>
<!-- To handle secret code to activate the debug screen. --> <!-- To handle secret code to activate the debug screen. -->
<intent-filter> <intent-filter>
@ -415,6 +330,12 @@
<service <service
android:name=".service.MailService" android:name=".service.MailService"
android:enabled="false"
>
</service>
<service
android:name=".Controller$ControllerService"
android:enabled="true" android:enabled="true"
> >
</service> </service>
@ -427,7 +348,7 @@
<!--Required stanza to register the PopImapAuthenticatorService with AccountManager --> <!--Required stanza to register the PopImapAuthenticatorService with AccountManager -->
<service <service
android:name=".service.Pop3AuthenticatorService" android:name=".service.PopImapAuthenticatorService"
android:exported="true" android:exported="true"
android:enabled="true" android:enabled="true"
> >
@ -437,46 +358,20 @@
</intent-filter> </intent-filter>
<meta-data <meta-data
android:name="android.accounts.AccountAuthenticator" android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator_pop3" android:resource="@xml/pop_imap_authenticator"
/>
</service>
<!--Required stanza to register the PopImapAuthenticatorService with AccountManager -->
<service
android:name=".service.ImapAuthenticatorService"
android:exported="true"
android:enabled="true"
>
<intent-filter>
<action
android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator_imap"
/> />
</service> </service>
<!--Required stanza to register the PopImapSyncAdapterService with SyncManager --> <!--Required stanza to register the PopImapSyncAdapterService with SyncManager -->
<service <service
android:name="com.android.email.service.Pop3SyncAdapterService" android:name="com.android.email.service.PopImapSyncAdapterService"
android:exported="true"> android:exported="true">
<intent-filter> <intent-filter>
<action <action
android:name="android.content.SyncAdapter" /> android:name="android.content.SyncAdapter" />
</intent-filter> </intent-filter>
<meta-data android:name="android.content.SyncAdapter" <meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter_pop3" /> android:resource="@xml/syncadapter_pop_imap" />
</service>
<service
android:name="com.android.email.service.LegacyImapSyncAdapterService"
android:exported="true">
<intent-filter>
<action
android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter_legacy_imap" />
</service> </service>
<!-- Require provider permission to use our Policy and Account services --> <!-- Require provider permission to use our Policy and Account services -->
@ -502,28 +397,6 @@
</intent-filter> </intent-filter>
</service> </service>
<service
android:name=".service.ImapService"
android:enabled="true"
android:permission="com.android.email.permission.ACCESS_PROVIDER"
>
<intent-filter>
<action
android:name="com.android.email.IMAP_INTENT" />
</intent-filter>
</service>
<service
android:name=".service.Pop3Service"
android:enabled="true"
android:permission="com.android.email.permission.ACCESS_PROVIDER"
>
<intent-filter>
<action
android:name="com.android.email.POP3_INTENT" />
</intent-filter>
</service>
<!--Required stanza to register the EasAuthenticatorService with AccountManager --> <!--Required stanza to register the EasAuthenticatorService with AccountManager -->
<service <service
android:name=".service.EasAuthenticatorService" android:name=".service.EasAuthenticatorService"
@ -536,7 +409,7 @@
</intent-filter> </intent-filter>
<meta-data <meta-data
android:name="android.accounts.AccountAuthenticator" android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator_eas" android:resource="@xml/eas_authenticator"
/> />
</service> </service>
<!--Required stanza to register the EasTestAuthenticatorService with AccountManager --> <!--Required stanza to register the EasTestAuthenticatorService with AccountManager -->
@ -572,49 +445,12 @@
android:resource="@xml/authenticator_alternate" android:resource="@xml/authenticator_alternate"
/> />
</service> </service>
<service
android:name=".service.ImapAuthenticatorService"
android:exported="true"
android:enabled="true"
>
<intent-filter>
<action
android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator_imap"
/>
</service>
<service
android:name=".service.LegacyImapAuthenticatorService"
android:exported="false"
android:enabled="true"
>
<intent-filter>
<action
android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator_legacy_imap"
/>
</service>
<service
android:name=".imap2.Imap2SyncManager"
android:enabled="true"
android:permission="com.android.email.permission.ACCESS_PROVIDER"
>
</service>
<provider <provider
android:name=".provider.AttachmentProvider" android:name=".provider.AttachmentProvider"
android:authorities="com.android.email.attachmentprovider" android:authorities="com.android.email.attachmentprovider"
android:multiprocess="true" android:multiprocess="true"
android:grantUriPermissions="true" android:grantUriPermissions="true"
android:exported="true"
android:readPermission="com.android.email.permission.READ_ATTACHMENT" android:readPermission="com.android.email.permission.READ_ATTACHMENT"
/> />
@ -624,45 +460,41 @@
android:name=".provider.EmailProvider" android:name=".provider.EmailProvider"
android:authorities="com.android.email.provider;com.android.email.notifier" android:authorities="com.android.email.provider;com.android.email.notifier"
android:multiprocess="true" android:multiprocess="true"
android:exported="true"
android:permission="com.android.email.permission.ACCESS_PROVIDER" android:permission="com.android.email.permission.ACCESS_PROVIDER"
android:label="@string/app_name" android:label="@string/app_name"
/> />
<!-- Legacy authenticators, etc. can be added below. OEMs may remove these --> <!-- Email AppWidget definitions -->
<activity
<service android:name=".widget.WidgetConfiguration"
android:name=".service.LegacyEmailAuthenticatorService"
android:exported="false"
android:enabled="true" android:enabled="true"
android:theme="@android:style/Theme.Holo.Light.DialogWhenLarge"
> >
<intent-filter
android:label="@string/account_shortcut_picker_name">
<action
android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/>
<category
android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<service
android:name=".provider.WidgetProvider$WidgetService"
android:permission="android.permission.BIND_REMOTEVIEWS"
android:exported="false"
/>
<receiver
android:name=".provider.WidgetProvider" >
<intent-filter> <intent-filter>
<action <action
android:name="android.accounts.AccountAuthenticator" /> android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter> </intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator_legacy_email"
/>
</service>
<service
android:name=".service.LegacyEasAuthenticatorService"
android:exported="false"
android:enabled="true"
>
<intent-filter> <intent-filter>
<action <action android:name="com.android.email.MESSAGE_LIST_DATASET_CHANGED" />
android:name="android.accounts.AccountAuthenticator" />
</intent-filter> </intent-filter>
<meta-data <meta-data
android:name="android.accounts.AccountAuthenticator" android:name="android.appwidget.provider"
android:resource="@xml/authenticator_legacy_eas" android:resource="@xml/widget_info" />
/> </receiver>
</service>
</application> </application>
<!-- Legacy permissions, etc. can go here -->
</manifest> </manifest>

View File

@ -43,8 +43,37 @@
#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/APPS/Email*)
$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/APPS/Email*)
$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/APPS/Exchange*)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/JAVA_LIBRARIES/com.android.emailcommon*)
$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/classes/com/android/emailcommon*)
$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/EmailGoogle_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/EmailGoogle_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/EmailGoogle_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email2_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/EmailGoogle_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email2_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/EmailGoogle_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email2_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/EmailGoogle_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email2_intermediates)
$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email*)
$(call add-clean-step, rm -rf $(OUT_DIR)/out/target/common/obj/JAVA_LIBRARIES/com.android.emailcommon*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email*)
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.email*) # ************************************************
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email*) # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/com.android.email*) # ************************************************
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Email*)

View File

@ -1,31 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 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.
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- DO NOT TRANSLATE THESE STRINGS -->
<string name="account_manager_type_exchange" translatable="false">com.android.exchange</string>
<string name="account_manager_type_pop3" translatable="false">com.android.email</string>
<string name="account_manager_type_imap" translatable="false">com.android.email</string>
<string name="account_manager_type_legacy_imap" translatable="false">com.android.email</string>
<string name="intent_exchange" translatable="false">com.android.email.EXCHANGE_INTENT</string>
<string name="intent_account_manager_entry" translatable="false">com.android.email.ACCOUNT_MANAGER_ENTRY_INTENT</string>
<string name="authority_email_provider" translatable="false">com.android.email.provider</string>
<string name="protocol_legacy_imap" translatable="false">imap</string>
<string name="protocol_imap" translatable="false">imap</string>
<string name="protocol_pop3" translatable="false">pop3</string>
<string name="protocol_eas" translatable="false">eas</string>
<string name="application_mime_type" translatable="false">application/email-ls</string>
</resources>

View File

@ -1,108 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2012 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.
-->
<!--
Email services (protocols) are defined here. For the present, these are baked into the Email
apk; the goal is for remote services to register themselves into this file.
The required attributes are as follows (except that EITHER serviceClass or intent is required):
protocol: the unique name used to identify the protocol
name: the name of the account type option presented to users during account setup
accountType: the AccountManager type of accounts created using this service
serviceClass: a class implementing IEmailService (or null, if the service is remote)
intent: the intent used to connect to a remote IEmailService
port: the (default) port used when creating accounts using this service
portSsl: as above, when SSL is selected
syncIntervalStrings: a reference to an array of sync interval options
syncIntervals: a reference to an array of values corresponding to syncIntervalStrings
defaultSyncInterval: the default sync interval, selected from enums defined in attrs.xml
The following optional attributes default to "false":
offerTls: whether a TLS option (e.g. STARTTLS) is offered for this service
offerCerts: whether or not certificate authentication is an option for this service
usesSmtp: whether SMTP is used as the outgoing protocol for this service
offerPrefix: whether a "prefix" is offered to the user (for IMAP)
offerLocalDeletes: whether an option to delete locally is offered
syncChanges: whether non-deletion changes to messages sync back to the server
offerAttachmentPreload: whether to offer attachment preloading (pre-caching)
usesAutodiscover: whether to attempt using the "autodiscover" API when creating
an account
offerLookback: whether a sync "lookback" is offered (rather than the POP/IMAP
legacy "25 most recent messages synced")
defaultLookback: if "lookback" is offered, an enum of possible lookbacks
syncCalendar: whether this service is capable of syncing a calendar (offering a checkbox)
syncContacts: whether this service is capable of syncing contacts (offering a checkbox)
-->
<emailservices xmlns:email="http://schemas.android.com/apk/res/com.android.email">
<emailservice
email:protocol="pop3"
email:name="@string/pop3_name"
email:accountType="@string/account_manager_type_pop3"
email:serviceClass="com.android.email.service.Pop3Service"
email:port="110"
email:portSsl="995"
email:syncIntervalStrings="@array/account_settings_check_frequency_entries"
email:syncIntervals="@array/account_settings_check_frequency_values"
email:defaultSyncInterval="mins15"
email:offerTls="true"
email:usesSmtp="true"
email:offerLocalDeletes="true"
email:inferPrefix="pop"
email:offerLoadMore="true"
/>
<emailservice
email:protocol="imap"
email:name="@string/imap_name"
email:accountType="@string/account_manager_type_imap"
email:serviceClass="com.android.email.service.ImapService"
email:port="143"
email:portSsl="993"
email:syncIntervalStrings="@array/account_settings_check_frequency_entries"
email:syncIntervals="@array/account_settings_check_frequency_values"
email:defaultSyncInterval="mins15"
email:offerTls="true"
email:usesSmtp="true"
email:offerAttachmentPreload="true"
email:offerPrefix="true"
email:syncChanges="true"
email:inferPrefix="imap"
email:offerLoadMore="true"
/>
<emailservice
email:protocol="@string/protocol_eas"
email:name="Exchange"
email:accountType="@string/account_manager_type_exchange"
email:intent="com.android.email.EXCHANGE_INTENT"
email:port="80"
email:portSsl="443"
email:syncIntervalStrings="@array/account_settings_check_frequency_entries_push"
email:syncIntervals="@array/account_settings_check_frequency_values_push"
email:defaultSyncInterval="push"
email:defaultSsl="true"
email:offerCerts="true"
email:syncChanges="true"
email:usesAutodiscover="true"
email:offerAttachmentPreload="true"
email:offerLookback="true"
email:defaultLookback="auto"
email:syncContacts="true"
email:syncCalendar="true"
/>
</emailservices>

View File

@ -20,24 +20,24 @@ LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS) include $(CLEAR_VARS)
unified_email_src_dir := ../../UnifiedEmail/src unified_email_src_dir := ../src
apache_src_dir := ../../UnifiedEmail/src/org apache_src_dir := ../src/org
imported_unified_email_files := \ imported_unified_email_files := \
$(unified_email_src_dir)/com/android/mail/utils/LogUtils.java \ $(unified_email_src_dir)/com/android/mail/utils/LogUtils.java \
$(unified_email_src_dir)/com/android/mail/utils/LoggingInputStream.java \
$(unified_email_src_dir)/com/android/mail/providers/UIProvider.java $(unified_email_src_dir)/com/android/mail/providers/UIProvider.java
LOCAL_MODULE := com.android.emailcommon2 LOCAL_MODULE := com.android.emailcommon
LOCAL_STATIC_JAVA_LIBRARIES := guava android-common LOCAL_STATIC_JAVA_LIBRARIES := guava android-common
LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/emailcommon) LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SRC_FILES += \
src/com/android/emailcommon/service/IEmailService.aidl \
src/com/android/emailcommon/service/IEmailServiceCallback.aidl \
src/com/android/emailcommon/service/IPolicyService.aidl \
src/com/android/emailcommon/service/IAccountService.aidl
LOCAL_SRC_FILES += $(call all-java-files-under, $(apache_src_dir)) LOCAL_SRC_FILES += $(call all-java-files-under, $(apache_src_dir))
LOCAL_SRC_FILES += src/com/android/emailcommon/service/IEmailService.aidl
LOCAL_SRC_FILES += src/com/android/emailcommon/service/IAccountService.aidl
LOCAL_SRC_FILES += src/com/android/emailcommon/service/IPolicyService.aidl
LOCAL_SRC_FILES += src/com/android/emailcommon/service/IEmailServiceCallback.aidl
LOCAL_SRC_FILES += $(imported_unified_email_files) LOCAL_SRC_FILES += $(imported_unified_email_files)
LOCAL_SDK_VERSION := 14 LOCAL_SDK_VERSION := current
include $(BUILD_STATIC_JAVA_LIBRARY) include $(BUILD_STATIC_JAVA_LIBRARY)

View File

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2010 The Android Open Source Project /*
* Copyright (C) 2011 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,10 +15,9 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.email.service; package com.android.emailcommon;
/** public class AccountManagerTypes {
* This service needs to be declared separately from the base service public static final String TYPE_EXCHANGE = "com.android.exchange";
*/ public static final String TYPE_POP_IMAP = "com.android.email";
public class LegacyImapAuthenticatorService extends AuthenticatorService {
} }

View File

@ -21,9 +21,8 @@ package com.android.emailcommon;
* *
* Level 1: As shipped in HC/MR1 * Level 1: As shipped in HC/MR1
* Level 2: Adds searchMessages to EmailService * Level 2: Adds searchMessages to EmailService
* Level 3: Adds capabilities query
* *
*/ */
public class Api { public class Api {
public static final int LEVEL = 3; public static final int LEVEL = 2;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2010 The Android Open Source Project * Copyright (C) 2011 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -14,10 +14,12 @@
* limitations under the License. * limitations under the License.
*/ */
package com.android.email.service; package com.android.emailcommon;
/** /**
* This service needs to be declared separately from the base service * This is the only non-SDK reference in the com.android.email project, referencing the once and
* future CalendarProvider authority name.
*/ */
public class ImapAuthenticatorService extends AuthenticatorService { public class CalendarProviderStub {
public static final String AUTHORITY = "com.android.calendar";
} }

View File

@ -19,17 +19,29 @@ package com.android.emailcommon;
import android.content.Context; import android.content.Context;
import com.android.emailcommon.provider.Account; import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.HostAuth;
import com.android.emailcommon.utility.Utility;
/** /**
* Constants for tagging threads for traffic stats, and associated utilities * Constants for tagging threads for traffic stats, and associated utilities
* *
* Example usage: * Example usage:
* TrafficStats.setThreadStatsTag(accountId | DATA_EMAIL | REASON_SYNC); * TrafficStats.setThreadStatsTag(accountId | PROTOCOL_IMAP | DATA_EMAIL | REASON_SYNC);
*/ */
public class TrafficFlags { public class TrafficFlags {
// Bits 0->15, account id // Bits 0->15, account id
private static final int ACCOUNT_MASK = 0x0000FFFF; private static final int ACCOUNT_MASK = 0x0000FFFF;
// Bits 16&17, protocol (0 = POP3)
private static final int PROTOCOL_SHIFT = 16;
private static final int PROTOCOL_MASK = 3 << PROTOCOL_SHIFT;
public static final int PROTOCOL_POP3 = 0 << PROTOCOL_SHIFT;
public static final int PROTOCOL_IMAP = 1 << PROTOCOL_SHIFT;
public static final int PROTOCOL_EAS = 2 << PROTOCOL_SHIFT;
public static final int PROTOCOL_SMTP = 3 << PROTOCOL_SHIFT;
private static final String[] PROTOCOLS = new String[] {HostAuth.SCHEME_POP3,
HostAuth.SCHEME_IMAP, HostAuth.SCHEME_EAS, HostAuth.SCHEME_SMTP};
// Bits 18&19, type (0 = EMAIL) // Bits 18&19, type (0 = EMAIL)
private static final int DATA_SHIFT = 18; private static final int DATA_SHIFT = 18;
private static final int DATA_MASK = 3 << DATA_SHIFT; private static final int DATA_MASK = 3 << DATA_SHIFT;
@ -56,7 +68,8 @@ public class TrafficFlags {
* @return flags for syncing this account * @return flags for syncing this account
*/ */
public static int getSyncFlags(Context context, Account account) { public static int getSyncFlags(Context context, Account account) {
return (int)account.mId | REASON_SYNC; int protocolIndex = Utility.arrayIndex(PROTOCOLS, account.getProtocol(context));
return (int)account.mId | REASON_SYNC | (protocolIndex << PROTOCOL_SHIFT);
} }
/** /**
@ -67,7 +80,8 @@ public class TrafficFlags {
* @return flags for loading an attachment in this account * @return flags for loading an attachment in this account
*/ */
public static int getAttachmentFlags(Context context, Account account) { public static int getAttachmentFlags(Context context, Account account) {
return (int)account.mId | REASON_ATTACHMENT_USER; int protocolIndex = Utility.arrayIndex(PROTOCOLS, account.getProtocol(context));
return (int)account.mId | REASON_ATTACHMENT_USER | (protocolIndex << PROTOCOL_SHIFT);
} }
/** /**
@ -78,7 +92,7 @@ public class TrafficFlags {
* @return flags for sending SMTP email from this account * @return flags for sending SMTP email from this account
*/ */
public static int getSmtpFlags(Context context, Account account) { public static int getSmtpFlags(Context context, Account account) {
return (int)account.mId | REASON_SYNC; return (int)account.mId | REASON_SYNC | PROTOCOL_SMTP;
} }
public static String toString(int flags) { public static String toString(int flags) {
@ -87,6 +101,8 @@ public class TrafficFlags {
sb.append(flags & ACCOUNT_MASK); sb.append(flags & ACCOUNT_MASK);
sb.append(','); sb.append(',');
sb.append(REASONS[(flags & REASON_MASK) >> REASON_SHIFT]); sb.append(REASONS[(flags & REASON_MASK) >> REASON_SHIFT]);
sb.append(',');
sb.append(PROTOCOLS[(flags & PROTOCOL_MASK) >> PROTOCOL_SHIFT]);
int maskedData = flags & DATA_MASK; int maskedData = flags & DATA_MASK;
if (maskedData != 0) { if (maskedData != 0) {
sb.append(','); sb.append(',');

View File

@ -67,7 +67,6 @@ public class MimeMessage extends Message {
private Body mBody; private Body mBody;
protected int mSize; protected int mSize;
private boolean mInhibitLocalMessageId = false; private boolean mInhibitLocalMessageId = false;
private boolean mComplete = true;
// Shared random source for generating local message-id values // Shared random source for generating local message-id values
private static final java.util.Random sRandom = new java.util.Random(); private static final java.util.Random sRandom = new java.util.Random();
@ -120,7 +119,7 @@ public class MimeMessage extends Message {
parse(in); parse(in);
} }
private MimeStreamParser init() { protected void parse(InputStream in) throws IOException, MessagingException {
// Before parsing the input stream, clear all local fields that may be superceded by // Before parsing the input stream, clear all local fields that may be superceded by
// the new incoming message. // the new incoming message.
getMimeHeaders().clear(); getMimeHeaders().clear();
@ -135,20 +134,7 @@ public class MimeMessage extends Message {
MimeStreamParser parser = new MimeStreamParser(); MimeStreamParser parser = new MimeStreamParser();
parser.setContentHandler(new MimeMessageBuilder()); parser.setContentHandler(new MimeMessageBuilder());
return parser;
}
protected void parse(InputStream in) throws IOException, MessagingException {
MimeStreamParser parser = init();
parser.parse(new EOLConvertingInputStream(in)); parser.parse(new EOLConvertingInputStream(in));
mComplete = !parser.getPrematureEof();
}
public void parse(InputStream in, EOLConvertingInputStream.Callback callback)
throws IOException, MessagingException {
MimeStreamParser parser = init();
parser.parse(new EOLConvertingInputStream(in, getSize(), callback));
mComplete = !parser.getPrematureEof();
} }
/** /**
@ -216,10 +202,6 @@ public class MimeMessage extends Message {
} }
} }
public boolean isComplete() {
return mComplete;
}
public String getMimeType() throws MessagingException { public String getMimeType() throws MessagingException {
return MimeUtility.getHeaderParameter(getContentType(), null); return MimeUtility.getHeaderParameter(getContentType(), null);
} }

View File

@ -16,7 +16,6 @@
package com.android.emailcommon.internet; package com.android.emailcommon.internet;
import android.text.TextUtils;
import android.util.Base64; import android.util.Base64;
import android.util.Base64DataException; import android.util.Base64DataException;
import android.util.Base64InputStream; import android.util.Base64InputStream;
@ -407,12 +406,33 @@ public class MimeUtility {
public static void collectParts(Part part, ArrayList<Part> viewables, public static void collectParts(Part part, ArrayList<Part> viewables,
ArrayList<Part> attachments) throws MessagingException { ArrayList<Part> attachments) throws MessagingException {
String disposition = part.getDisposition(); String disposition = part.getDisposition();
String dispositionType = MimeUtility.getHeaderParameter(disposition, null); String dispositionType = null;
String dispositionFilename = null;
if (disposition != null) {
dispositionType = MimeUtility.getHeaderParameter(disposition, null);
dispositionFilename = MimeUtility.getHeaderParameter(disposition, "filename");
}
// An attachment filename can be defined in either the Content-Disposition header
// or the Content-Type header. Content-Disposition is preferred, so we only try
// the Content-Type header as a last resort.
if (dispositionFilename == null) {
String contentType = part.getContentType();
dispositionFilename = MimeUtility.getHeaderParameter(contentType, "name");
}
boolean attachmentDisposition = "attachment".equalsIgnoreCase(dispositionType);
// If a disposition is not specified, default to "inline" // If a disposition is not specified, default to "inline"
boolean inline = boolean inlineDisposition = dispositionType == null
TextUtils.isEmpty(dispositionType) || "inline".equalsIgnoreCase(dispositionType); || "inline".equalsIgnoreCase(dispositionType);
// The lower-case mime type
String mimeType = part.getMimeType().toLowerCase(); // A guess that this part is intended to be an attachment
boolean attachment = attachmentDisposition
|| (dispositionFilename != null && !inlineDisposition);
// A guess that this part is intended to be an inline.
boolean inline = inlineDisposition && (dispositionFilename != null);
// One or the other
boolean attachmentOrInline = attachment || inline;
if (part.getBody() instanceof Multipart) { if (part.getBody() instanceof Multipart) {
// If the part is Multipart but not alternative it's either mixed or // If the part is Multipart but not alternative it's either mixed or
@ -442,11 +462,14 @@ public class MimeUtility {
// it, pulling any viewables or attachments into the running list. // it, pulling any viewables or attachments into the running list.
Message message = (Message)part.getBody(); Message message = (Message)part.getBody();
collectParts(message, viewables, attachments); collectParts(message, viewables, attachments);
} else if (inline && (mimeType.startsWith("text") || (mimeType.startsWith("image")))) { } else if ((!attachmentOrInline) && ("text/html".equalsIgnoreCase(part.getMimeType()))) {
// We'll treat text and images as viewables // If the part is HTML and we got this far, it's a viewable part of a mixed
viewables.add(part); viewables.add(part);
} else { } else if ((!attachmentOrInline) && ("text/plain".equalsIgnoreCase(part.getMimeType()))) {
// Everything else is an attachment. // If the part is text and we got this far, it's a viewable part of a mixed
viewables.add(part);
} else if (attachmentOrInline) {
// Finally, if it's an attachment or an inline we will include it as an attachment.
attachments.add(part); attachments.add(part);
} }
} }

View File

@ -16,8 +16,12 @@
package com.android.emailcommon.internet; package com.android.emailcommon.internet;
import android.content.ContentUris;
import android.content.Context; import android.content.Context;
import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.text.Html;
import android.text.TextUtils;
import android.util.Base64; import android.util.Base64;
import android.util.Base64OutputStream; import android.util.Base64OutputStream;
@ -38,9 +42,7 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.Writer; import java.io.Writer;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -50,17 +52,28 @@ import java.util.regex.Pattern;
*/ */
public class Rfc822Output { public class Rfc822Output {
private static final Pattern PATTERN_START_OF_LINE = Pattern.compile("(?m)^");
private static final Pattern PATTERN_ENDLINE_CRLF = Pattern.compile("\r\n");
// In MIME, en_US-like date format should be used. In other words "MMM" should be encoded to // In MIME, en_US-like date format should be used. In other words "MMM" should be encoded to
// "Jan", not the other localized format like "Ene" (meaning January in locale es). // "Jan", not the other localized format like "Ene" (meaning January in locale es).
private static final SimpleDateFormat DATE_FORMAT = private static final SimpleDateFormat DATE_FORMAT =
new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US); new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z", Locale.US);
private static final String WHERE_NOT_SMART_FORWARD = "(" + Attachment.FLAGS + "&" +
Attachment.FLAG_SMART_FORWARD + ")=0";
/** A less-than-perfect pattern to pull out <body> content */ /** A less-than-perfect pattern to pull out <body> content */
private static final Pattern BODY_PATTERN = Pattern.compile( private static final Pattern BODY_PATTERN = Pattern.compile(
"(?:<\\s*body[^>]*>)(.*)(?:<\\s*/\\s*body\\s*>)", "(?:<\\s*body[^>]*>)(.*)(?:<\\s*/\\s*body\\s*>)",
Pattern.CASE_INSENSITIVE | Pattern.DOTALL); Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
/** Match group in {@code BODDY_PATTERN} for the body HTML */ /** Match group in {@code BODDY_PATTERN} for the body HTML */
private static final int BODY_PATTERN_GROUP = 1; private static final int BODY_PATTERN_GROUP = 1;
/** Pattern to find both dos and unix newlines */
private static final Pattern NEWLINE_PATTERN =
Pattern.compile("\\r?\\n");
/** HTML string to use when replacing text newlines */
private static final String NEWLINE_HTML = "<br>";
/** Index of the plain text version of the message body */ /** Index of the plain text version of the message body */
private final static int INDEX_BODY_TEXT = 0; private final static int INDEX_BODY_TEXT = 0;
/** Index of the HTML version of the message body */ /** Index of the HTML version of the message body */
@ -82,20 +95,78 @@ public class Rfc822Output {
} }
} }
/**
* Returns an HTML encoded message alternate
*/
/*package*/ static String getHtmlAlternate(Body body, boolean useSmartReply) {
if (body.mHtmlReply == null) {
return null;
}
StringBuffer altMessage = new StringBuffer();
String htmlContent = TextUtils.htmlEncode(body.mTextContent); // Escape HTML reserved chars
htmlContent = NEWLINE_PATTERN.matcher(htmlContent).replaceAll(NEWLINE_HTML);
altMessage.append(htmlContent);
if (body.mIntroText != null) {
String htmlIntro = TextUtils.htmlEncode(body.mIntroText);
htmlIntro = NEWLINE_PATTERN.matcher(htmlIntro).replaceAll(NEWLINE_HTML);
altMessage.append(htmlIntro);
}
if (!useSmartReply) {
String htmlBody = getHtmlBody(body.mHtmlReply);
altMessage.append(htmlBody);
}
return altMessage.toString();
}
/** /**
* Gets both the plain text and HTML versions of the message body. * Gets both the plain text and HTML versions of the message body.
*/ */
/*package*/ static String[] buildBodyText(Body body, int flags, boolean useSmartReply) { /*package*/ static String[] buildBodyText(Body body, int flags, boolean useSmartReply) {
String[] messageBody = new String[] { null, null };
if (body == null) { if (body == null) {
return new String[2]; return messageBody;
} }
String[] messageBody = new String[] { body.mTextContent, body.mHtmlContent }; String text = body.mTextContent;
if (useSmartReply && body.mQuotedTextStartPos > 0) { boolean isReply = (flags & Message.FLAG_TYPE_REPLY) != 0;
if (messageBody[0] != null) { boolean isForward = (flags & Message.FLAG_TYPE_FORWARD) != 0;
messageBody[0] = messageBody[0].substring(0, body.mQuotedTextStartPos); // For all forwards/replies, we add the intro text
} else if (messageBody[1] != null) { if (isReply || isForward) {
messageBody[1] = messageBody[1].substring(0, body.mQuotedTextStartPos); String intro = body.mIntroText == null ? "" : body.mIntroText;
text += intro;
}
if (useSmartReply) {
// useSmartReply is set to true for use by SmartReply/SmartForward in EAS.
// SmartForward doesn't put a break between the original and new text, so we add an LF
if (isForward) {
text += "\n";
} }
} else {
String quotedText = body.mTextReply;
// If there is no plain-text body, use de-tagified HTML as the text body
if (quotedText == null && body.mHtmlReply != null) {
quotedText = Html.fromHtml(body.mHtmlReply).toString();
}
if (quotedText != null) {
// fix CR-LF line endings to LF-only needed by EditText.
Matcher matcher = PATTERN_ENDLINE_CRLF.matcher(quotedText);
quotedText = matcher.replaceAll("\n");
}
if (isReply) {
if (quotedText != null) {
Matcher matcher = PATTERN_START_OF_LINE.matcher(quotedText);
text += matcher.replaceAll(">");
}
} else if (isForward) {
if (quotedText != null) {
text += quotedText;
}
}
}
messageBody[INDEX_BODY_TEXT] = text;
// Exchange 2003 doesn't seem to support multipart w/SmartReply and SmartForward, so
// we'll skip this. Really, it would only matter if we could compose HTML replies
if (!useSmartReply) {
messageBody[INDEX_BODY_HTML] = getHtmlAlternate(body, useSmartReply);
} }
return messageBody; return messageBody;
} }
@ -108,16 +179,9 @@ public class Rfc822Output {
* @param messageId the message to write out * @param messageId the message to write out
* @param out the output stream to write the message to * @param out the output stream to write the message to
* @param useSmartReply whether or not quoted text is appended to a reply/forward * @param useSmartReply whether or not quoted text is appended to a reply/forward
* @param a list of attachments to send (or null if retrieved from the message itself)
*/ */
public static void writeTo(Context context, long messageId, OutputStream out, public static void writeTo(Context context, long messageId, OutputStream out,
boolean useSmartReply, boolean sendBcc) throws IOException, MessagingException { boolean useSmartReply, boolean sendBcc) throws IOException, MessagingException {
writeTo(context, messageId, out, useSmartReply, sendBcc, null);
}
public static void writeTo(Context context, long messageId, OutputStream out,
boolean useSmartReply, boolean sendBcc, List<Attachment> attachments)
throws IOException, MessagingException {
Message message = Message.restoreMessageWithId(context, messageId); Message message = Message.restoreMessageWithId(context, messageId);
if (message == null) { if (message == null) {
// throw something? // throw something?
@ -152,53 +216,59 @@ public class Rfc822Output {
Body body = Body.restoreBodyWithMessageId(context, message.mId); Body body = Body.restoreBodyWithMessageId(context, message.mId);
String[] bodyText = buildBodyText(body, message.mFlags, useSmartReply); String[] bodyText = buildBodyText(body, message.mFlags, useSmartReply);
// If a list of attachments hasn't been passed in, build one from the message Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, messageId);
if (attachments == null) { Cursor attachmentsCursor = context.getContentResolver().query(uri,
attachments = Attachment.CONTENT_PROJECTION, WHERE_NOT_SMART_FORWARD, null, null);
Arrays.asList(Attachment.restoreAttachmentsWithMessageId(context, messageId));
}
boolean multipart = attachments.size() > 0; try {
String multipartBoundary = null; int attachmentCount = attachmentsCursor.getCount();
String multipartType = "mixed"; boolean multipart = attachmentCount > 0;
String multipartBoundary = null;
String multipartType = "mixed";
// Simplified case for no multipart - just emit text and be done. // Simplified case for no multipart - just emit text and be done.
if (!multipart) { if (!multipart) {
writeTextWithHeaders(writer, stream, bodyText);
} else {
// continue with multipart headers, then into multipart body
multipartBoundary = getNextBoundary();
// Move to the first attachment; this must succeed because multipart is true
if (attachments.size() == 1) {
// If we've got one attachment and it's an ics "attachment", we want to send
// this as multipart/alternative instead of multipart/mixed
int flags = attachments.get(0).mFlags;
if ((flags & Attachment.FLAG_ICS_ALTERNATIVE_PART) != 0) {
multipartType = "alternative";
}
}
writeHeader(writer, "Content-Type",
"multipart/" + multipartType + "; boundary=\"" + multipartBoundary + "\"");
// Finish headers and prepare for body section(s)
writer.write("\r\n");
// first multipart element is the body
if (bodyText[INDEX_BODY_TEXT] != null || bodyText[INDEX_BODY_HTML] != null) {
writeBoundary(writer, multipartBoundary, false);
writeTextWithHeaders(writer, stream, bodyText); writeTextWithHeaders(writer, stream, bodyText);
} } else {
// continue with multipart headers, then into multipart body
multipartBoundary = getNextBoundary();
// Write out the attachments until we run out // Move to the first attachment; this must succeed because multipart is true
for (Attachment att: attachments) { attachmentsCursor.moveToFirst();
writeBoundary(writer, multipartBoundary, false); if (attachmentCount == 1) {
writeOneAttachment(context, writer, stream, att); // If we've got one attachment and it's an ics "attachment", we want to send
// this as multipart/alternative instead of multipart/mixed
int flags = attachmentsCursor.getInt(Attachment.CONTENT_FLAGS_COLUMN);
if ((flags & Attachment.FLAG_ICS_ALTERNATIVE_PART) != 0) {
multipartType = "alternative";
}
}
writeHeader(writer, "Content-Type",
"multipart/" + multipartType + "; boundary=\"" + multipartBoundary + "\"");
// Finish headers and prepare for body section(s)
writer.write("\r\n"); writer.write("\r\n");
}
// end of multipart section // first multipart element is the body
writeBoundary(writer, multipartBoundary, true); if (bodyText[INDEX_BODY_TEXT] != null) {
writeBoundary(writer, multipartBoundary, false);
writeTextWithHeaders(writer, stream, bodyText);
}
// Write out the attachments until we run out
do {
writeBoundary(writer, multipartBoundary, false);
Attachment attachment =
Attachment.getContent(attachmentsCursor, Attachment.class);
writeOneAttachment(context, writer, stream, attachment);
writer.write("\r\n");
} while (attachmentsCursor.moveToNext());
// end of multipart section
writeBoundary(writer, multipartBoundary, true);
}
} finally {
attachmentsCursor.close();
} }
writer.flush(); writer.flush();
@ -234,7 +304,7 @@ public class Rfc822Output {
inStream = new ByteArrayInputStream(attachment.mContentBytes); inStream = new ByteArrayInputStream(attachment.mContentBytes);
} else { } else {
// try to open the file // try to open the file
Uri fileUri = Uri.parse(attachment.getContentUri()); Uri fileUri = Uri.parse(attachment.mContentUri);
inStream = context.getContentResolver().openInputStream(fileUri); inStream = context.getContentResolver().openInputStream(fileUri);
} }
// switch to output stream for base64 text output // switch to output stream for base64 text output
@ -328,7 +398,9 @@ public class Rfc822Output {
} }
/** /**
* Write the body text. * Write the body text. If only one version of the body is specified (either plain text
* or HTML), the text is written directly. Otherwise, the plain text and HTML bodies
* are both written with the appropriate headers.
* *
* Note this always uses base64, even when not required. Slightly less efficient for * Note this always uses base64, even when not required. Slightly less efficient for
* US-ASCII text, but handles all formats even when non-ascii chars are involved. A small * US-ASCII text, but handles all formats even when non-ascii chars are involved. A small
@ -340,23 +412,49 @@ public class Rfc822Output {
*/ */
private static void writeTextWithHeaders(Writer writer, OutputStream out, String[] bodyText) private static void writeTextWithHeaders(Writer writer, OutputStream out, String[] bodyText)
throws IOException { throws IOException {
boolean html = false;
String text = bodyText[INDEX_BODY_TEXT]; String text = bodyText[INDEX_BODY_TEXT];
if (text == null) { String html = bodyText[INDEX_BODY_HTML];
text = bodyText[INDEX_BODY_HTML];
html = true;
}
if (text == null) { if (text == null) {
writer.write("\r\n"); // a truly empty message writer.write("\r\n"); // a truly empty message
} else { } else {
String multipartBoundary = null;
boolean multipart = html != null;
// Simplified case for no multipart - just emit text and be done.
if (multipart) {
// continue with multipart headers, then into multipart body
multipartBoundary = getNextBoundary();
writeHeader(writer, "Content-Type",
"multipart/alternative; boundary=\"" + multipartBoundary + "\"");
// Finish headers and prepare for body section(s)
writer.write("\r\n");
writeBoundary(writer, multipartBoundary, false);
}
// first multipart element is the body // first multipart element is the body
String mimeType = "text/" + (html ? "html" : "plain"); writeHeader(writer, "Content-Type", "text/plain; charset=utf-8");
writeHeader(writer, "Content-Type", mimeType + "; charset=utf-8");
writeHeader(writer, "Content-Transfer-Encoding", "base64"); writeHeader(writer, "Content-Transfer-Encoding", "base64");
writer.write("\r\n"); writer.write("\r\n");
byte[] textBytes = text.getBytes("UTF-8"); byte[] textBytes = text.getBytes("UTF-8");
writer.flush(); writer.flush();
out.write(Base64.encode(textBytes, Base64.CRLF)); out.write(Base64.encode(textBytes, Base64.CRLF));
if (multipart) {
// next multipart section
writeBoundary(writer, multipartBoundary, false);
writeHeader(writer, "Content-Type", "text/html; charset=utf-8");
writeHeader(writer, "Content-Transfer-Encoding", "base64");
writer.write("\r\n");
byte[] htmlBytes = html.getBytes("UTF-8");
writer.flush();
out.write(Base64.encode(htmlBytes, Base64.CRLF));
// end of multipart section
writeBoundary(writer, multipartBoundary, true);
}
} }
} }

View File

@ -1,18 +0,0 @@
/* Copyright (C) 2012 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.emailcommon.provider;
parcelable Account;

View File

@ -40,6 +40,16 @@ import java.util.UUID;
public final class Account extends EmailContent implements AccountColumns, Parcelable { public final class Account extends EmailContent implements AccountColumns, Parcelable {
public static final String TABLE_NAME = "Account"; 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");
public static final Uri RESET_NEW_MESSAGE_COUNT_URI =
Uri.parse(EmailContent.CONTENT_URI + "/resetNewMessageCount");
public static final Uri NOTIFIER_URI =
Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/account");
public static final Uri DEFAULT_ACCOUNT_ID_URI =
Uri.parse(EmailContent.CONTENT_URI + "/account/default");
// Define all pseudo account IDs here to avoid conflict with one another. // Define all pseudo account IDs here to avoid conflict with one another.
/** /**
@ -90,8 +100,6 @@ public final class Account extends EmailContent implements AccountColumns, Parce
// Whether or not server-side search supports global search (i.e. all mailboxes); only valid // Whether or not server-side search supports global search (i.e. all mailboxes); only valid
// if FLAGS_SUPPORTS_SEARCH is true // if FLAGS_SUPPORTS_SEARCH is true
public static final int FLAGS_SUPPORTS_GLOBAL_SEARCH = 1<<12; public static final int FLAGS_SUPPORTS_GLOBAL_SEARCH = 1<<12;
// Whether or not the initial folder list has been loaded
public static final int FLAGS_INITIAL_FOLDER_LIST_LOADED = 1<<13;
// Deletion policy (see FLAGS_DELETE_POLICY_MASK, above) // Deletion policy (see FLAGS_DELETE_POLICY_MASK, above)
public static final int DELETE_POLICY_NEVER = 0; public static final int DELETE_POLICY_NEVER = 0;
@ -102,20 +110,6 @@ public final class Account extends EmailContent implements AccountColumns, Parce
public static final int CHECK_INTERVAL_NEVER = -1; public static final int CHECK_INTERVAL_NEVER = -1;
public static final int CHECK_INTERVAL_PUSH = -2; public static final int CHECK_INTERVAL_PUSH = -2;
public static Uri CONTENT_URI;
public static Uri ADD_TO_FIELD_URI;
public static Uri RESET_NEW_MESSAGE_COUNT_URI;
public static Uri NOTIFIER_URI;
public static Uri DEFAULT_ACCOUNT_ID_URI;
public static void initAccount() {
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/account");
ADD_TO_FIELD_URI = Uri.parse(EmailContent.CONTENT_URI + "/accountIdAddToField");
RESET_NEW_MESSAGE_COUNT_URI = Uri.parse(EmailContent.CONTENT_URI + "/resetNewMessageCount");
NOTIFIER_URI = Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/account");
DEFAULT_ACCOUNT_ID_URI = Uri.parse(EmailContent.CONTENT_URI + "/account/default");
}
public String mDisplayName; public String mDisplayName;
public String mEmailAddress; public String mEmailAddress;
public String mSyncKey; public String mSyncKey;
@ -134,6 +128,10 @@ public final class Account extends EmailContent implements AccountColumns, Parce
public String mSignature; public String mSignature;
public long mPolicyKey; public long mPolicyKey;
// For compatibility with Email1
public long mNotifiedMessageId;
public int mNotifiedMessageCount;
// Convenience for creating/working with an account // Convenience for creating/working with an account
public transient HostAuth mHostAuthRecv; public transient HostAuth mHostAuthRecv;
public transient HostAuth mHostAuthSend; public transient HostAuth mHostAuthSend;
@ -159,6 +157,8 @@ public final class Account extends EmailContent implements AccountColumns, Parce
public static final int CONTENT_SECURITY_SYNC_KEY_COLUMN = 15; public static final int CONTENT_SECURITY_SYNC_KEY_COLUMN = 15;
public static final int CONTENT_SIGNATURE_COLUMN = 16; public static final int CONTENT_SIGNATURE_COLUMN = 16;
public static final int CONTENT_POLICY_KEY = 17; public static final int CONTENT_POLICY_KEY = 17;
public static final int CONTENT_NOTIFIED_MESSAGE_ID_COLUMN = 18;
public static final int CONTENT_NOTIFIED_MESSAGE_COUNT_COLUMN = 19;
public static final String[] CONTENT_PROJECTION = new String[] { public static final String[] CONTENT_PROJECTION = new String[] {
RECORD_ID, AccountColumns.DISPLAY_NAME, RECORD_ID, AccountColumns.DISPLAY_NAME,
@ -168,7 +168,8 @@ public final class Account extends EmailContent implements AccountColumns, Parce
AccountColumns.COMPATIBILITY_UUID, AccountColumns.SENDER_NAME, AccountColumns.COMPATIBILITY_UUID, AccountColumns.SENDER_NAME,
AccountColumns.RINGTONE_URI, AccountColumns.PROTOCOL_VERSION, AccountColumns.RINGTONE_URI, AccountColumns.PROTOCOL_VERSION,
AccountColumns.NEW_MESSAGE_COUNT, AccountColumns.SECURITY_SYNC_KEY, AccountColumns.NEW_MESSAGE_COUNT, AccountColumns.SECURITY_SYNC_KEY,
AccountColumns.SIGNATURE, AccountColumns.POLICY_KEY AccountColumns.SIGNATURE, AccountColumns.POLICY_KEY,
AccountColumns.NOTIFIED_MESSAGE_ID, AccountColumns.NOTIFIED_MESSAGE_COUNT
}; };
public static final int CONTENT_MAILBOX_TYPE_COLUMN = 1; public static final int CONTENT_MAILBOX_TYPE_COLUMN = 1;
@ -200,6 +201,13 @@ public final class Account extends EmailContent implements AccountColumns, Parce
MailboxColumns.TYPE + " = " + Mailbox.TYPE_INBOX + MailboxColumns.TYPE + " = " + Mailbox.TYPE_INBOX +
" AND " + MailboxColumns.ACCOUNT_KEY + " =?"; " AND " + MailboxColumns.ACCOUNT_KEY + " =?";
/**
* This projection is for searching for the default account
*/
private static final String[] DEFAULT_ID_PROJECTION = new String[] {
RECORD_ID, IS_DEFAULT
};
/** /**
* no public constructor since this is a utility class * no public constructor since this is a utility class
*/ */
@ -266,6 +274,8 @@ public final class Account extends EmailContent implements AccountColumns, Parce
mSecuritySyncKey = cursor.getString(CONTENT_SECURITY_SYNC_KEY_COLUMN); mSecuritySyncKey = cursor.getString(CONTENT_SECURITY_SYNC_KEY_COLUMN);
mSignature = cursor.getString(CONTENT_SIGNATURE_COLUMN); mSignature = cursor.getString(CONTENT_SIGNATURE_COLUMN);
mPolicyKey = cursor.getLong(CONTENT_POLICY_KEY); mPolicyKey = cursor.getLong(CONTENT_POLICY_KEY);
mNotifiedMessageId = cursor.getLong(CONTENT_NOTIFIED_MESSAGE_ID_COLUMN);
mNotifiedMessageCount = cursor.getInt(CONTENT_NOTIFIED_MESSAGE_COUNT_COLUMN);
} }
private long getId(Uri u) { private long getId(Uri u) {
@ -453,6 +463,21 @@ public final class Account extends EmailContent implements AccountColumns, Parce
return "local://localhost/" + context.getDatabasePath(getUuid() + ".db"); return "local://localhost/" + context.getDatabasePath(getUuid() + ".db");
} }
/**
* @return true if the instance is of an EAS account.
*
* NOTE This method accesses the DB if {@link #mHostAuthRecv} hasn't been restored yet.
* Use caution when you use this on the main thread.
*/
public boolean isEasAccount(Context context) {
return "eas".equals(getProtocol(context));
}
public boolean supportsMoveMessages(Context context) {
String protocol = getProtocol(context);
return "eas".equals(protocol) || "imap".equals(protocol);
}
/** /**
* @return true if the account supports "search". * @return true if the account supports "search".
*/ */
@ -502,7 +527,7 @@ public final class Account extends EmailContent implements AccountColumns, Parce
public static long getAccountIdFromShortcutSafeUri(Context context, Uri uri) { public static long getAccountIdFromShortcutSafeUri(Context context, Uri uri) {
// Make sure the URI is in the correct format. // Make sure the URI is in the correct format.
if (!"content".equals(uri.getScheme()) if (!"content".equals(uri.getScheme())
|| !EmailContent.AUTHORITY.equals(uri.getAuthority())) { || !AUTHORITY.equals(uri.getAuthority())) {
return -1; return -1;
} }
@ -706,7 +731,7 @@ public final class Account extends EmailContent implements AccountColumns, Parce
.newUpdate(ContentUris.withAppendedId(CONTENT_URI, mId)) .newUpdate(ContentUris.withAppendedId(CONTENT_URI, mId))
.withValues(cv).build()); .withValues(cv).build());
try { try {
context.getContentResolver().applyBatch(EmailContent.AUTHORITY, ops); context.getContentResolver().applyBatch(AUTHORITY, ops);
return 1; return 1;
} catch (RemoteException e) { } catch (RemoteException e) {
// There is nothing to be done here; fail by returning 0 // There is nothing to be done here; fail by returning 0
@ -786,7 +811,7 @@ public final class Account extends EmailContent implements AccountColumns, Parce
try { try {
ContentProviderResult[] results = ContentProviderResult[] results =
context.getContentResolver().applyBatch(EmailContent.AUTHORITY, ops); context.getContentResolver().applyBatch(AUTHORITY, ops);
// If saving, set the mId's of the various saved objects // If saving, set the mId's of the various saved objects
if (recvIndex >= 0) { if (recvIndex >= 0) {
long newId = getId(results[recvIndex].uri); long newId = getId(results[recvIndex].uri);
@ -829,6 +854,8 @@ public final class Account extends EmailContent implements AccountColumns, Parce
values.put(AccountColumns.SECURITY_SYNC_KEY, mSecuritySyncKey); values.put(AccountColumns.SECURITY_SYNC_KEY, mSecuritySyncKey);
values.put(AccountColumns.SIGNATURE, mSignature); values.put(AccountColumns.SIGNATURE, mSignature);
values.put(AccountColumns.POLICY_KEY, mPolicyKey); values.put(AccountColumns.POLICY_KEY, mPolicyKey);
values.put(AccountColumns.NOTIFIED_MESSAGE_ID, mNotifiedMessageId);
values.put(AccountColumns.NOTIFIED_MESSAGE_COUNT, mNotifiedMessageCount);
return values; return values;
} }

View File

@ -29,7 +29,6 @@ import android.os.Environment;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log;
import com.android.emailcommon.utility.TextUtilities; import com.android.emailcommon.utility.TextUtilities;
import com.android.emailcommon.utility.Utility; import com.android.emailcommon.utility.Utility;
@ -60,12 +59,31 @@ import java.util.ArrayList;
* *
*/ */
public abstract class EmailContent { public abstract class EmailContent {
public static final String 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 NOTIFIER_AUTHORITY = "com.android.email.notifier";
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);
public static final Uri MAILBOX_NOTIFICATION_URI =
Uri.parse("content://" + EmailContent.AUTHORITY + "/mailboxNotification");
public static final String[] NOTIFICATION_PROJECTION = public static final String[] NOTIFICATION_PROJECTION =
new String[] {MailboxColumns.ID, MailboxColumns.UNREAD_COUNT, MailboxColumns.MESSAGE_COUNT}; new String[] {MailboxColumns.ID, MailboxColumns.UNREAD_COUNT, MailboxColumns.MESSAGE_COUNT};
public static final int NOTIFICATION_MAILBOX_ID_COLUMN = 0; public static final int NOTIFICATION_MAILBOX_ID_COLUMN = 0;
public static final int NOTIFICATION_MAILBOX_UNREAD_COUNT_COLUMN = 1; public static final int NOTIFICATION_MAILBOX_UNREAD_COUNT_COLUMN = 1;
public static final int NOTIFICATION_MAILBOX_MESSAGE_COUNT_COLUMN = 2; public static final int NOTIFICATION_MAILBOX_MESSAGE_COUNT_COLUMN = 2;
public static final Uri MAILBOX_MOST_RECENT_MESSAGE_URI =
Uri.parse("content://" + EmailContent.AUTHORITY + "/mailboxMostRecentMessage");
public static final String PROVIDER_PERMISSION = "com.android.email.permission.ACCESS_PROVIDER";
// All classes share this // All classes share this
public static final String RECORD_ID = "_id"; public static final String RECORD_ID = "_id";
@ -113,57 +131,6 @@ public abstract class EmailContent {
// Read the Content from a ContentCursor // Read the Content from a ContentCursor
public abstract void restore (Cursor cursor); public abstract void restore (Cursor cursor);
public static String PACKAGE_NAME;
public static String EMAIL_PACKAGE_NAME;
public static String AUTHORITY;
// 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 String NOTIFIER_AUTHORITY;
public static Uri CONTENT_URI;
public static final String PARAMETER_LIMIT = "limit";
public static Uri CONTENT_NOTIFIER_URI;
public static Uri PICK_TRASH_FOLDER_URI;
public static Uri PICK_SENT_FOLDER_URI;
public static Uri MAILBOX_NOTIFICATION_URI;
public static Uri MAILBOX_MOST_RECENT_MESSAGE_URI;
public static Uri ACCOUNT_CHECK_URI;
public static String PROVIDER_PERMISSION;
public static void init(Context context) {
if (AUTHORITY == null) {
PACKAGE_NAME = context.getPackageName();
EMAIL_PACKAGE_NAME = PACKAGE_NAME;
// If our package is com...exchange, the provider is com...email.provider
if (PACKAGE_NAME.endsWith("exchange")) {
int lastDot = EMAIL_PACKAGE_NAME.lastIndexOf('.');
EMAIL_PACKAGE_NAME = PACKAGE_NAME.substring(0, lastDot + 1) + "email";
}
AUTHORITY = EMAIL_PACKAGE_NAME + ".provider";
Log.d("EmailContent", "init for " + AUTHORITY);
NOTIFIER_AUTHORITY = EMAIL_PACKAGE_NAME + ".notifier";
CONTENT_URI = Uri.parse("content://" + AUTHORITY);
CONTENT_NOTIFIER_URI = Uri.parse("content://" + NOTIFIER_AUTHORITY);
PICK_TRASH_FOLDER_URI = Uri.parse("content://" + AUTHORITY + "/pickTrashFolder");
PICK_SENT_FOLDER_URI = Uri.parse("content://" + AUTHORITY + "/pickSentFolder");
MAILBOX_NOTIFICATION_URI = Uri.parse("content://" + AUTHORITY + "/mailboxNotification");
MAILBOX_MOST_RECENT_MESSAGE_URI = Uri.parse("content://" + AUTHORITY +
"/mailboxMostRecentMessage");
ACCOUNT_CHECK_URI = Uri.parse("content://" + AUTHORITY + "/accountCheck");
PROVIDER_PERMISSION = EMAIL_PACKAGE_NAME + ".permission.ACCESS_PROVIDER";
// Initialize subclasses
Account.initAccount();
Mailbox.initMailbox();
QuickResponse.initQuickResponse();
HostAuth.initHostAuth();
Policy.initPolicy();
Message.initMessage();
Body.initBody();
Attachment.initAttachment();
}
}
// The Uri is lazily initialized // The Uri is lazily initialized
public Uri getUri() { public Uri getUri() {
if (mUri == null) { if (mUri == null) {
@ -293,17 +260,14 @@ public abstract class EmailContent {
// The plain text content itself // The plain text content itself
public static final String TEXT_CONTENT = "textContent"; public static final String TEXT_CONTENT = "textContent";
// Replied-to or forwarded body (in html form) // Replied-to or forwarded body (in html form)
@Deprecated
public static final String HTML_REPLY = "htmlReply"; public static final String HTML_REPLY = "htmlReply";
// Replied-to or forwarded body (in text form) // Replied-to or forwarded body (in text form)
@Deprecated
public static final String TEXT_REPLY = "textReply"; public static final String TEXT_REPLY = "textReply";
// A reference to a message's unique id used in reply/forward. // A reference to a message's unique id used in reply/forward.
// Protocol code can be expected to use this column in determining whether a message can be // Protocol code can be expected to use this column in determining whether a message can be
// deleted safely (i.e. isn't referenced by other messages) // deleted safely (i.e. isn't referenced by other messages)
public static final String SOURCE_MESSAGE_KEY = "sourceMessageKey"; public static final String SOURCE_MESSAGE_KEY = "sourceMessageKey";
// The text to be placed between a reply/forward response and the original message // The text to be placed between a reply/forward response and the original message
@Deprecated
public static final String INTRO_TEXT = "introText"; public static final String INTRO_TEXT = "introText";
// The start of quoted text within our text content // The start of quoted text within our text content
public static final String QUOTED_TEXT_START_POS = "quotedTextStartPos"; public static final String QUOTED_TEXT_START_POS = "quotedTextStartPos";
@ -312,22 +276,16 @@ public abstract class EmailContent {
public static final class Body extends EmailContent implements BodyColumns { public static final class Body extends EmailContent implements BodyColumns {
public static final String TABLE_NAME = "Body"; public static final String TABLE_NAME = "Body";
public static Uri CONTENT_URI; @SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/body");
public static void initBody() {
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/body");
}
public static final int CONTENT_ID_COLUMN = 0; public static final int CONTENT_ID_COLUMN = 0;
public static final int CONTENT_MESSAGE_KEY_COLUMN = 1; public static final int CONTENT_MESSAGE_KEY_COLUMN = 1;
public static final int CONTENT_HTML_CONTENT_COLUMN = 2; public static final int CONTENT_HTML_CONTENT_COLUMN = 2;
public static final int CONTENT_TEXT_CONTENT_COLUMN = 3; public static final int CONTENT_TEXT_CONTENT_COLUMN = 3;
@Deprecated
public static final int CONTENT_HTML_REPLY_COLUMN = 4; public static final int CONTENT_HTML_REPLY_COLUMN = 4;
@Deprecated
public static final int CONTENT_TEXT_REPLY_COLUMN = 5; public static final int CONTENT_TEXT_REPLY_COLUMN = 5;
public static final int CONTENT_SOURCE_KEY_COLUMN = 6; public static final int CONTENT_SOURCE_KEY_COLUMN = 6;
@Deprecated
public static final int CONTENT_INTRO_TEXT_COLUMN = 7; public static final int CONTENT_INTRO_TEXT_COLUMN = 7;
public static final int CONTENT_QUOTED_TEXT_START_POS_COLUMN = 8; public static final int CONTENT_QUOTED_TEXT_START_POS_COLUMN = 8;
@ -343,22 +301,19 @@ public abstract class EmailContent {
public static final String[] COMMON_PROJECTION_HTML = new String[] { public static final String[] COMMON_PROJECTION_HTML = new String[] {
RECORD_ID, BodyColumns.HTML_CONTENT RECORD_ID, BodyColumns.HTML_CONTENT
}; };
@Deprecated
public static final String[] COMMON_PROJECTION_REPLY_TEXT = new String[] { public static final String[] COMMON_PROJECTION_REPLY_TEXT = new String[] {
RECORD_ID, BodyColumns.TEXT_REPLY RECORD_ID, BodyColumns.TEXT_REPLY
}; };
@Deprecated
public static final String[] COMMON_PROJECTION_REPLY_HTML = new String[] { public static final String[] COMMON_PROJECTION_REPLY_HTML = new String[] {
RECORD_ID, BodyColumns.HTML_REPLY RECORD_ID, BodyColumns.HTML_REPLY
}; };
@Deprecated
public static final String[] COMMON_PROJECTION_INTRO = new String[] { public static final String[] COMMON_PROJECTION_INTRO = new String[] {
RECORD_ID, BodyColumns.INTRO_TEXT RECORD_ID, BodyColumns.INTRO_TEXT
}; };
public static final String[] COMMON_PROJECTION_SOURCE = new String[] { public static final String[] COMMON_PROJECTION_SOURCE = new String[] {
RECORD_ID, BodyColumns.SOURCE_MESSAGE_KEY RECORD_ID, BodyColumns.SOURCE_MESSAGE_KEY
}; };
public static final int COMMON_PROJECTION_COLUMN_TEXT = 1; public static final int COMMON_PROJECTION_COLUMN_TEXT = 1;
private static final String[] PROJECTION_SOURCE_KEY = private static final String[] PROJECTION_SOURCE_KEY =
new String[] { BodyColumns.SOURCE_MESSAGE_KEY }; new String[] { BodyColumns.SOURCE_MESSAGE_KEY };
@ -366,9 +321,7 @@ public abstract class EmailContent {
public long mMessageKey; public long mMessageKey;
public String mHtmlContent; public String mHtmlContent;
public String mTextContent; public String mTextContent;
@Deprecated
public String mHtmlReply; public String mHtmlReply;
@Deprecated
public String mTextReply; public String mTextReply;
public int mQuotedTextStartPos; public int mQuotedTextStartPos;
@ -378,7 +331,6 @@ public abstract class EmailContent {
* want to include quoted text. * want to include quoted text.
*/ */
public long mSourceKey; public long mSourceKey;
@Deprecated
public String mIntroText; public String mIntroText;
public Body() { public Body() {
@ -493,17 +445,14 @@ public abstract class EmailContent {
return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_HTML); return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_HTML);
} }
@Deprecated
public static String restoreReplyTextWithMessageId(Context context, long messageId) { public static String restoreReplyTextWithMessageId(Context context, long messageId) {
return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_REPLY_TEXT); return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_REPLY_TEXT);
} }
@Deprecated
public static String restoreReplyHtmlWithMessageId(Context context, long messageId) { public static String restoreReplyHtmlWithMessageId(Context context, long messageId) {
return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_REPLY_HTML); return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_REPLY_HTML);
} }
@Deprecated
public static String restoreIntroTextWithMessageId(Context context, long messageId) { public static String restoreIntroTextWithMessageId(Context context, long messageId) {
return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_INTRO); return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_INTRO);
} }
@ -548,8 +497,8 @@ public abstract class EmailContent {
public static final String FLAGS = "flags"; public static final String FLAGS = "flags";
// Sync related identifiers // Sync related identifiers
// Saved draft info (reusing the never-used "clientId" column) // Any client-required identifier
public static final String DRAFT_INFO = "clientId"; public static final String CLIENT_ID = "clientId";
// The message-id in the message's header // The message-id in the message's header
public static final String MESSAGE_ID = "messageId"; public static final String MESSAGE_ID = "messageId";
@ -576,8 +525,6 @@ public abstract class EmailContent {
public static final String PROTOCOL_SEARCH_INFO = "protocolSearchInfo"; public static final String PROTOCOL_SEARCH_INFO = "protocolSearchInfo";
// Simple thread topic // Simple thread topic
public static final String THREAD_TOPIC = "threadTopic"; public static final String THREAD_TOPIC = "threadTopic";
// For sync adapter use
public static final String SYNC_DATA = "syncData";
} }
public static final class Message extends EmailContent implements SyncColumns, MessageColumns { public static final class Message extends EmailContent implements SyncColumns, MessageColumns {
@ -586,28 +533,17 @@ public abstract class EmailContent {
public static final String DELETED_TABLE_NAME = "Message_Deletes"; public static final String DELETED_TABLE_NAME = "Message_Deletes";
// To refer to a specific message, use ContentUris.withAppendedId(CONTENT_URI, id) // To refer to a specific message, use ContentUris.withAppendedId(CONTENT_URI, id)
public static Uri CONTENT_URI; @SuppressWarnings("hiding")
public static Uri CONTENT_URI_LIMIT_1; public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/message");
public static Uri SYNCED_CONTENT_URI; public static final Uri CONTENT_URI_LIMIT_1 = uriWithLimit(CONTENT_URI, 1);
public static Uri SELECTED_MESSAGE_CONTENT_URI ; public static final Uri SYNCED_CONTENT_URI =
public static Uri DELETED_CONTENT_URI; Uri.parse(EmailContent.CONTENT_URI + "/syncedMessage");
public static Uri UPDATED_CONTENT_URI; public static final Uri DELETED_CONTENT_URI =
public static Uri NOTIFIER_URI; Uri.parse(EmailContent.CONTENT_URI + "/deletedMessage");
public static final Uri UPDATED_CONTENT_URI =
public static void initMessage() { Uri.parse(EmailContent.CONTENT_URI + "/updatedMessage");
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/message"); public static final Uri NOTIFIER_URI =
CONTENT_URI_LIMIT_1 = uriWithLimit(CONTENT_URI, 1); Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/message");
SYNCED_CONTENT_URI =
Uri.parse(EmailContent.CONTENT_URI + "/syncedMessage");
SELECTED_MESSAGE_CONTENT_URI =
Uri.parse(EmailContent.CONTENT_URI + "/messageBySelection");
DELETED_CONTENT_URI =
Uri.parse(EmailContent.CONTENT_URI + "/deletedMessage");
UPDATED_CONTENT_URI =
Uri.parse(EmailContent.CONTENT_URI + "/updatedMessage");
NOTIFIER_URI =
Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/message");
}
public static final String KEY_TIMESTAMP_DESC = MessageColumns.TIMESTAMP + " desc"; public static final String KEY_TIMESTAMP_DESC = MessageColumns.TIMESTAMP + " desc";
@ -621,7 +557,7 @@ public abstract class EmailContent {
public static final int CONTENT_FLAG_ATTACHMENT_COLUMN = 7; public static final int CONTENT_FLAG_ATTACHMENT_COLUMN = 7;
public static final int CONTENT_FLAGS_COLUMN = 8; public static final int CONTENT_FLAGS_COLUMN = 8;
public static final int CONTENT_SERVER_ID_COLUMN = 9; public static final int CONTENT_SERVER_ID_COLUMN = 9;
public static final int CONTENT_DRAFT_INFO_COLUMN = 10; public static final int CONTENT_CLIENT_ID_COLUMN = 10;
public static final int CONTENT_MESSAGE_ID_COLUMN = 11; public static final int CONTENT_MESSAGE_ID_COLUMN = 11;
public static final int CONTENT_MAILBOX_KEY_COLUMN = 12; public static final int CONTENT_MAILBOX_KEY_COLUMN = 12;
public static final int CONTENT_ACCOUNT_KEY_COLUMN = 13; public static final int CONTENT_ACCOUNT_KEY_COLUMN = 13;
@ -635,7 +571,6 @@ public abstract class EmailContent {
public static final int CONTENT_SNIPPET_COLUMN = 21; public static final int CONTENT_SNIPPET_COLUMN = 21;
public static final int CONTENT_PROTOCOL_SEARCH_INFO_COLUMN = 22; public static final int CONTENT_PROTOCOL_SEARCH_INFO_COLUMN = 22;
public static final int CONTENT_THREAD_TOPIC_COLUMN = 23; public static final int CONTENT_THREAD_TOPIC_COLUMN = 23;
public static final int CONTENT_SYNC_DATA_COLUMN = 24;
public static final String[] CONTENT_PROJECTION = new String[] { public static final String[] CONTENT_PROJECTION = new String[] {
RECORD_ID, RECORD_ID,
@ -643,14 +578,14 @@ public abstract class EmailContent {
MessageColumns.SUBJECT, MessageColumns.FLAG_READ, MessageColumns.SUBJECT, MessageColumns.FLAG_READ,
MessageColumns.FLAG_LOADED, MessageColumns.FLAG_FAVORITE, MessageColumns.FLAG_LOADED, MessageColumns.FLAG_FAVORITE,
MessageColumns.FLAG_ATTACHMENT, MessageColumns.FLAGS, MessageColumns.FLAG_ATTACHMENT, MessageColumns.FLAGS,
SyncColumns.SERVER_ID, MessageColumns.DRAFT_INFO, SyncColumns.SERVER_ID, MessageColumns.CLIENT_ID,
MessageColumns.MESSAGE_ID, MessageColumns.MAILBOX_KEY, MessageColumns.MESSAGE_ID, MessageColumns.MAILBOX_KEY,
MessageColumns.ACCOUNT_KEY, MessageColumns.FROM_LIST, MessageColumns.ACCOUNT_KEY, MessageColumns.FROM_LIST,
MessageColumns.TO_LIST, MessageColumns.CC_LIST, MessageColumns.TO_LIST, MessageColumns.CC_LIST,
MessageColumns.BCC_LIST, MessageColumns.REPLY_TO_LIST, MessageColumns.BCC_LIST, MessageColumns.REPLY_TO_LIST,
SyncColumns.SERVER_TIMESTAMP, MessageColumns.MEETING_INFO, SyncColumns.SERVER_TIMESTAMP, MessageColumns.MEETING_INFO,
MessageColumns.SNIPPET, MessageColumns.PROTOCOL_SEARCH_INFO, MessageColumns.SNIPPET, MessageColumns.PROTOCOL_SEARCH_INFO,
MessageColumns.THREAD_TOPIC, MessageColumns.SYNC_DATA MessageColumns.THREAD_TOPIC
}; };
public static final int LIST_ID_COLUMN = 0; public static final int LIST_ID_COLUMN = 0;
@ -766,7 +701,7 @@ public abstract class EmailContent {
public String mServerId; public String mServerId;
public long mServerTimeStamp; public long mServerTimeStamp;
public int mDraftInfo; public String mClientId;
public String mMessageId; public String mMessageId;
public long mMailboxKey; public long mMailboxKey;
@ -787,8 +722,6 @@ public abstract class EmailContent {
public String mThreadTopic; public String mThreadTopic;
public String mSyncData;
/** /**
* Base64-encoded representation of the byte array provided by servers for identifying * Base64-encoded representation of the byte array provided by servers for identifying
* messages belonging to the same conversation thread. Currently unsupported and not * messages belonging to the same conversation thread. Currently unsupported and not
@ -817,7 +750,6 @@ public abstract class EmailContent {
public static final int FLAG_LOADED_COMPLETE = 1; public static final int FLAG_LOADED_COMPLETE = 1;
public static final int FLAG_LOADED_PARTIAL = 2; public static final int FLAG_LOADED_PARTIAL = 2;
public static final int FLAG_LOADED_DELETED = 3; public static final int FLAG_LOADED_DELETED = 3;
public static final int FLAG_LOADED_UNKNOWN = 4;
// Bits used in mFlags // Bits used in mFlags
// The following three states are mutually exclusive, and indicate whether the message is an // The following three states are mutually exclusive, and indicate whether the message is an
@ -858,10 +790,6 @@ public abstract class EmailContent {
// compatibility // compatibility
public static final int FLAG_TYPE_REPLY_ALL = 1 << 21; public static final int FLAG_TYPE_REPLY_ALL = 1 << 21;
// Flag used in draftInfo to indicate that the reference message should be appended
public static final int DRAFT_INFO_APPEND_REF_MESSAGE = 1 << 24;
public static final int DRAFT_INFO_QUOTE_POS_MASK = 0xFFFFFF;
/** a pseudo ID for "no message". */ /** a pseudo ID for "no message". */
public static final long NO_MESSAGE = -1L; public static final long NO_MESSAGE = -1L;
@ -882,22 +810,28 @@ public abstract class EmailContent {
values.put(MessageColumns.FLAG_FAVORITE, mFlagFavorite); values.put(MessageColumns.FLAG_FAVORITE, mFlagFavorite);
values.put(MessageColumns.FLAG_ATTACHMENT, mFlagAttachment); values.put(MessageColumns.FLAG_ATTACHMENT, mFlagAttachment);
values.put(MessageColumns.FLAGS, mFlags); values.put(MessageColumns.FLAGS, mFlags);
values.put(SyncColumns.SERVER_ID, mServerId); values.put(SyncColumns.SERVER_ID, mServerId);
values.put(SyncColumns.SERVER_TIMESTAMP, mServerTimeStamp); values.put(SyncColumns.SERVER_TIMESTAMP, mServerTimeStamp);
values.put(MessageColumns.DRAFT_INFO, mDraftInfo); values.put(MessageColumns.CLIENT_ID, mClientId);
values.put(MessageColumns.MESSAGE_ID, mMessageId); values.put(MessageColumns.MESSAGE_ID, mMessageId);
values.put(MessageColumns.MAILBOX_KEY, mMailboxKey); values.put(MessageColumns.MAILBOX_KEY, mMailboxKey);
values.put(MessageColumns.ACCOUNT_KEY, mAccountKey); values.put(MessageColumns.ACCOUNT_KEY, mAccountKey);
values.put(MessageColumns.FROM_LIST, mFrom); values.put(MessageColumns.FROM_LIST, mFrom);
values.put(MessageColumns.TO_LIST, mTo); values.put(MessageColumns.TO_LIST, mTo);
values.put(MessageColumns.CC_LIST, mCc); values.put(MessageColumns.CC_LIST, mCc);
values.put(MessageColumns.BCC_LIST, mBcc); values.put(MessageColumns.BCC_LIST, mBcc);
values.put(MessageColumns.REPLY_TO_LIST, mReplyTo); values.put(MessageColumns.REPLY_TO_LIST, mReplyTo);
values.put(MessageColumns.MEETING_INFO, mMeetingInfo); values.put(MessageColumns.MEETING_INFO, mMeetingInfo);
values.put(MessageColumns.SNIPPET, mSnippet); values.put(MessageColumns.SNIPPET, mSnippet);
values.put(MessageColumns.PROTOCOL_SEARCH_INFO, mProtocolSearchInfo); values.put(MessageColumns.PROTOCOL_SEARCH_INFO, mProtocolSearchInfo);
values.put(MessageColumns.THREAD_TOPIC, mThreadTopic); values.put(MessageColumns.THREAD_TOPIC, mThreadTopic);
values.put(MessageColumns.SYNC_DATA, mSyncData);
return values; return values;
} }
@ -920,7 +854,7 @@ public abstract class EmailContent {
mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN); mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN);
mServerId = cursor.getString(CONTENT_SERVER_ID_COLUMN); mServerId = cursor.getString(CONTENT_SERVER_ID_COLUMN);
mServerTimeStamp = cursor.getLong(CONTENT_SERVER_TIMESTAMP_COLUMN); mServerTimeStamp = cursor.getLong(CONTENT_SERVER_TIMESTAMP_COLUMN);
mDraftInfo = cursor.getInt(CONTENT_DRAFT_INFO_COLUMN); mClientId = cursor.getString(CONTENT_CLIENT_ID_COLUMN);
mMessageId = cursor.getString(CONTENT_MESSAGE_ID_COLUMN); mMessageId = cursor.getString(CONTENT_MESSAGE_ID_COLUMN);
mMailboxKey = cursor.getLong(CONTENT_MAILBOX_KEY_COLUMN); mMailboxKey = cursor.getLong(CONTENT_MAILBOX_KEY_COLUMN);
mAccountKey = cursor.getLong(CONTENT_ACCOUNT_KEY_COLUMN); mAccountKey = cursor.getLong(CONTENT_ACCOUNT_KEY_COLUMN);
@ -933,7 +867,6 @@ public abstract class EmailContent {
mSnippet = cursor.getString(CONTENT_SNIPPET_COLUMN); mSnippet = cursor.getString(CONTENT_SNIPPET_COLUMN);
mProtocolSearchInfo = cursor.getString(CONTENT_PROTOCOL_SEARCH_INFO_COLUMN); mProtocolSearchInfo = cursor.getString(CONTENT_PROTOCOL_SEARCH_INFO_COLUMN);
mThreadTopic = cursor.getString(CONTENT_THREAD_TOPIC_COLUMN); mThreadTopic = cursor.getString(CONTENT_THREAD_TOPIC_COLUMN);
mSyncData = cursor.getString(CONTENT_SYNC_DATA_COLUMN);
} }
public boolean update() { public boolean update() {
@ -1030,31 +963,37 @@ public abstract class EmailContent {
if (mHtml != null) { if (mHtml != null) {
cv.put(Body.HTML_CONTENT, mHtml); cv.put(Body.HTML_CONTENT, mHtml);
} }
if (mTextReply != null) {
cv.put(Body.TEXT_REPLY, mTextReply);
}
if (mHtmlReply != null) {
cv.put(Body.HTML_REPLY, mHtmlReply);
}
if (mSourceKey != 0) { if (mSourceKey != 0) {
cv.put(Body.SOURCE_MESSAGE_KEY, mSourceKey); cv.put(Body.SOURCE_MESSAGE_KEY, mSourceKey);
} }
if (mIntroText != null) {
cv.put(Body.INTRO_TEXT, mIntroText);
}
if (mQuotedTextStartPos != 0) { if (mQuotedTextStartPos != 0) {
cv.put(Body.QUOTED_TEXT_START_POS, mQuotedTextStartPos); cv.put(Body.QUOTED_TEXT_START_POS, mQuotedTextStartPos);
} }
b = ContentProviderOperation.newInsert(Body.CONTENT_URI);
// Put our message id in the Body
if (!isNew) {
cv.put(Body.MESSAGE_KEY, mId);
}
b.withValues(cv);
// We'll need this if we're new // We'll need this if we're new
int messageBackValue = ops.size() - 1; int messageBackValue = ops.size() - 1;
// Only create a body if we've got some data // If we're new, create a back value entry
if (!cv.keySet().isEmpty()) { if (isNew) {
b = ContentProviderOperation.newInsert(Body.CONTENT_URI); ContentValues backValues = new ContentValues();
// Put our message id in the Body backValues.put(Body.MESSAGE_KEY, messageBackValue);
if (!isNew) { b.withValueBackReferences(backValues);
cv.put(Body.MESSAGE_KEY, mId);
}
b.withValues(cv);
// If we're new, create a back value entry
if (isNew) {
ContentValues backValues = new ContentValues();
backValues.put(Body.MESSAGE_KEY, messageBackValue);
b.withValueBackReferences(backValues);
}
// And add the Body operation
ops.add(b.build());
} }
// And add the Body operation
ops.add(b.build());
// Create the attaachments, if any // Create the attaachments, if any
if (mAttachments != null) { if (mAttachments != null) {
@ -1181,30 +1120,17 @@ public abstract class EmailContent {
public static final class Attachment extends EmailContent public static final class Attachment extends EmailContent
implements AttachmentColumns, Parcelable { implements AttachmentColumns, Parcelable {
public static final String TABLE_NAME = "Attachment"; public static final String TABLE_NAME = "Attachment";
public static final String ATTACHMENT_PROVIDER_LEGACY_URI_PREFIX = @SuppressWarnings("hiding")
"content://com.android.email.attachmentprovider"; public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/attachment");
public static Uri CONTENT_URI;
// This must be used with an appended id: ContentUris.withAppendedId(MESSAGE_ID_URI, id) // This must be used with an appended id: ContentUris.withAppendedId(MESSAGE_ID_URI, id)
public static Uri MESSAGE_ID_URI; public static final Uri MESSAGE_ID_URI = Uri.parse(
public static String ATTACHMENT_PROVIDER_URI_PREFIX; EmailContent.CONTENT_URI + "/attachment/message");
public static boolean sUsingLegacyPrefix;
public static void initAttachment() {
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/attachment");
MESSAGE_ID_URI = Uri.parse(
EmailContent.CONTENT_URI + "/attachment/message");
ATTACHMENT_PROVIDER_URI_PREFIX = "content://" + EmailContent.EMAIL_PACKAGE_NAME +
".attachmentprovider";
sUsingLegacyPrefix =
ATTACHMENT_PROVIDER_URI_PREFIX.equals(ATTACHMENT_PROVIDER_LEGACY_URI_PREFIX);
}
public String mFileName; public String mFileName;
public String mMimeType; public String mMimeType;
public long mSize; public long mSize;
public String mContentId; public String mContentId;
private String mContentUri; public String mContentUri;
public long mMessageKey; public long mMessageKey;
public String mLocation; public String mLocation;
public String mEncoding; public String mEncoding;
@ -1280,31 +1206,6 @@ public abstract class EmailContent {
mBaseUri = CONTENT_URI; mBaseUri = CONTENT_URI;
} }
public void setContentUri(String contentUri) {
mContentUri = contentUri;
}
public String getContentUri() {
if (mContentUri == null) return null; //
// If we're not using the legacy prefix and the uri IS, we need to modify it
if (!Attachment.sUsingLegacyPrefix &&
mContentUri.startsWith(Attachment.ATTACHMENT_PROVIDER_LEGACY_URI_PREFIX)) {
// In an upgrade scenario, we may still have legacy attachment Uri's
// Skip past content://
int prefix = mContentUri.indexOf('/', 10);
if (prefix > 0) {
// Create a proper uri string using the actual provider
return ATTACHMENT_PROVIDER_URI_PREFIX + "/" + mContentUri.substring(prefix);
} else {
Log.e("Attachment", "Improper contentUri format: " + mContentUri);
// Belt & suspenders; can't really happen
return mContentUri;
}
} else {
return mContentUri;
}
}
/** /**
* Restore an Attachment from the database, given its unique id * Restore an Attachment from the database, given its unique id
* @param context * @param context
@ -1534,6 +1435,10 @@ public abstract class EmailContent {
public static final String SIGNATURE = "signature"; public static final String SIGNATURE = "signature";
// A foreign key into the Policy table // A foreign key into the Policy table
public static final String POLICY_KEY = "policyKey"; public static final String POLICY_KEY = "policyKey";
// For compatibility w/ Email1
public static final String NOTIFIED_MESSAGE_ID = "notifiedMessageId";
// For compatibility w/ Email1
public static final String NOTIFIED_MESSAGE_COUNT = "notifiedMessageCount";
} }
public interface QuickResponseColumns { public interface QuickResponseColumns {
@ -1591,8 +1496,8 @@ public abstract class EmailContent {
public static final String LAST_NOTIFIED_MESSAGE_COUNT = "lastNotifiedMessageCount"; public static final String LAST_NOTIFIED_MESSAGE_COUNT = "lastNotifiedMessageCount";
// The total number of messages in the remote mailbox // The total number of messages in the remote mailbox
public static final String TOTAL_COUNT = "totalCount"; public static final String TOTAL_COUNT = "totalCount";
// The full hierarchical name of this folder, in the form a/b/c // For compatibility with Email1
public static final String HIERARCHICAL_NAME = "hierarchicalName"; public static final String LAST_SEEN_MESSAGE_KEY = "lastSeenMessageKey";
} }
public interface HostAuthColumns { public interface HostAuthColumns {
@ -1615,8 +1520,6 @@ public abstract class EmailContent {
static final String CLIENT_CERT_ALIAS = "certAlias"; static final String CLIENT_CERT_ALIAS = "certAlias";
// DEPRECATED - Will not be set or stored // DEPRECATED - Will not be set or stored
static final String ACCOUNT_KEY = "accountKey"; static final String ACCOUNT_KEY = "accountKey";
// A blob containing an X509 server certificate
static final String SERVER_CERT = "serverCert";
} }
public interface PolicyColumns { public interface PolicyColumns {

View File

@ -34,15 +34,14 @@ import java.net.URISyntaxException;
public final class HostAuth extends EmailContent implements HostAuthColumns, Parcelable { public final class HostAuth extends EmailContent implements HostAuthColumns, Parcelable {
public static final String TABLE_NAME = "HostAuth"; public static final String TABLE_NAME = "HostAuth";
public static Uri CONTENT_URI; @SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/hostauth");
public static void initHostAuth() { // TODO the three following constants duplicate constants in Store.java; remove those and
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/hostauth"); // just reference these.
} public static final String SCHEME_IMAP = "imap";
public static final String SCHEME_POP3 = "pop3";
// These legacy constants should be used in code created prior to Email2 public static final String SCHEME_EAS = "eas";
public static final String LEGACY_SCHEME_SMTP = "smtp"; public static final String SCHEME_SMTP = "smtp";
public static final String SCHEME_TRUST_ALL_CERTS = "trustallcerts"; public static final String SCHEME_TRUST_ALL_CERTS = "trustallcerts";
public static final int PORT_UNKNOWN = -1; public static final int PORT_UNKNOWN = -1;
@ -63,8 +62,6 @@ public final class HostAuth extends EmailContent implements HostAuthColumns, Par
public String mPassword; public String mPassword;
public String mDomain; public String mDomain;
public String mClientCertAlias = null; public String mClientCertAlias = null;
// NOTE: The server certificate is NEVER automatically retrieved from EmailProvider
public byte[] mServerCert = null;
public static final int CONTENT_ID_COLUMN = 0; public static final int CONTENT_ID_COLUMN = 0;
public static final int CONTENT_PROTOCOL_COLUMN = 1; public static final int CONTENT_PROTOCOL_COLUMN = 1;
@ -272,7 +269,19 @@ public final class HostAuth extends EmailContent implements HostAuthColumns, Par
mPort = port; mPort = port;
if (mPort == PORT_UNKNOWN) { if (mPort == PORT_UNKNOWN) {
boolean useSSL = ((mFlags & FLAG_SSL) != 0); boolean useSSL = ((mFlags & FLAG_SSL) != 0);
if (LEGACY_SCHEME_SMTP.equals(mProtocol)) { // infer port# from protocol + security
// SSL implies a different port - TLS runs in the "regular" port
// NOTE: Although the port should be setup in the various setup screens, this
// block cannot easily be moved because we get process URIs from other sources
// (e.g. for tests, provider templates and account restore) that may or may not
// have a port specified.
if (SCHEME_POP3.equals(mProtocol)) {
mPort = useSSL ? 995 : 110;
} else if (SCHEME_IMAP.equals(mProtocol)) {
mPort = useSSL ? 993 : 143;
} else if (SCHEME_EAS.equals(mProtocol)) {
mPort = useSSL ? 443 : 80;
} else if (SCHEME_SMTP.equals(mProtocol)) {
mPort = useSSL ? 465 : 587; mPort = useSSL ? 465 : 587;
} }
} }
@ -280,6 +289,10 @@ public final class HostAuth extends EmailContent implements HostAuthColumns, Par
mClientCertAlias = clientCertAlias; mClientCertAlias = clientCertAlias;
} }
/** Returns {@code true} if this is an EAS connection; otherwise, {@code false}. */
public boolean isEasConnection() {
return SCHEME_EAS.equals(mProtocol);
}
/** Convenience method to determine if SSL is used. */ /** Convenience method to determine if SSL is used. */
public boolean shouldUseSsl() { public boolean shouldUseSsl() {
@ -355,7 +368,6 @@ public final class HostAuth extends EmailContent implements HostAuthColumns, Par
} }
HostAuth that = (HostAuth)o; HostAuth that = (HostAuth)o;
return mPort == that.mPort return mPort == that.mPort
&& mId == that.mId
&& mFlags == that.mFlags && mFlags == that.mFlags
&& Utility.areStringsEqual(mProtocol, that.mProtocol) && Utility.areStringsEqual(mProtocol, that.mProtocol)
&& Utility.areStringsEqual(mAddress, that.mAddress) && Utility.areStringsEqual(mAddress, that.mAddress)
@ -363,7 +375,6 @@ public final class HostAuth extends EmailContent implements HostAuthColumns, Par
&& Utility.areStringsEqual(mPassword, that.mPassword) && Utility.areStringsEqual(mPassword, that.mPassword)
&& Utility.areStringsEqual(mDomain, that.mDomain) && Utility.areStringsEqual(mDomain, that.mDomain)
&& Utility.areStringsEqual(mClientCertAlias, that.mClientCertAlias); && Utility.areStringsEqual(mClientCertAlias, that.mClientCertAlias);
// We don't care about the server certificate for equals
} }
/** /**

View File

@ -33,17 +33,12 @@ import com.android.emailcommon.utility.Utility;
public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns, Parcelable { public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns, Parcelable {
public static final String TABLE_NAME = "Mailbox"; public static final String TABLE_NAME = "Mailbox";
@SuppressWarnings("hiding")
public static Uri CONTENT_URI; public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/mailbox");
public static Uri ADD_TO_FIELD_URI; public static final Uri ADD_TO_FIELD_URI =
public static Uri FROM_ACCOUNT_AND_TYPE_URI; Uri.parse(EmailContent.CONTENT_URI + "/mailboxIdAddToField");
public static final Uri FROM_ACCOUNT_AND_TYPE_URI =
public static void initMailbox() { Uri.parse(EmailContent.CONTENT_URI + "/mailboxIdFromAccountAndType");
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/mailbox");
ADD_TO_FIELD_URI = Uri.parse(EmailContent.CONTENT_URI + "/mailboxIdAddToField");
FROM_ACCOUNT_AND_TYPE_URI = Uri.parse(EmailContent.CONTENT_URI +
"/mailboxIdFromAccountAndType");
}
public String mDisplayName; public String mDisplayName;
public String mServerId; public String mServerId;
@ -66,7 +61,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
public long mLastNotifiedMessageKey; public long mLastNotifiedMessageKey;
public int mLastNotifiedMessageCount; public int mLastNotifiedMessageCount;
public int mTotalCount; public int mTotalCount;
public String mHierarchicalName; public long mLastSeenMessageKey;
public static final int CONTENT_ID_COLUMN = 0; public static final int CONTENT_ID_COLUMN = 0;
public static final int CONTENT_DISPLAY_NAME_COLUMN = 1; public static final int CONTENT_DISPLAY_NAME_COLUMN = 1;
@ -90,7 +85,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
public static final int CONTENT_LAST_NOTIFIED_MESSAGE_KEY_COLUMN = 19; public static final int CONTENT_LAST_NOTIFIED_MESSAGE_KEY_COLUMN = 19;
public static final int CONTENT_LAST_NOTIFIED_MESSAGE_COUNT_COLUMN = 20; public static final int CONTENT_LAST_NOTIFIED_MESSAGE_COUNT_COLUMN = 20;
public static final int CONTENT_TOTAL_COUNT_COLUMN = 21; public static final int CONTENT_TOTAL_COUNT_COLUMN = 21;
public static final int CONTENT_HIERARCHICAL_NAME_COLUMN = 22; public static final int CONTENT_LAST_SEEN_MESSAGE_KEY_COLUMN = 22;
/** /**
* <em>NOTE</em>: If fields are added or removed, the method {@link #getHashes()} * <em>NOTE</em>: If fields are added or removed, the method {@link #getHashes()}
@ -105,7 +100,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
MailboxColumns.SYNC_STATUS, MailboxColumns.PARENT_KEY, MailboxColumns.LAST_TOUCHED_TIME, MailboxColumns.SYNC_STATUS, MailboxColumns.PARENT_KEY, MailboxColumns.LAST_TOUCHED_TIME,
MailboxColumns.UI_SYNC_STATUS, MailboxColumns.UI_LAST_SYNC_RESULT, MailboxColumns.UI_SYNC_STATUS, MailboxColumns.UI_LAST_SYNC_RESULT,
MailboxColumns.LAST_NOTIFIED_MESSAGE_KEY, MailboxColumns.LAST_NOTIFIED_MESSAGE_COUNT, MailboxColumns.LAST_NOTIFIED_MESSAGE_KEY, MailboxColumns.LAST_NOTIFIED_MESSAGE_COUNT,
MailboxColumns.TOTAL_COUNT, MailboxColumns.HIERARCHICAL_NAME MailboxColumns.TOTAL_COUNT, MailboxColumns.LAST_SEEN_MESSAGE_KEY
}; };
private static final String ACCOUNT_AND_MAILBOX_TYPE_SELECTION = private static final String ACCOUNT_AND_MAILBOX_TYPE_SELECTION =
@ -337,7 +332,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
mLastNotifiedMessageKey = cursor.getLong(CONTENT_LAST_NOTIFIED_MESSAGE_KEY_COLUMN); mLastNotifiedMessageKey = cursor.getLong(CONTENT_LAST_NOTIFIED_MESSAGE_KEY_COLUMN);
mLastNotifiedMessageCount = cursor.getInt(CONTENT_LAST_NOTIFIED_MESSAGE_COUNT_COLUMN); mLastNotifiedMessageCount = cursor.getInt(CONTENT_LAST_NOTIFIED_MESSAGE_COUNT_COLUMN);
mTotalCount = cursor.getInt(CONTENT_TOTAL_COUNT_COLUMN); mTotalCount = cursor.getInt(CONTENT_TOTAL_COUNT_COLUMN);
mHierarchicalName = cursor.getString(CONTENT_HIERARCHICAL_NAME_COLUMN); mLastSeenMessageKey = cursor.getLong(CONTENT_LAST_SEEN_MESSAGE_KEY_COLUMN);
} }
@Override @Override
@ -364,7 +359,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
values.put(MailboxColumns.LAST_NOTIFIED_MESSAGE_KEY, mLastNotifiedMessageKey); values.put(MailboxColumns.LAST_NOTIFIED_MESSAGE_KEY, mLastNotifiedMessageKey);
values.put(MailboxColumns.LAST_NOTIFIED_MESSAGE_COUNT, mLastNotifiedMessageCount); values.put(MailboxColumns.LAST_NOTIFIED_MESSAGE_COUNT, mLastNotifiedMessageCount);
values.put(MailboxColumns.TOTAL_COUNT, mTotalCount); values.put(MailboxColumns.TOTAL_COUNT, mTotalCount);
values.put(MailboxColumns.HIERARCHICAL_NAME, mHierarchicalName); values.put(MailboxColumns.LAST_SEEN_MESSAGE_KEY, mLastSeenMessageKey);
return values; return values;
} }
@ -497,9 +492,43 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
case TYPE_MAIL: case TYPE_MAIL:
case TYPE_TRASH: case TYPE_TRASH:
case TYPE_JUNK: case TYPE_JUNK:
case TYPE_SENT:
return true; return true;
} }
return false; // TYPE_DRAFTS, TYPE_OUTBOX, TYPE_SENT, etc return false; // TYPE_DRAFTS, TYPE_OUTBOX, etc
}
/**
* @return whether or not this mailbox retrieves its data from the server (as opposed to just
* a local mailbox that is never synced).
*/
public boolean loadsFromServer(String protocol) {
if (HostAuth.SCHEME_EAS.equals(protocol)) {
return mType != Mailbox.TYPE_DRAFTS
&& mType != Mailbox.TYPE_OUTBOX
&& mType != Mailbox.TYPE_SEARCH
&& mType < Mailbox.TYPE_NOT_SYNCABLE;
} else if (HostAuth.SCHEME_IMAP.equals(protocol)) {
// TODO: actually use a sync flag when creating the mailboxes. Right now we use an
// approximation for IMAP.
return mType != Mailbox.TYPE_DRAFTS
&& mType != Mailbox.TYPE_OUTBOX
&& mType != Mailbox.TYPE_SEARCH;
} else if (HostAuth.SCHEME_POP3.equals(protocol)) {
return TYPE_INBOX == mType;
}
return false;
}
public boolean uploadsToServer(Context context) {
if (mType == TYPE_DRAFTS || mType == TYPE_OUTBOX || mType == TYPE_SEARCH) {
return false;
}
String protocol = Account.getProtocol(context, mAccountKey);
return (!protocol.equals(HostAuth.SCHEME_POP3));
} }
/** /**
@ -560,8 +589,6 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
= mLastNotifiedMessageCount; = mLastNotifiedMessageCount;
hash[CONTENT_TOTAL_COUNT_COLUMN] hash[CONTENT_TOTAL_COUNT_COLUMN]
= mTotalCount; = mTotalCount;
hash[CONTENT_HIERARCHICAL_NAME_COLUMN]
= mHierarchicalName;
return hash; return hash;
} }
@ -597,7 +624,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
dest.writeLong(mLastNotifiedMessageKey); dest.writeLong(mLastNotifiedMessageKey);
dest.writeInt(mLastNotifiedMessageCount); dest.writeInt(mLastNotifiedMessageCount);
dest.writeInt(mTotalCount); dest.writeInt(mTotalCount);
dest.writeString(mHierarchicalName); dest.writeLong(mLastSeenMessageKey);
} }
public Mailbox(Parcel in) { public Mailbox(Parcel in) {
@ -624,7 +651,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
mLastNotifiedMessageKey = in.readLong(); mLastNotifiedMessageKey = in.readLong();
mLastNotifiedMessageCount = in.readInt(); mLastNotifiedMessageCount = in.readInt();
mTotalCount = in.readInt(); mTotalCount = in.readInt();
mHierarchicalName = in.readString(); mLastSeenMessageKey = in.readLong();
} }
public static final Parcelable.Creator<Mailbox> CREATOR = new Parcelable.Creator<Mailbox>() { public static final Parcelable.Creator<Mailbox> CREATOR = new Parcelable.Creator<Mailbox>() {

View File

@ -1,274 +0,0 @@
/*
* Copyright (C) 2011 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.emailcommon.provider;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.os.Debug;
import android.text.TextUtils;
import android.util.Log;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.EmailContent.MailboxColumns;
import com.android.emailcommon.provider.Mailbox;
import java.util.HashMap;
public class MailboxUtilities {
public static final String WHERE_PARENT_KEY_UNINITIALIZED =
"(" + MailboxColumns.PARENT_KEY + " isnull OR " + MailboxColumns.PARENT_KEY + "=" +
Mailbox.PARENT_KEY_UNINITIALIZED + ")";
// The flag we use in Account to indicate a mailbox change in progress
private static final int ACCOUNT_MAILBOX_CHANGE_FLAG = Account.FLAGS_SYNC_ADAPTER;
/**
* Recalculate a mailbox's flags and the parent key of any children
* @param context the caller's context
* @param parentCursor a cursor to a mailbox that requires fixup
*/
public static void setFlagsAndChildrensParentKey(Context context, Cursor parentCursor,
String accountSelector) {
ContentResolver resolver = context.getContentResolver();
String[] selectionArgs = new String[1];
ContentValues parentValues = new ContentValues();
// Get the data we need first
long parentId = parentCursor.getLong(Mailbox.CONTENT_ID_COLUMN);
int parentFlags = 0;
int parentType = parentCursor.getInt(Mailbox.CONTENT_TYPE_COLUMN);
String parentServerId = parentCursor.getString(Mailbox.CONTENT_SERVER_ID_COLUMN);
// All email-type boxes hold mail
if (parentType <= Mailbox.TYPE_NOT_EMAIL) {
parentFlags |= Mailbox.FLAG_HOLDS_MAIL + Mailbox.FLAG_SUPPORTS_SETTINGS;
}
// Outbox, Drafts, and Sent don't allow mail to be moved to them
if (parentType == Mailbox.TYPE_MAIL || parentType == Mailbox.TYPE_TRASH ||
parentType == Mailbox.TYPE_JUNK || parentType == Mailbox.TYPE_INBOX) {
parentFlags |= Mailbox.FLAG_ACCEPTS_MOVED_MAIL;
}
// There's no concept of "append" in EAS so FLAG_ACCEPTS_APPENDED_MAIL is never used
// Mark parent mailboxes as parents & add parent key to children
// An example of a mailbox with a null serverId would be an Outbox that we create locally
// for hotmail accounts (which don't have a server-based Outbox)
if (parentServerId != null) {
selectionArgs[0] = parentServerId;
Cursor childCursor = resolver.query(Mailbox.CONTENT_URI,
Mailbox.ID_PROJECTION, MailboxColumns.PARENT_SERVER_ID + "=? AND " +
accountSelector, selectionArgs, null);
if (childCursor == null) return;
try {
while (childCursor.moveToNext()) {
parentFlags |= Mailbox.FLAG_HAS_CHILDREN | Mailbox.FLAG_CHILDREN_VISIBLE;
ContentValues childValues = new ContentValues();
childValues.put(Mailbox.PARENT_KEY, parentId);
long childId = childCursor.getLong(Mailbox.ID_PROJECTION_COLUMN);
resolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, childId),
childValues, null, null);
}
} finally {
childCursor.close();
}
} else {
// Mark this is having no parent, so that we don't examine this mailbox again
parentValues.put(Mailbox.PARENT_KEY, Mailbox.NO_MAILBOX);
Log.w(Logging.LOG_TAG, "Mailbox with null serverId: " +
parentCursor.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN) + ", type: " +
parentType);
}
// Save away updated flags and parent key (if any)
parentValues.put(Mailbox.FLAGS, parentFlags);
resolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, parentId),
parentValues, null, null);
}
/**
* Recalculate a mailbox's flags and the parent key of any children
* @param context the caller's context
* @param accountSelector (see description below in fixupUninitializedParentKeys)
* @param serverId the server id of an individual mailbox
*/
public static void setFlagsAndChildrensParentKey(Context context, String accountSelector,
String serverId) {
Cursor cursor = context.getContentResolver().query(Mailbox.CONTENT_URI,
Mailbox.CONTENT_PROJECTION, MailboxColumns.SERVER_ID + "=? AND " + accountSelector,
new String[] {serverId}, null);
if (cursor == null) return;
try {
if (cursor.moveToFirst()) {
setFlagsAndChildrensParentKey(context, cursor, accountSelector);
}
} finally {
cursor.close();
}
}
/**
* Given an account selector, specifying the account(s) on which to work, create the parentKey
* and flags for each mailbox in the account(s) that is uninitialized (parentKey = 0 or null)
*
* @param accountSelector a sqlite WHERE clause expression to be used in determining the
* mailboxes to be acted upon, e.g. accountKey IN (1, 2), accountKey = 12, etc.
*/
public static void fixupUninitializedParentKeys(Context context, String accountSelector) {
// Sanity check first on our arguments
if (accountSelector == null) throw new IllegalArgumentException();
// The selection we'll use to find uninitialized parent key mailboxes
String noParentKeySelection = WHERE_PARENT_KEY_UNINITIALIZED + " AND " + accountSelector;
// We'll loop through mailboxes with an uninitialized parent key
ContentResolver resolver = context.getContentResolver();
Cursor noParentKeyMailboxCursor =
resolver.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
noParentKeySelection, null, null);
if (noParentKeyMailboxCursor == null) return;
try {
while (noParentKeyMailboxCursor.moveToNext()) {
setFlagsAndChildrensParentKey(context, noParentKeyMailboxCursor, accountSelector);
String parentServerId =
noParentKeyMailboxCursor.getString(Mailbox.CONTENT_PARENT_SERVER_ID_COLUMN);
// Fixup the parent so that the children's parentKey is updated
if (parentServerId != null) {
setFlagsAndChildrensParentKey(context, accountSelector, parentServerId);
}
}
} finally {
noParentKeyMailboxCursor.close();
}
// Any mailboxes without a parent key should have parentKey set to -1 (no parent)
ContentValues values = new ContentValues();
values.put(Mailbox.PARENT_KEY, Mailbox.NO_MAILBOX);
resolver.update(Mailbox.CONTENT_URI, values, noParentKeySelection, null);
}
private static void setAccountSyncAdapterFlag(Context context, long accountId, boolean start) {
Account account = Account.restoreAccountWithId(context, accountId);
if (account == null) return;
// Set temporary flag indicating state of update of mailbox list
ContentValues cv = new ContentValues();
cv.put(Account.FLAGS, start ? (account.mFlags | ACCOUNT_MAILBOX_CHANGE_FLAG) :
account.mFlags & ~ACCOUNT_MAILBOX_CHANGE_FLAG);
context.getContentResolver().update(
ContentUris.withAppendedId(Account.CONTENT_URI, account.mId), cv, null, null);
}
/**
* Indicate that the specified account is starting the process of changing its mailbox list
* @param context the caller's context
* @param accountId the account that is starting to change its mailbox list
*/
public static void startMailboxChanges(Context context, long accountId) {
setAccountSyncAdapterFlag(context, accountId, true);
}
/**
* Indicate that the specified account is ending the process of changing its mailbox list
* @param context the caller's context
* @param accountId the account that is finished with changes to its mailbox list
*/
public static void endMailboxChanges(Context context, long accountId) {
setAccountSyncAdapterFlag(context, accountId, false);
}
/**
* Check that we didn't leave the account's mailboxes in a (possibly) inconsistent state
* If we did, make them consistent again
* @param context the caller's context
* @param accountId the account whose mailboxes are to be checked
*/
public static void checkMailboxConsistency(Context context, long accountId) {
// If our temporary flag is set, we were interrupted during an update
// First, make sure we're current (really fast w/ caching)
Account account = Account.restoreAccountWithId(context, accountId);
if (account == null) return;
if ((account.mFlags & ACCOUNT_MAILBOX_CHANGE_FLAG) != 0) {
Log.w(Logging.LOG_TAG, "Account " + account.mDisplayName +
" has inconsistent mailbox data; fixing up...");
// Set all account mailboxes to uninitialized parent key
ContentValues values = new ContentValues();
values.put(Mailbox.PARENT_KEY, Mailbox.PARENT_KEY_UNINITIALIZED);
String accountSelector = Mailbox.ACCOUNT_KEY + "=" + account.mId;
ContentResolver resolver = context.getContentResolver();
resolver.update(Mailbox.CONTENT_URI, values, accountSelector, null);
// Fix up keys and flags
MailboxUtilities.fixupUninitializedParentKeys(context, accountSelector);
// Clear the temporary flag
endMailboxChanges(context, accountId);
}
}
private static final String[] HIERARCHY_PROJECTION = new String[] {
MailboxColumns.ID, MailboxColumns.DISPLAY_NAME, MailboxColumns.PARENT_KEY,
MailboxColumns.HIERARCHICAL_NAME
};
private static final int HIERARCHY_ID = 0;
private static final int HIERARCHY_NAME = 1;
private static final int HIERARCHY_PARENT_KEY = 2;
private static final int HIERARCHY_HIERARCHICAL_NAME = 3;
private static String getHierarchicalName(Context context, long id, HashMap<Long, String> map,
String name, long parentId) {
String hierarchicalName;
if (map.containsKey(id)) {
return map.get(id);
} else if (parentId == Mailbox.NO_MAILBOX) {
hierarchicalName = name;
} else {
Mailbox parent = Mailbox.restoreMailboxWithId(context, parentId);
if (parent == null) return name + "/" + "??";
hierarchicalName = getHierarchicalName(context, parentId, map, parent.mDisplayName,
parent.mParentKey) + "/" + name;
}
map.put(id, hierarchicalName);
return hierarchicalName;
}
public static void setupHierarchicalNames(Context context, long accountId) {
Account account = Account.restoreAccountWithId(context, accountId);
if (account == null) return;
// Start by clearing all names
ContentValues values = new ContentValues();
String accountSelector = Mailbox.ACCOUNT_KEY + "=" + account.mId;
ContentResolver resolver = context.getContentResolver();
HashMap<Long, String> nameMap = new HashMap<Long, String>();
Cursor c = resolver.query(Mailbox.CONTENT_URI, HIERARCHY_PROJECTION, accountSelector,
null, null);
try {
while(c.moveToNext()) {
long id = c.getLong(HIERARCHY_ID);
String displayName = c.getString(HIERARCHY_NAME);
String name = getHierarchicalName(context, id, nameMap, displayName,
c.getLong(HIERARCHY_PARENT_KEY));
String oldHierarchicalName = c.getString(HIERARCHY_HIERARCHICAL_NAME);
// Don't write the name unless it has changed or we don't need one (it's top-level)
if (name.equals(oldHierarchicalName) ||
((name.equals(displayName)) && TextUtils.isEmpty(oldHierarchicalName))) {
continue;
}
// If the name has changed, update it
values.put(MailboxColumns.HIERARCHICAL_NAME, name);
resolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, id), values, null,
null);
}
} finally {
c.close();
}
}
}

View File

@ -40,11 +40,8 @@ public final class Policy extends EmailContent implements EmailContent.PolicyCol
public static final String TAG = "Email/Policy"; public static final String TAG = "Email/Policy";
public static final String TABLE_NAME = "Policy"; public static final String TABLE_NAME = "Policy";
public static Uri CONTENT_URI; @SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/policy");
public static void initPolicy() {
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/policy");
}
/* Convert days to mSec (used for password expiration) */ /* Convert days to mSec (used for password expiration) */
private static final long DAYS_TO_MSEC = 24 * 60 * 60 * 1000; private static final long DAYS_TO_MSEC = 24 * 60 * 60 * 1000;

View File

@ -17,6 +17,10 @@
package com.android.emailcommon.provider; package com.android.emailcommon.provider;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.EmailContent.QuickResponseColumns;
import com.google.common.base.Objects;
import android.content.ContentUris; import android.content.ContentUris;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
@ -25,9 +29,6 @@ import android.net.Uri;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import com.android.emailcommon.provider.EmailContent.QuickResponseColumns;
import com.google.common.base.Objects;
/** /**
* A user-modifiable message that may be quickly inserted into the body while user is composing * A user-modifiable message that may be quickly inserted into the body while user is composing
* a message. Tied to a specific account. * a message. Tied to a specific account.
@ -35,13 +36,11 @@ import com.google.common.base.Objects;
public final class QuickResponse extends EmailContent public final class QuickResponse extends EmailContent
implements QuickResponseColumns, Parcelable { implements QuickResponseColumns, Parcelable {
public static final String TABLE_NAME = "QuickResponse"; public static final String TABLE_NAME = "QuickResponse";
public static Uri CONTENT_URI; @SuppressWarnings("hiding")
public static Uri ACCOUNT_ID_URI; public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI
+ "/quickresponse");
public static void initQuickResponse() { public static final Uri ACCOUNT_ID_URI = Uri.parse(
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/quickresponse"); EmailContent.CONTENT_URI + "/quickresponse/account");
ACCOUNT_ID_URI = Uri.parse(EmailContent.CONTENT_URI + "/quickresponse/account");
}
private String mText; private String mText;
private long mAccountKey; private long mAccountKey;

View File

@ -24,13 +24,14 @@ import android.os.RemoteException;
public class AccountServiceProxy extends ServiceProxy implements IAccountService { public class AccountServiceProxy extends ServiceProxy implements IAccountService {
public static final String ACCOUNT_INTENT = "com.android.email.ACCOUNT_INTENT";
public static final int DEFAULT_ACCOUNT_COLOR = 0xFF0000FF; public static final int DEFAULT_ACCOUNT_COLOR = 0xFF0000FF;
private IAccountService mService = null; private IAccountService mService = null;
private Object mReturn; private Object mReturn;
public AccountServiceProxy(Context _context) { public AccountServiceProxy(Context _context) {
super(_context, getIntentForEmailPackage(_context, "ACCOUNT_INTENT")); super(_context, new Intent(ACCOUNT_INTENT));
} }
@Override @Override
@ -44,11 +45,11 @@ public class AccountServiceProxy extends ServiceProxy implements IAccountService
} }
@Override @Override
public void notifyLoginFailed(final long accountId, final String reason) { public void notifyLoginFailed(final long accountId) {
setTask(new ProxyTask() { setTask(new ProxyTask() {
@Override @Override
public void run() throws RemoteException { public void run() throws RemoteException {
mService.notifyLoginFailed(accountId, reason); mService.notifyLoginFailed(accountId);
} }
}, "notifyLoginFailed"); }, "notifyLoginFailed");
} }

View File

@ -1,117 +0,0 @@
/* Copyright (C) 2012 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.emailcommon.service;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import com.android.emailcommon.service.IEmailServiceCallback.Stub;
public class EmailServiceCallback extends Stub {
private final RemoteCallbackList<IEmailServiceCallback> mCallbackList;
public EmailServiceCallback(RemoteCallbackList<IEmailServiceCallback> callbackList) {
mCallbackList = callbackList;
}
/**
* Broadcast a callback to the everyone that's registered
*
* @param wrapper the ServiceCallbackWrapper used in the broadcast
*/
private synchronized void broadcastCallback(ServiceCallbackWrapper wrapper) {
RemoteCallbackList<IEmailServiceCallback> callbackList = mCallbackList;
if (callbackList != null) {
// Call everyone on our callback list
int count = callbackList.beginBroadcast();
try {
for (int i = 0; i < count; i++) {
try {
wrapper.call(callbackList.getBroadcastItem(i));
} catch (RemoteException e) {
// Safe to ignore
} catch (RuntimeException e) {
// We don't want an exception in one call to prevent other calls, so
// we'll just log this and continue
Log.e("EmailServiceCallback", "Caught RuntimeException in broadcast", e);
}
}
} finally {
// No matter what, we need to finish the broadcast
callbackList.finishBroadcast();
}
}
}
@Override
public void loadAttachmentStatus(final long messageId, final long attachmentId,
final int status, final int progress) {
broadcastCallback(new ServiceCallbackWrapper() {
@Override
public void call(IEmailServiceCallback cb) throws RemoteException {
cb.loadAttachmentStatus(messageId, attachmentId, status, progress);
}
});
}
@Override
public void loadMessageStatus(final long messageId, final int status, final int progress) {
broadcastCallback(new ServiceCallbackWrapper() {
@Override
public void call(IEmailServiceCallback cb) throws RemoteException {
cb.loadMessageStatus(messageId, status, progress);
}
});
}
@Override
public void sendMessageStatus(final long messageId, final String subject, final int status,
final int progress) {
broadcastCallback(new ServiceCallbackWrapper() {
@Override
public void call(IEmailServiceCallback cb) throws RemoteException {
cb.sendMessageStatus(messageId, subject, status, progress);
}
});
}
@Override
public void syncMailboxListStatus(final long accountId, final int status,
final int progress) {
broadcastCallback(new ServiceCallbackWrapper() {
@Override
public void call(IEmailServiceCallback cb) throws RemoteException {
cb.syncMailboxListStatus(accountId, status, progress);
}
});
}
@Override
public void syncMailboxStatus(final long mailboxId, final int status,
final int progress) {
broadcastCallback(new ServiceCallbackWrapper() {
@Override
public void call(IEmailServiceCallback cb) throws RemoteException {
cb.syncMailboxStatus(mailboxId, status, progress);
}
});
}
private interface ServiceCallbackWrapper {
public void call(IEmailServiceCallback cb) throws RemoteException;
}
}

View File

@ -27,7 +27,6 @@ import com.android.emailcommon.Api;
import com.android.emailcommon.Device; import com.android.emailcommon.Device;
import com.android.emailcommon.TempDirectory; import com.android.emailcommon.TempDirectory;
import com.android.emailcommon.mail.MessagingException; import com.android.emailcommon.mail.MessagingException;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.HostAuth; import com.android.emailcommon.provider.HostAuth;
import com.android.emailcommon.provider.Policy; import com.android.emailcommon.provider.Policy;
@ -51,6 +50,10 @@ import java.io.IOException;
public class EmailServiceProxy extends ServiceProxy implements IEmailService { public class EmailServiceProxy extends ServiceProxy implements IEmailService {
private static final String TAG = "EmailServiceProxy"; private static final String TAG = "EmailServiceProxy";
// Private intent that will be used to connect to an independent Exchange service
public static final String EXCHANGE_INTENT = "com.android.email.EXCHANGE_INTENT";
public static final String IMAP_INTENT = "com.android.email.IMAP_INTENT";
public static final String AUTO_DISCOVER_BUNDLE_ERROR_CODE = "autodiscover_error_code"; public static final String AUTO_DISCOVER_BUNDLE_ERROR_CODE = "autodiscover_error_code";
public static final String AUTO_DISCOVER_BUNDLE_HOST_AUTH = "autodiscover_host_auth"; public static final String AUTO_DISCOVER_BUNDLE_HOST_AUTH = "autodiscover_host_auth";
@ -411,7 +414,9 @@ public class EmailServiceProxy extends ServiceProxy implements IEmailService {
}, "deleteAccountPIMData"); }, "deleteAccountPIMData");
} }
/** /**
* PRELIMINARY
* Search for messages given a query string. The string is interpreted as the logical AND of * Search for messages given a query string. The string is interpreted as the logical AND of
* terms separated by white space. The search is performed on the specified mailbox in the * terms separated by white space. The search is performed on the specified mailbox in the
* specified account (including subfolders, as specified by the includeSubfolders parameter). * specified account (including subfolders, as specified by the includeSubfolders parameter).
@ -459,40 +464,6 @@ public class EmailServiceProxy extends ServiceProxy implements IEmailService {
}, "sendMail"); }, "sendMail");
} }
@Override
public int getCapabilities(final Account acct) throws RemoteException {
setTask(new ProxyTask() {
@Override
public void run() throws RemoteException{
if (mCallback != null) mService.setCallback(mCallback);
mReturn = mService.getCapabilities(acct);
}
}, "getCapabilities");
waitForCompletion();
if (mReturn == null) {
return 0;
} else {
return (Integer)mReturn;
}
}
/**
* Request that the account be updated for this service; this call is synchronous
*
* @param the email address of the account to be updated
*/
@Override
public void serviceUpdated(final String emailAddress) throws RemoteException {
setTask(new ProxyTask() {
@Override
public void run() throws RemoteException{
if (mCallback != null) mService.setCallback(mCallback);
mService.serviceUpdated(emailAddress);
}
}, "settingsUpdate");
waitForCompletion();
}
@Override @Override
public IBinder asBinder() { public IBinder asBinder() {
return null; return null;

View File

@ -19,7 +19,7 @@ package com.android.emailcommon.service;
import android.os.Bundle; import android.os.Bundle;
interface IAccountService { interface IAccountService {
oneway void notifyLoginFailed(long accountId, String reason); oneway void notifyLoginFailed(long accountId);
oneway void notifyLoginSucceeded(long accountId); oneway void notifyLoginSucceeded(long accountId);
void reconcileAccounts(String protocol, String accountManagerType); void reconcileAccounts(String protocol, String accountManagerType);

View File

@ -18,7 +18,6 @@
package com.android.emailcommon.service; package com.android.emailcommon.service;
import com.android.emailcommon.provider.HostAuth; import com.android.emailcommon.provider.HostAuth;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.service.IEmailServiceCallback; import com.android.emailcommon.service.IEmailServiceCallback;
import com.android.emailcommon.service.SearchParams; import com.android.emailcommon.service.SearchParams;
import android.os.Bundle; import android.os.Bundle;
@ -60,9 +59,4 @@ interface IEmailService {
int searchMessages(long accountId, in SearchParams params, long destMailboxId); int searchMessages(long accountId, in SearchParams params, long destMailboxId);
void sendMail(long accountId); void sendMail(long accountId);
// API level 3
int getCapabilities(in Account acct);
void serviceUpdated(String emailAddress);
} }

View File

@ -17,6 +17,7 @@
package com.android.emailcommon.service; package com.android.emailcommon.service;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log; import android.util.Log;
@ -28,11 +29,14 @@ public class PolicyServiceProxy extends ServiceProxy implements IPolicyService {
private static final boolean DEBUG_PROXY = false; // DO NOT CHECK THIS IN SET TO TRUE private static final boolean DEBUG_PROXY = false; // DO NOT CHECK THIS IN SET TO TRUE
private static final String TAG = "PolicyServiceProxy"; private static final String TAG = "PolicyServiceProxy";
// The intent used by sync adapter services to connect to the PolicyService
public static final String POLICY_INTENT = "com.android.email.POLICY_INTENT";
private IPolicyService mService = null; private IPolicyService mService = null;
private Object mReturn = null; private Object mReturn = null;
public PolicyServiceProxy(Context _context) { public PolicyServiceProxy(Context _context) {
super(_context, getIntentForEmailPackage(_context, "POLICY_INTENT")); super(_context, new Intent(POLICY_INTENT));
} }
@Override @Override

View File

@ -56,27 +56,6 @@ public abstract class ServiceProxy {
private long mStartTime; private long mStartTime;
private boolean mDead = false; private boolean mDead = false;
public static Intent getIntentForEmailPackage(Context context, String actionName) {
return new Intent(getIntentStringForEmailPackage(context, actionName));
}
/**
* Create Intent action based on the Email package name
* Package com.android.email + ACTION -> com.android.email.ACTION
* Package com.google.android.email + ACTION -> com.google.android.email.ACTION
* Package com.android.exchange + ACTION -> com.android.email.ACTION
* Package com.google.exchange + ACTION -> com.google.android.email.ACTION
*
* @param context the caller's context
* @param actionName the Intent action
* @return an Intent action based on the package name
*/
public static String getIntentStringForEmailPackage(Context context, String actionName) {
String packageName = context.getPackageName();
int lastDot = packageName.lastIndexOf('.');
return packageName.substring(0, lastDot + 1) + "email." + actionName;
}
public abstract void onConnected(IBinder binder); public abstract void onConnected(IBinder binder);
public ServiceProxy(Context _context, Intent _intent) { public ServiceProxy(Context _context, Intent _intent) {
@ -92,8 +71,7 @@ public abstract class ServiceProxy {
public void onServiceConnected(ComponentName name, IBinder binder) { public void onServiceConnected(ComponentName name, IBinder binder) {
onConnected(binder); onConnected(binder);
if (DEBUG_PROXY) { if (DEBUG_PROXY) {
Log.v(mTag, "Connected: " + name.getShortClassName() + " at " + Log.v(mTag, "Connected: " + name.getShortClassName());
(System.currentTimeMillis() - mStartTime) + "ms");
} }
// Run our task on a new thread // Run our task on a new thread
new Thread(new Runnable() { new Thread(new Runnable() {
@ -108,8 +86,7 @@ public abstract class ServiceProxy {
public void onServiceDisconnected(ComponentName name) { public void onServiceDisconnected(ComponentName name) {
if (DEBUG_PROXY) { if (DEBUG_PROXY) {
Log.v(mTag, "Disconnected: " + name.getShortClassName() + " at " + Log.v(mTag, "Disconnected: " + name.getShortClassName());
(System.currentTimeMillis() - mStartTime) + "ms");
} }
} }
} }
@ -192,9 +169,8 @@ public abstract class ServiceProxy {
// Can be ignored safely // Can be ignored safely
} }
if (DEBUG_PROXY) { if (DEBUG_PROXY) {
Log.v(mTag, "Wait for " + mName + (mDead ? " finished in " : " timed out in ") + Log.v(mTag, "Wait for " + mName + " finished in " +
(System.currentTimeMillis() - time) + "ms"); (System.currentTimeMillis() - time) + "ms");
mDead = true;
} }
} }
} }

View File

@ -26,25 +26,4 @@ public class SyncWindow {
public static final int SYNC_WINDOW_2_WEEKS = 4; public static final int SYNC_WINDOW_2_WEEKS = 4;
public static final int SYNC_WINDOW_1_MONTH = 5; public static final int SYNC_WINDOW_1_MONTH = 5;
public static final int SYNC_WINDOW_ALL = 6; public static final int SYNC_WINDOW_ALL = 6;
public static int toDays(int window) {
switch(window) {
case SYNC_WINDOW_1_DAY:
return 1;
case SYNC_WINDOW_3_DAYS:
return 3;
case SYNC_WINDOW_1_WEEK:
return 7;
case SYNC_WINDOW_2_WEEKS:
return 14;
case SYNC_WINDOW_1_MONTH:
return 30;
case SYNC_WINDOW_ALL:
return 365*10;
case SYNC_WINDOW_UNKNOWN:
case SYNC_WINDOW_AUTO:
default:
return 14;
}
}
} }

View File

@ -32,8 +32,6 @@ import android.webkit.MimeTypeMap;
import com.android.emailcommon.Logging; import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.EmailContent.Attachment; import com.android.emailcommon.provider.EmailContent.Attachment;
import com.android.emailcommon.provider.EmailContent.AttachmentColumns; import com.android.emailcommon.provider.EmailContent.AttachmentColumns;
import com.android.emailcommon.provider.EmailContent.Body;
import com.android.emailcommon.provider.EmailContent.BodyColumns;
import com.android.emailcommon.provider.EmailContent.Message; import com.android.emailcommon.provider.EmailContent.Message;
import com.android.emailcommon.provider.EmailContent.MessageColumns; import com.android.emailcommon.provider.EmailContent.MessageColumns;
import com.android.mail.providers.UIProvider; import com.android.mail.providers.UIProvider;
@ -44,9 +42,10 @@ import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
public class AttachmentUtilities { public class AttachmentUtilities {
public static final String AUTHORITY = "com.android.email.attachmentprovider";
public static final Uri CONTENT_URI = Uri.parse( "content://" + AUTHORITY);
public static final String FORMAT_RAW = "RAW"; public static final String FORMAT_RAW = "RAW";
public static final String FORMAT_THUMBNAIL = "THUMBNAIL"; public static final String FORMAT_THUMBNAIL = "THUMBNAIL";
@ -135,16 +134,23 @@ public class AttachmentUtilities {
*/ */
public static final int MAX_ATTACHMENT_UPLOAD_SIZE = (5 * 1024 * 1024); public static final int MAX_ATTACHMENT_UPLOAD_SIZE = (5 * 1024 * 1024);
private static Uri sUri;
public static Uri getAttachmentUri(long accountId, long id) { public static Uri getAttachmentUri(long accountId, long id) {
if (sUri == null) { return CONTENT_URI.buildUpon()
sUri = Uri.parse(Attachment.ATTACHMENT_PROVIDER_URI_PREFIX); .appendPath(Long.toString(accountId))
} .appendPath(Long.toString(id))
return sUri.buildUpon() .appendPath(FORMAT_RAW)
.appendPath(Long.toString(accountId)) .build();
.appendPath(Long.toString(id)) }
.appendPath(FORMAT_RAW)
.build(); public static Uri getAttachmentThumbnailUri(long accountId, long id,
int width, int height) {
return CONTENT_URI.buildUpon()
.appendPath(Long.toString(accountId))
.appendPath(Long.toString(id))
.appendPath(FORMAT_THUMBNAIL)
.appendPath(Integer.toString(width))
.appendPath(Integer.toString(height))
.build();
} }
/** /**
@ -229,6 +235,24 @@ public class AttachmentUtilities {
return resultType.toLowerCase(); return resultType.toLowerCase();
} }
/**
* @return mime-type for a {@link Uri}.
* - Use {@link ContentResolver#getType} for a content: URI.
* - Use {@link #inferMimeType} for a file: URI.
* - Otherwise returns null.
*/
public static String inferMimeTypeForUri(Context context, Uri uri) {
final String scheme = uri.getScheme();
if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
return context.getContentResolver().getType(uri);
} else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
return inferMimeType(uri.getLastPathSegment(), "");
} else {
Log.e(Logging.LOG_TAG, "Unable to determine MIME type for uri=" + uri, new Error());
return null;
}
}
/** /**
* Extract and return filename's extension, converted to lower case, and not including the "." * Extract and return filename's extension, converted to lower case, and not including the "."
* *
@ -338,7 +362,8 @@ public class AttachmentUtilities {
} }
} }
private static long copyFile(InputStream in, OutputStream out) throws IOException { private static long copyFile(InputStream in, File file) throws IOException {
FileOutputStream out = new FileOutputStream(file);
long size = IOUtils.copy(in, out); long size = IOUtils.copy(in, out);
in.close(); in.close();
out.flush(); out.flush();
@ -354,20 +379,24 @@ public class AttachmentUtilities {
ContentValues cv = new ContentValues(); ContentValues cv = new ContentValues();
long attachmentId = attachment.mId; long attachmentId = attachment.mId;
long accountId = attachment.mAccountKey; long accountId = attachment.mAccountKey;
String contentUri = null; String contentUri;
long size; long size;
try { try {
ContentResolver resolver = context.getContentResolver();
if (attachment.mUiDestination == UIProvider.AttachmentDestination.CACHE) { if (attachment.mUiDestination == UIProvider.AttachmentDestination.CACHE) {
Uri attUri = getAttachmentUri(accountId, attachmentId); File saveIn = getAttachmentDirectory(context, accountId);
size = copyFile(in, resolver.openOutputStream(attUri)); if (!saveIn.exists()) {
contentUri = attUri.toString(); saveIn.mkdirs();
}
File file = getAttachmentFilename(context, accountId, attachmentId);
file.createNewFile();
size = copyFile(in, file);
contentUri = getAttachmentUri(accountId, attachmentId).toString();
} else if (Utility.isExternalStorageMounted()) { } else if (Utility.isExternalStorageMounted()) {
File downloads = Environment.getExternalStoragePublicDirectory( File downloads = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS); Environment.DIRECTORY_DOWNLOADS);
downloads.mkdirs(); downloads.mkdirs();
File file = Utility.createUniqueFile(downloads, attachment.mFileName); File file = Utility.createUniqueFile(downloads, attachment.mFileName);
size = copyFile(in, new FileOutputStream(file)); size = copyFile(in, file);
String absolutePath = file.getAbsolutePath(); String absolutePath = file.getAbsolutePath();
// Although the download manager can scan media files, scanning only happens // Although the download manager can scan media files, scanning only happens
@ -400,20 +429,5 @@ public class AttachmentUtilities {
} }
context.getContentResolver().update(uri, cv, null, null); context.getContentResolver().update(uri, cv, null, null);
// If this is an inline attachment, update the body
if (contentUri != null && attachment.mContentId != null) {
Body body = Body.restoreBodyWithMessageId(context, attachment.mMessageKey);
if (body != null && body.mHtmlContent != null) {
cv.clear();
String html = body.mHtmlContent;
String contentIdRe =
"\\s+(?i)src=\"cid(?-i):\\Q" + attachment.mContentId + "\\E\"";
String srcContentUri = " src=\"" + contentUri + "\"";
html = html.replaceAll(contentIdRe, srcContentUri);
cv.put(BodyColumns.HTML_CONTENT, html);
context.getContentResolver().update(
ContentUris.withAppendedId(Body.CONTENT_URI, body.mId), cv, null, null);
}
}
} }
} }

View File

@ -42,10 +42,9 @@ import javax.net.ssl.KeyManager;
*/ */
public class EmailClientConnectionManager extends ThreadSafeClientConnManager { public class EmailClientConnectionManager extends ThreadSafeClientConnManager {
private static final boolean LOG_ENABLED = false;
private static final int STANDARD_PORT = 80; private static final int STANDARD_PORT = 80;
private static final int STANDARD_SSL_PORT = 443; private static final int STANDARD_SSL_PORT = 443;
private static final boolean LOG_ENABLED = false;
/** /**
* A {@link KeyManager} to track client certificate requests from servers. * A {@link KeyManager} to track client certificate requests from servers.
*/ */
@ -60,11 +59,9 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager {
mTrackingKeyManager = keyManager; mTrackingKeyManager = keyManager;
} }
public static EmailClientConnectionManager newInstance(Context context, HttpParams params, public static EmailClientConnectionManager newInstance(HttpParams params, boolean ssl,
HostAuth hostAuth) { int port) {
TrackingKeyManager keyManager = new TrackingKeyManager(); TrackingKeyManager keyManager = new TrackingKeyManager();
boolean ssl = hostAuth.shouldUseSsl();
int port = hostAuth.mPort;
// Create a registry for our three schemes; http and https will use built-in factories // Create a registry for our three schemes; http and https will use built-in factories
SchemeRegistry registry = new SchemeRegistry(); SchemeRegistry registry = new SchemeRegistry();
@ -72,11 +69,10 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager {
ssl ? STANDARD_PORT : port)); ssl ? STANDARD_PORT : port));
// Register https with the secure factory // Register https with the secure factory
registry.register(new Scheme("https", registry.register(new Scheme("https",
SSLUtils.getHttpSocketFactory(context, hostAuth, keyManager, false), SSLUtils.getHttpSocketFactory(false, keyManager), ssl ? port : STANDARD_SSL_PORT));
ssl ? port : STANDARD_SSL_PORT));
// Register the httpts scheme with our insecure factory // Register the httpts scheme with our insecure factory
registry.register(new Scheme("httpts", registry.register(new Scheme("httpts",
SSLUtils.getHttpSocketFactory(context, hostAuth, keyManager, true), SSLUtils.getHttpSocketFactory(true /*insecure*/, keyManager),
ssl ? port : STANDARD_SSL_PORT)); ssl ? port : STANDARD_SSL_PORT));
return new EmailClientConnectionManager(params, registry, keyManager); return new EmailClientConnectionManager(params, registry, keyManager);
@ -102,10 +98,11 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager {
} }
KeyManager keyManager = KeyManager keyManager =
KeyChainKeyManager.fromAlias(context, hostAuth.mClientCertAlias); KeyChainKeyManager.fromAlias(context, hostAuth.mClientCertAlias);
boolean insecure = hostAuth.shouldTrustAllServerCerts(); SSLCertificateSocketFactory underlying = SSLUtils.getSSLSocketFactory(
SSLSocketFactory ssf = hostAuth.shouldTrustAllServerCerts());
SSLUtils.getHttpSocketFactory(context, hostAuth, keyManager, insecure); underlying.setKeyManagers(new KeyManager[] { keyManager });
registry.register(new Scheme(schemeName, ssf, hostAuth.mPort)); registry.register(
new Scheme(schemeName, new SSLSocketFactory(underlying), hostAuth.mPort));
} }
} }

View File

@ -16,139 +16,46 @@
package com.android.emailcommon.utility; package com.android.emailcommon.utility;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.database.Cursor;
import android.net.SSLCertificateSocketFactory; import android.net.SSLCertificateSocketFactory;
import android.security.KeyChain; import android.security.KeyChain;
import android.security.KeyChainException; import android.security.KeyChainException;
import android.util.Log; import android.util.Log;
import com.android.emailcommon.provider.EmailContent.HostAuthColumns;
import com.android.emailcommon.provider.HostAuth;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
import java.security.Principal; import java.security.Principal;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Arrays; import java.util.Arrays;
import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedKeyManager; import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509TrustManager;
public class SSLUtils { public class SSLUtils {
// All secure factories are the same; all insecure factories are associated with HostAuth's private static SSLCertificateSocketFactory sInsecureFactory;
private static SSLCertificateSocketFactory sSecureFactory; private static SSLCertificateSocketFactory sSecureFactory;
private static final boolean LOG_ENABLED = false; private static final boolean LOG_ENABLED = false;
private static final String TAG = "Email.Ssl"; private static final String TAG = "Email.Ssl";
/**
* A trust manager specific to a particular HostAuth. The first time a server certificate is
* encountered for the HostAuth, its certificate is saved; subsequent checks determine whether
* the PublicKey of the certificate presented matches that of the saved certificate
* TODO: UI to ask user about changed certificates
*/
private static class SameCertificateCheckingTrustManager implements X509TrustManager {
private final HostAuth mHostAuth;
private final Context mContext;
// The public key associated with the HostAuth; we'll lazily initialize it
private PublicKey mPublicKey;
SameCertificateCheckingTrustManager(Context context, HostAuth hostAuth) {
mContext = context;
mHostAuth = hostAuth;
// We must load the server cert manually (the ContentCache won't handle blobs
Cursor c = context.getContentResolver().query(HostAuth.CONTENT_URI,
new String[] {HostAuthColumns.SERVER_CERT}, HostAuth.ID + "=?",
new String[] {Long.toString(hostAuth.mId)}, null);
if (c != null) {
try {
if (c.moveToNext()) {
mHostAuth.mServerCert = c.getBlob(0);
}
} finally {
c.close();
}
}
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
// We don't check client certificates
throw new CertificateException("We don't check client certificates");
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
if (chain.length == 0) {
throw new CertificateException("No certificates?");
} else {
X509Certificate serverCert = chain[0];
if (mHostAuth.mServerCert != null) {
// Compare with the current public key
if (mPublicKey == null) {
ByteArrayInputStream bais = new ByteArrayInputStream(mHostAuth.mServerCert);
Certificate storedCert =
CertificateFactory.getInstance("X509").generateCertificate(bais);
mPublicKey = storedCert.getPublicKey();
try {
bais.close();
} catch (IOException e) {
// Yeah, right.
}
}
if (!mPublicKey.equals(serverCert.getPublicKey())) {
throw new CertificateException(
"PublicKey has changed since initial connection!");
}
} else {
// First time; save this away
byte[] encodedCert = serverCert.getEncoded();
mHostAuth.mServerCert = encodedCert;
ContentValues values = new ContentValues();
values.put(HostAuthColumns.SERVER_CERT, encodedCert);
mContext.getContentResolver().update(
ContentUris.withAppendedId(HostAuth.CONTENT_URI, mHostAuth.mId),
values, null, null);
}
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
/** /**
* Returns a {@link javax.net.ssl.SSLSocketFactory}. * Returns a {@link javax.net.ssl.SSLSocketFactory}.
* Optionally bypass all SSL certificate checks. * Optionally bypass all SSL certificate checks.
* *
* @param insecure if true, bypass all SSL certificate checks * @param insecure if true, bypass all SSL certificate checks
*/ */
public synchronized static SSLCertificateSocketFactory getSSLSocketFactory(Context context, public synchronized static SSLCertificateSocketFactory getSSLSocketFactory(
HostAuth hostAuth, boolean insecure) { boolean insecure) {
if (insecure) { if (insecure) {
SSLCertificateSocketFactory insecureFactory = (SSLCertificateSocketFactory) if (sInsecureFactory == null) {
SSLCertificateSocketFactory.getDefault(0, null); sInsecureFactory = (SSLCertificateSocketFactory)
insecureFactory.setTrustManagers( SSLCertificateSocketFactory.getInsecure(0, null);
new TrustManager[] { }
new SameCertificateCheckingTrustManager(context, hostAuth)}); return sInsecureFactory;
return insecureFactory;
} else { } else {
if (sSecureFactory == null) { if (sSecureFactory == null) {
sSecureFactory = (SSLCertificateSocketFactory) sSecureFactory = (SSLCertificateSocketFactory)
@ -162,9 +69,8 @@ public class SSLUtils {
* Returns a {@link org.apache.http.conn.ssl.SSLSocketFactory SSLSocketFactory} for use with the * Returns a {@link org.apache.http.conn.ssl.SSLSocketFactory SSLSocketFactory} for use with the
* Apache HTTP stack. * Apache HTTP stack.
*/ */
public static SSLSocketFactory getHttpSocketFactory(Context context, HostAuth hostAuth, public static SSLSocketFactory getHttpSocketFactory(boolean insecure, KeyManager keyManager) {
KeyManager keyManager, boolean insecure) { SSLCertificateSocketFactory underlying = getSSLSocketFactory(insecure);
SSLCertificateSocketFactory underlying = getSSLSocketFactory(context, hostAuth, insecure);
if (keyManager != null) { if (keyManager != null) {
underlying.setKeyManagers(new KeyManager[] { keyManager }); underlying.setKeyManagers(new KeyManager[] { keyManager });
} }

View File

@ -52,6 +52,7 @@ import com.android.emailcommon.provider.EmailContent.AttachmentColumns;
import com.android.emailcommon.provider.EmailContent.HostAuthColumns; import com.android.emailcommon.provider.EmailContent.HostAuthColumns;
import com.android.emailcommon.provider.EmailContent.MailboxColumns; import com.android.emailcommon.provider.EmailContent.MailboxColumns;
import com.android.emailcommon.provider.EmailContent.Message; import com.android.emailcommon.provider.EmailContent.Message;
import com.android.emailcommon.provider.EmailContent.MessageColumns;
import com.android.emailcommon.provider.HostAuth; import com.android.emailcommon.provider.HostAuth;
import com.android.emailcommon.provider.Mailbox; import com.android.emailcommon.provider.Mailbox;
import com.android.emailcommon.provider.ProviderUnavailableException; import com.android.emailcommon.provider.ProviderUnavailableException;
@ -363,7 +364,6 @@ public class Utility {
cal.setTimeZone(TimeZone.getTimeZone("GMT")); cal.setTimeZone(TimeZone.getTimeZone("GMT"));
return cal; return cal;
} }
/** /**
* Generate a time in milliseconds from an email date string that represents a date/time in GMT * Generate a time in milliseconds from an email date string that represents a date/time in GMT
* @param date string in format 2010-02-23T16:00:00.000Z (ISO 8601, rfc3339) * @param date string in format 2010-02-23T16:00:00.000Z (ISO 8601, rfc3339)
@ -745,28 +745,25 @@ public class Utility {
return false; return false;
} else if (attachment.mContentBytes != null) { } else if (attachment.mContentBytes != null) {
return true; return true;
} else { } else if (TextUtils.isEmpty(attachment.mContentUri)) {
String contentUri = attachment.getContentUri(); return false;
if (TextUtils.isEmpty(contentUri)) { }
return false; try {
} Uri fileUri = Uri.parse(attachment.mContentUri);
try { try {
Uri fileUri = Uri.parse(contentUri); InputStream inStream = context.getContentResolver().openInputStream(fileUri);
try { try {
InputStream inStream = context.getContentResolver().openInputStream(fileUri); inStream.close();
try { } catch (IOException e) {
inStream.close(); // Nothing to be done if can't close the stream
} catch (IOException e) {
// Nothing to be done if can't close the stream
}
return true;
} catch (FileNotFoundException e) {
return false;
} }
} catch (RuntimeException re) { return true;
Log.w(Logging.LOG_TAG, "attachmentExists RuntimeException=" + re); } catch (FileNotFoundException e) {
return false; return false;
} }
} catch (RuntimeException re) {
Log.w(Logging.LOG_TAG, "attachmentExists RuntimeException=" + re);
return false;
} }
} }
@ -792,18 +789,8 @@ public class Utility {
Attachment.FLAG_DOWNLOAD_USER_REQUEST)) == 0) { Attachment.FLAG_DOWNLOAD_USER_REQUEST)) == 0) {
Log.d(Logging.LOG_TAG, "Unloaded attachment isn't marked for download: " + Log.d(Logging.LOG_TAG, "Unloaded attachment isn't marked for download: " +
att.mFileName + ", #" + att.mId); att.mFileName + ", #" + att.mId);
Account acct = Account.restoreAccountWithId(context, msg.mAccountKey);
if (acct == null) return true;
// If smart forward is set and the message is a forward, we'll act as though
// the attachment has been loaded
// In Email1 this test wasn't necessary, as the UI handled it...
if ((msg.mFlags & Message.FLAG_TYPE_FORWARD) != 0) {
if ((acct.mFlags & Account.FLAGS_SUPPORTS_SMART_FORWARD) != 0) {
continue;
}
}
Attachment.delete(context, Attachment.CONTENT_URI, att.mId); Attachment.delete(context, Attachment.CONTENT_URI, att.mId);
} else if (att.getContentUri() != null) { } else if (att.mContentUri != null) {
// In this case, the attachment file is gone from the cache; let's clear the // In this case, the attachment file is gone from the cache; let's clear the
// contentUri; this should be a very unusual case // contentUri; this should be a very unusual case
ContentValues cv = new ContentValues(); ContentValues cv = new ContentValues();
@ -1163,4 +1150,72 @@ public class Utility {
sb.append(')'); sb.append(')');
return sb.toString(); return sb.toString();
} }
/**
* Updates the last seen message key in the mailbox data base for the INBOX of the currently
* shown account. If the account is {@link Account#ACCOUNT_ID_COMBINED_VIEW}, the INBOX for
* all accounts are updated.
* @return an {@link EmailAsyncTask} for test only.
*/
public static EmailAsyncTask<Void, Void, Void> updateLastSeenMessageKey(final Context context,
final long accountId) {
return EmailAsyncTask.runAsyncParallel(new Runnable() {
private void updateLastSeenMessageKeyForAccount(long accountId) {
ContentResolver resolver = context.getContentResolver();
if (accountId == Account.ACCOUNT_ID_COMBINED_VIEW) {
Cursor c = resolver.query(
Account.CONTENT_URI, EmailContent.ID_PROJECTION, null, null, null);
if (c == null) throw new ProviderUnavailableException();
try {
while (c.moveToNext()) {
final long id = c.getLong(EmailContent.ID_PROJECTION_COLUMN);
updateLastSeenMessageKeyForAccount(id);
}
} finally {
c.close();
}
} else if (accountId > 0L) {
Mailbox mailbox =
Mailbox.restoreMailboxOfType(context, accountId, Mailbox.TYPE_INBOX);
// mailbox has been removed
if (mailbox == null) {
return;
}
// We use the highest _id for the account the mailbox table as the "last seen
// message key". We don't care if the message has been read or not. We only
// need a point at which we can compare against in the future. By setting this
// value, we are claiming that every message before this has potentially been
// seen by the user.
long messageId = Utility.getFirstRowLong(
context,
Message.CONTENT_URI,
EmailContent.ID_PROJECTION,
MessageColumns.MAILBOX_KEY + "=?",
new String[] { Long.toString(mailbox.mId) },
MessageColumns.ID + " DESC",
EmailContent.ID_PROJECTION_COLUMN, 0L);
long oldLastSeenMessageId = Utility.getFirstRowLong(
context, ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailbox.mId),
new String[] { MailboxColumns.LAST_SEEN_MESSAGE_KEY },
null, null, null, 0, 0L);
// Only update the db if the value has changed
if (messageId != oldLastSeenMessageId) {
ContentValues values = mailbox.toContentValues();
values.put(MailboxColumns.LAST_SEEN_MESSAGE_KEY, messageId);
resolver.update(
Mailbox.CONTENT_URI,
values,
EmailContent.ID_SELECTION,
new String[] { Long.toString(mailbox.mId) });
}
}
}
@Override
public void run() {
updateLastSeenMessageKeyForAccount(accountId);
}
});
}
} }

View File

@ -1,30 +0,0 @@
# Copyright 2012, 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.
LOCAL_PATH := $(call my-dir)
# Build the com.android.emailcommon static library. At the moment, this includes
# the emailcommon files themselves plus everything under src/org (apache code). All of our
# AIDL files are also compiled into the static library
include $(CLEAR_VARS)
LOCAL_MODULE := com.android.emailsync
LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/emailsync)
LOCAL_STATIC_JAVA_LIBRARIES := com.android.emailcommon2
LOCAL_SDK_VERSION := 14
include $(BUILD_STATIC_JAVA_LIBRARY)

View File

@ -1,307 +0,0 @@
/*
* Copyright (C) 2008-2009 Marc Blank
* Licensed to 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.emailsync;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.util.Log;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.HostAuth;
import com.android.emailcommon.provider.Mailbox;
import java.util.concurrent.LinkedBlockingQueue;
/**
* Base class for all protocol services SyncManager (extends Service, implements
* Runnable) instantiates subclasses to run a sync (either timed, or push, or
* mail placed in outbox, etc.) EasSyncService is currently implemented; my goal
* would be to move IMAP to this structure when it comes time to introduce push
* functionality.
*/
public abstract class AbstractSyncService implements Runnable {
public String TAG = "AbstractSyncService";
public static final int SECONDS = 1000;
public static final int MINUTES = 60*SECONDS;
public static final int HOURS = 60*MINUTES;
public static final int DAYS = 24*HOURS;
public static final int CONNECT_TIMEOUT = 30*SECONDS;
public static final int NETWORK_WAIT = 15*SECONDS;
public static final int EXIT_DONE = 0;
public static final int EXIT_IO_ERROR = 1;
public static final int EXIT_LOGIN_FAILURE = 2;
public static final int EXIT_EXCEPTION = 3;
public static final int EXIT_SECURITY_FAILURE = 4;
public static final int EXIT_ACCESS_DENIED = 5;
public Mailbox mMailbox;
protected long mMailboxId;
protected int mExitStatus = EXIT_EXCEPTION;
protected String mExitReason;
protected String mMailboxName;
public Account mAccount;
public Context mContext;
public int mChangeCount = 0;
public volatile int mSyncReason = 0;
protected volatile boolean mStop = false;
public volatile Thread mThread;
protected final Object mSynchronizer = new Object();
// Whether or not the sync service is valid (usable)
public boolean mIsValid = true;
public boolean mUserLog = true; // STOPSHIP
public boolean mFileLog = false;
protected volatile long mRequestTime = 0;
protected LinkedBlockingQueue<Request> mRequestQueue = new LinkedBlockingQueue<Request>();
/**
* Sent by SyncManager to request that the service stop itself cleanly
*/
public abstract void stop();
/**
* Sent by SyncManager to indicate that an alarm has fired for this service, and that its
* pending (network) operation has timed out. The service is NOT automatically stopped,
* although the behavior is service dependent.
*
* @return true if the operation was stopped normally; false if the thread needed to be
* interrupted.
*/
public abstract boolean alarm();
/**
* Sent by SyncManager to request that the service reset itself cleanly; the meaning of this
* operation is service dependent.
*/
public abstract void reset();
/**
* Called to validate an account; abstract to allow each protocol to do what
* is necessary. For consistency with the Email app's original
* functionality, success is indicated by a failure to throw an Exception
* (ugh). Parameters are self-explanatory
*
* @param hostAuth
* @return a Bundle containing a result code and, depending on the result, a PolicySet or an
* error message
*/
public abstract Bundle validateAccount(HostAuth hostAuth, Context context);
/**
* Called to clear the syncKey for the calendar associated with this service; this is necessary
* because changes to calendar sync state cause a reset of data.
*/
public abstract void resetCalendarSyncKey();
public AbstractSyncService(Context _context, Mailbox _mailbox) {
mContext = _context;
mMailbox = _mailbox;
mMailboxId = _mailbox.mId;
mMailboxName = _mailbox.mServerId;
mAccount = Account.restoreAccountWithId(_context, _mailbox.mAccountKey);
}
// Will be required when subclasses are instantiated by name
public AbstractSyncService(String prefix) {
}
/**
* The UI can call this static method to perform account validation. This method wraps each
* protocol's validateAccount method. Arguments are self-explanatory, except where noted.
*
* @param klass the protocol class (EasSyncService.class for example)
* @param hostAuth
* @param context
* @return a Bundle containing a result code and, depending on the result, a PolicySet or an
* error message
*/
public static Bundle validate(Class<? extends AbstractSyncService> klass,
HostAuth hostAuth, Context context) {
AbstractSyncService svc;
try {
svc = klass.newInstance();
return svc.validateAccount(hostAuth, context);
} catch (IllegalAccessException e) {
} catch (InstantiationException e) {
}
return null;
}
public static class ValidationResult {
static final int NO_FAILURE = 0;
static final int CONNECTION_FAILURE = 1;
static final int VALIDATION_FAILURE = 2;
static final int EXCEPTION = 3;
static final ValidationResult succeeded = new ValidationResult(true, NO_FAILURE, null);
boolean success;
int failure = NO_FAILURE;
String reason = null;
Exception exception = null;
ValidationResult(boolean _success, int _failure, String _reason) {
success = _success;
failure = _failure;
reason = _reason;
}
ValidationResult(boolean _success) {
success = _success;
}
ValidationResult(Exception e) {
success = false;
failure = EXCEPTION;
exception = e;
}
public boolean isSuccess() {
return success;
}
public String getReason() {
return reason;
}
}
public boolean isStopped() {
return mStop;
}
public Object getSynchronizer() {
return mSynchronizer;
}
/**
* Convenience methods to do user logging (i.e. connection activity). Saves a bunch of
* repetitive code.
*/
public void userLog(String string, int code, String string2) {
if (mUserLog) {
userLog(string + code + string2);
}
}
public void userLog(String string, int code) {
if (mUserLog) {
userLog(string + code);
}
}
public void userLog(String str, Exception e) {
if (mUserLog) {
Log.e(TAG, str, e);
} else {
Log.e(TAG, str + e);
}
if (mFileLog) {
FileLogger.log(e);
}
}
/**
* Standard logging for EAS.
* If user logging is active, we concatenate any arguments and log them using Log.d
* We also check for file logging, and log appropriately
* @param strings strings to concatenate and log
*/
public void userLog(String ...strings) {
if (mUserLog) {
String logText;
if (strings.length == 1) {
logText = strings[0];
} else {
StringBuilder sb = new StringBuilder(64);
for (String string: strings) {
sb.append(string);
}
logText = sb.toString();
}
Log.d(TAG, logText);
if (mFileLog) {
FileLogger.log(TAG, logText);
}
}
}
/**
* Error log is used for serious issues that should always be logged
* @param str the string to log
*/
public void errorLog(String str) {
Log.e(TAG, str);
if (mFileLog) {
FileLogger.log(TAG, str);
}
}
/**
* Waits for up to 10 seconds for network connectivity; returns whether or not there is
* network connectivity.
*
* @return whether there is network connectivity
*/
public boolean hasConnectivity() {
ConnectivityManager cm =
(ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
int tries = 0;
while (tries++ < 1) {
// Use the same test as in ExchangeService#waitForConnectivity
// TODO: Create common code for this test in emailcommon
NetworkInfo info = cm.getActiveNetworkInfo();
if (info != null) {
return true;
}
try {
Thread.sleep(10*SECONDS);
} catch (InterruptedException e) {
}
}
return false;
}
/**
* Request handling (common functionality)
* Can be overridden if desired
*/
public void addRequest(Request req) {
if (!mRequestQueue.contains(req)) {
mRequestQueue.offer(req);
}
}
public void removeRequest(Request req) {
mRequestQueue.remove(req);
}
public boolean hasPendingRequests() {
return !mRequestQueue.isEmpty();
}
public void clearRequests() {
mRequestQueue.clear();
}
}

View File

@ -1,111 +0,0 @@
/*
* Copyright (C) 2008-2009 Marc Blank
* Licensed to 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.emailsync;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.util.Log;
import com.android.emailcommon.provider.EmailContent.Message;
import com.android.emailcommon.provider.EmailContent.MessageColumns;
import com.android.emailcommon.provider.ProviderUnavailableException;
import java.util.ArrayList;
/**
* EmailSyncAlarmReceiver (USAR) is used by the SyncManager to start up-syncs of user-modified data
* back to the Exchange server.
*
* Here's how this works for Email, for example:
*
* 1) User modifies or deletes an email from the UI.
* 2) SyncManager, which has a ContentObserver watching the Message class, is alerted to a change
* 3) SyncManager sets an alarm (to be received by USAR) for a few seconds in the
* future (currently 15), the delay preventing excess syncing (think of it as a debounce mechanism).
* 4) ESAR Receiver's onReceive method is called
* 5) ESAR goes through all change and deletion records and compiles a list of mailboxes which have
* changes to be uploaded.
* 6) ESAR calls SyncManager to start syncs of those mailboxes
*
* If EmailProvider isn't available, the upsyncs will happen the next time ExchangeService starts
*
*/
public class EmailSyncAlarmReceiver extends BroadcastReceiver {
final String[] MAILBOX_DATA_PROJECTION = {MessageColumns.MAILBOX_KEY};
@Override
public void onReceive(final Context context, Intent intent) {
new Thread(new Runnable() {
public void run() {
handleReceive(context);
}
}).start();
}
private void handleReceive(Context context) {
ArrayList<Long> mailboxesToNotify = new ArrayList<Long>();
ContentResolver cr = context.getContentResolver();
// Get a selector for EAS accounts (we don't want to sync on changes to POP/IMAP messages)
String selector = SyncManager.getAccountSelector();
try {
// Find all of the deletions
Cursor c = cr.query(Message.DELETED_CONTENT_URI, MAILBOX_DATA_PROJECTION, selector,
null, null);
if (c == null) throw new ProviderUnavailableException();
try {
// Keep track of which mailboxes to notify; we'll only notify each one once
while (c.moveToNext()) {
long mailboxId = c.getLong(0);
if (!mailboxesToNotify.contains(mailboxId)) {
mailboxesToNotify.add(mailboxId);
}
}
} finally {
c.close();
}
// Now, find changed messages
c = cr.query(Message.UPDATED_CONTENT_URI, MAILBOX_DATA_PROJECTION, selector,
null, null);
if (c == null) throw new ProviderUnavailableException();
try {
// Keep track of which mailboxes to notify; we'll only notify each one once
while (c.moveToNext()) {
long mailboxId = c.getLong(0);
if (!mailboxesToNotify.contains(mailboxId)) {
mailboxesToNotify.add(mailboxId);
}
}
} finally {
c.close();
}
// Request service from the mailbox
for (Long mailboxId: mailboxesToNotify) {
SyncManager.serviceRequest(mailboxId, SyncManager.SYNC_UPSYNC);
}
} catch (ProviderUnavailableException e) {
Log.e("EmailSyncAlarmReceiver", "EmailProvider unavailable; aborting alarm receiver");
}
}
}

View File

@ -1,120 +0,0 @@
/*
* Copyright (C) 2009 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.emailsync;
import android.content.Context;
import android.os.Environment;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
public class FileLogger {
private static FileLogger LOGGER = null;
private static FileWriter sLogWriter = null;
public static String LOG_FILE_NAME =
Environment.getExternalStorageDirectory() + "/emaillog.txt";
public synchronized static FileLogger getLogger (Context c) {
LOGGER = new FileLogger();
return LOGGER;
}
private FileLogger() {
try {
sLogWriter = new FileWriter(LOG_FILE_NAME, true);
} catch (IOException e) {
// Doesn't matter
}
}
static public synchronized void close() {
if (sLogWriter != null) {
try {
sLogWriter.close();
} catch (IOException e) {
// Doesn't matter
}
sLogWriter = null;
}
}
static public synchronized void log(Exception e) {
if (sLogWriter != null) {
log("Exception", "Stack trace follows...");
PrintWriter pw = new PrintWriter(sLogWriter);
e.printStackTrace(pw);
pw.flush();
}
}
@SuppressWarnings("deprecation")
static public synchronized void log(String prefix, String str) {
if (LOGGER == null) {
LOGGER = new FileLogger();
log("Logger", "\r\n\r\n --- New Log ---");
}
Date d = new Date();
int hr = d.getHours();
int min = d.getMinutes();
int sec = d.getSeconds();
// I don't use DateFormat here because (in my experience), it's much slower
StringBuffer sb = new StringBuffer(256);
sb.append('[');
sb.append(hr);
sb.append(':');
if (min < 10)
sb.append('0');
sb.append(min);
sb.append(':');
if (sec < 10) {
sb.append('0');
}
sb.append(sec);
sb.append("] ");
if (prefix != null) {
sb.append(prefix);
sb.append("| ");
}
sb.append(str);
sb.append("\r\n");
String s = sb.toString();
if (sLogWriter != null) {
try {
sLogWriter.write(s);
sLogWriter.flush();
} catch (IOException e) {
// Something might have happened to the sdcard
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
// If the card is mounted and we can create the writer, retry
LOGGER = new FileLogger();
if (sLogWriter != null) {
try {
log("FileLogger", "Exception writing log; recreating...");
log(prefix, str);
} catch (Exception e1) {
// Nothing to do at this point
}
}
}
}
}
}
}

View File

@ -1,42 +0,0 @@
/*
* Copyright (C) 2008-2009 Marc Blank
* Licensed to 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.emailsync;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
/**
* MailboxAlarmReceiver is used to "wake up" the ExchangeService at the appropriate time(s). It may
* also be used for individual sync adapters, but this isn't implemented at the present time.
*
*/
public class MailboxAlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
long mailboxId = intent.getLongExtra("mailbox", SyncManager.EXTRA_MAILBOX_ID);
// EXCHANGE_SERVICE_MAILBOX_ID tells us that the service is asking to be started
if (mailboxId == SyncManager.SYNC_SERVICE_MAILBOX_ID) {
context.startService(new Intent(context, SyncManager.class));
} else {
SyncManager.alert(context, mailboxId);
}
}
}

View File

@ -1,42 +0,0 @@
/*
* 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.emailsync;
import com.android.emailsync.Request;
/**
* MessageMoveRequest is the EAS wrapper for requesting a "move to folder"
*/
public class MessageMoveRequest extends Request {
public final long mMailboxId;
public MessageMoveRequest(long messageId, long mailboxId) {
super(messageId);
mMailboxId = mailboxId;
}
// MessageMoveRequests are unique by their message id (i.e. it's meaningless to have two
// separate message moves queued at the same time)
public boolean equals(Object o) {
if (!(o instanceof MessageMoveRequest)) return false;
return ((MessageMoveRequest)o).mMessageId == mMessageId;
}
public int hashCode() {
return (int)mMessageId;
}
}

View File

@ -1,51 +0,0 @@
/*
* Copyright (C) 2008-2009 Marc Blank
* Licensed to 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.emailsync;
import com.android.emailcommon.provider.EmailContent.Attachment;
/**
* PartRequest is the wrapper for attachment loading requests. In addition to information about
* the attachment to be loaded, it also contains the callback to be used for status/progress
* updates to the UI.
*/
public class PartRequest extends Request {
public final Attachment mAttachment;
public final String mDestination;
public final String mContentUriString;
public final String mLocation;
public PartRequest(Attachment _att, String _destination, String _contentUriString) {
super(_att.mMessageKey);
mAttachment = _att;
mLocation = mAttachment.mLocation;
mDestination = _destination;
mContentUriString = _contentUriString;
}
// PartRequests are unique by their attachment id (i.e. multiple attachments might be queued
// for a particular message, but any individual attachment can only be loaded once)
public boolean equals(Object o) {
if (!(o instanceof PartRequest)) return false;
return ((PartRequest)o).mAttachment.mId == mAttachment.mId;
}
public int hashCode() {
return (int)mAttachment.mId;
}
}

View File

@ -1,36 +0,0 @@
/*
* 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.emailsync;
/**
* Requests for mailbox actions are handled by subclasses of this abstract class.
* Three subclasses are now defined: PartRequest (attachment load), MeetingResponseRequest
* (respond to a meeting invitation), and MessageMoveRequest (move a message to another folder)
*/
public abstract class Request {
public final long mTimeStamp = System.currentTimeMillis();
public final long mMessageId;
public Request(long messageId) {
mMessageId = messageId;
}
// Subclasses of Request may have different semantics regarding equality; therefore,
// we force them to implement the equals method
public abstract boolean equals(Object o);
public abstract int hashCode();
}

File diff suppressed because it is too large Load Diff

View File

@ -215,3 +215,8 @@
*** toByteArray(java.io.Reader, java.lang.String); *** toByteArray(java.io.Reader, java.lang.String);
*** toByteArray(java.lang.String); *** toByteArray(java.lang.String);
} }
-keepclasseswithmembers class com.android.email.activity.ThreePaneLayout {
*** setMessageListWidthAnim(...);
*** setMailboxListLeftAnim(...);
}

61
remove-exchange-support.sh Executable file
View File

@ -0,0 +1,61 @@
#!/bin/bash
#
# 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.
#
# You can remove exchange by running this script.
#
set -e # fail fast
# Step 0. Make sure we're in the right directory, and the user really wants it.
if [[ ! -d src/com/android/email/ ]] ; then
echo "Run the script in the root of the email source tree." 1>&2
exit 1
fi
echo ""
echo -n "Do you wish to remove exchange support from the email app? (y/N):"
read answer
if [[ "$answer" != y ]] ; then
echo "Aborted." 1>&2
exit 1
fi
# Step 1. Remove all Exchange related packages.
rm -fr src/com/android/exchange/ \
tests/src/com/android/exchange/
# Step 2. Remove lines surrounded by START-EXCHANGE and END-EXCHANGE
find . \( -name '*.java' -o -name '*.xml' -o -name 'Android.mk' \) -print0 |
xargs -0 sed -i "" -e '/EXCHANGE-REMOVE-SECTION-START/,/EXCHANGE-REMOVE-SECTION-END/d'
# Step 3. Remove all imports from com.android.exchange (and its subpackages).
find . -name '*.java' -print0 |
xargs -0 sed -i "" -e '/^import com\.android\.exchange/d'
echo ""
echo "Exchange support has been successfully removed."
exit 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1020 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 705 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 832 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 566 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 845 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 951 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 501 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 937 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 830 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 923 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 806 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 715 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 538 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Some files were not shown because too many files have changed in this diff Show More