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)
# 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
include $(CLEAR_VARS)
# Include res dir from chips, unified, and photoviewer
# Include res dir from chips
chips_dir := ../../../frameworks/ex/chips/res
unified_email_dir := ../UnifiedEmail
photo_dir := ../../../frameworks/ex/photoviewer/res
res_dir := $(chips_dir) res $(unified_email_dir)/res $(photo_dir) build/res
mail_common_dir := ../../../frameworks/opt/mailcommon/res
res_dir := $(chips_dir) $(mail_common_dir) res
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)
LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/email)
LOCAL_SRC_FILES += $(call all-java-files-under, src/com/beetstra)
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 += --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-support-v4
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v13
LOCAL_STATIC_JAVA_LIBRARIES := android-common com.android.emailcommon guava android-common-chips
LOCAL_PACKAGE_NAME := Email2
LOCAL_OVERRIDES_PACKAGES := Email
LOCAL_PACKAGE_NAME := 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)

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2012 The Android Open Source Project
<!-- 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.
@ -14,35 +13,56 @@
See the License for the specific language governing permissions and
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"/>
<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.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"/>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.email"
android:versionCode="410000"
android:versionName="4.1"
>
<!-- 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 -->
<permission
@ -58,27 +78,160 @@
android:name="android.permission.USE_CREDENTIALS"/>
<!-- Grant permission to system apps to access provider (see provider below) -->
<!-- STOPSHIP: Temporarily set protection level to "dangerous" (from "signature") -->
<permission
android:name="com.android.email.permission.ACCESS_PROVIDER"
android:protectionLevel="dangerous"
android:protectionLevel="signature"
android:label="@string/permission_access_provider_label"
android:description="@string/permission_access_provider_desc"/>
<uses-permission
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
android:icon="@mipmap/ic_launcher_mail"
android:icon="@mipmap/ic_launcher_email"
android:label="@string/app_name"
android:theme="@style/UnifiedEmailTheme"
android:hardwareAccelerated="true" >
<!-- Enable search in all activities -->
<meta-data android:name="android.app.default_searchable"
android:value="com.android.email2.ui.MailActivityEmail" />
android:name="Email"
android:theme="@style/EmailTheme"
android:hardwareAccelerated="false"
>
<activity
android:name="com.android.mail.compose.ComposeActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light">
android:name=".activity.Welcome"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
<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>
<action
android:name="android.intent.action.VIEW" />
@ -113,189 +266,15 @@
<action
android:name="com.android.email.intent.action.REPLY" />
</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>
<!-- Only used to support pre-HC shortcuts -->
<activity
android:name=".activity.EventViewer"
android:label="@string/app_name"
android:theme="@android:style/Theme.Holo.Light"
android:name=".activity.MessageList"
>
<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" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.APP_EMAIL" />
</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
@ -304,68 +283,6 @@
>
</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
android:name=".service.AttachmentDownloadService$Watchdog"
android:enabled="true"/>
@ -374,16 +291,14 @@
android:name=".service.EmailBroadcastReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.DEVICE_STORAGE_LOW" />
<action android:name="android.intent.action.DEVICE_STORAGE_OK" />
<action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED" />
</intent-filter>
<!-- To handle new message notifications -->
<intent-filter>
<action android:name="com.android.mail.action.update_notification"
android:priority="-10" />
<data android:mimeType="@string/application_mime_type" />
<action
android:name="android.intent.action.BOOT_COMPLETED" />
<action
android:name="android.intent.action.DEVICE_STORAGE_LOW" />
<action
android:name="android.intent.action.DEVICE_STORAGE_OK" />
<action
android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED" />
</intent-filter>
<!-- To handle secret code to activate the debug screen. -->
<intent-filter>
@ -415,6 +330,12 @@
<service
android:name=".service.MailService"
android:enabled="false"
>
</service>
<service
android:name=".Controller$ControllerService"
android:enabled="true"
>
</service>
@ -427,7 +348,7 @@
<!--Required stanza to register the PopImapAuthenticatorService with AccountManager -->
<service
android:name=".service.Pop3AuthenticatorService"
android:name=".service.PopImapAuthenticatorService"
android:exported="true"
android:enabled="true"
>
@ -437,46 +358,20 @@
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator_pop3"
/>
</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"
android:resource="@xml/pop_imap_authenticator"
/>
</service>
<!--Required stanza to register the PopImapSyncAdapterService with SyncManager -->
<service
android:name="com.android.email.service.Pop3SyncAdapterService"
android:name="com.android.email.service.PopImapSyncAdapterService"
android:exported="true">
<intent-filter>
<action
android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="@xml/syncadapter_pop3" />
</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" />
android:resource="@xml/syncadapter_pop_imap" />
</service>
<!-- Require provider permission to use our Policy and Account services -->
@ -502,28 +397,6 @@
</intent-filter>
</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 -->
<service
android:name=".service.EasAuthenticatorService"
@ -536,7 +409,7 @@
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator_eas"
android:resource="@xml/eas_authenticator"
/>
</service>
<!--Required stanza to register the EasTestAuthenticatorService with AccountManager -->
@ -572,49 +445,12 @@
android:resource="@xml/authenticator_alternate"
/>
</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
android:name=".provider.AttachmentProvider"
android:authorities="com.android.email.attachmentprovider"
android:multiprocess="true"
android:grantUriPermissions="true"
android:exported="true"
android:readPermission="com.android.email.permission.READ_ATTACHMENT"
/>
@ -624,45 +460,41 @@
android:name=".provider.EmailProvider"
android:authorities="com.android.email.provider;com.android.email.notifier"
android:multiprocess="true"
android:exported="true"
android:permission="com.android.email.permission.ACCESS_PROVIDER"
android:label="@string/app_name"
/>
<!-- Legacy authenticators, etc. can be added below. OEMs may remove these -->
<service
android:name=".service.LegacyEmailAuthenticatorService"
android:exported="false"
<!-- Email AppWidget definitions -->
<activity
android:name=".widget.WidgetConfiguration"
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>
<action
android:name="android.accounts.AccountAuthenticator" />
android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</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>
<action
android:name="android.accounts.AccountAuthenticator" />
<action android:name="com.android.email.MESSAGE_LIST_DATASET_CHANGED" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator_legacy_eas"
/>
</service>
android:name="android.appwidget.provider"
android:resource="@xml/widget_info" />
</receiver>
</application>
<!-- Legacy permissions, etc. can go here -->
</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, 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 $(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*)
$(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
# ************************************************

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)
unified_email_src_dir := ../../UnifiedEmail/src
apache_src_dir := ../../UnifiedEmail/src/org
unified_email_src_dir := ../src
apache_src_dir := ../src/org
imported_unified_email_files := \
$(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
LOCAL_MODULE := com.android.emailcommon2
LOCAL_MODULE := com.android.emailcommon
LOCAL_STATIC_JAVA_LIBRARIES := guava android-common
LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/emailcommon)
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, src)
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_SDK_VERSION := 14
LOCAL_SDK_VERSION := current
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");
* you may not use this file except in compliance with the License.
@ -14,10 +15,9 @@
* limitations under the License.
*/
package com.android.email.service;
package com.android.emailcommon;
/**
* This service needs to be declared separately from the base service
*/
public class LegacyImapAuthenticatorService extends AuthenticatorService {
public class AccountManagerTypes {
public static final String TYPE_EXCHANGE = "com.android.exchange";
public static final String TYPE_POP_IMAP = "com.android.email";
}

View File

@ -21,9 +21,8 @@ package com.android.emailcommon;
*
* Level 1: As shipped in HC/MR1
* Level 2: Adds searchMessages to EmailService
* Level 3: Adds capabilities query
*
*/
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");
* you may not use this file except in compliance with the License.
@ -14,10 +14,12 @@
* 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 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
*
* Example usage:
* TrafficStats.setThreadStatsTag(accountId | DATA_EMAIL | REASON_SYNC);
* TrafficStats.setThreadStatsTag(accountId | PROTOCOL_IMAP | DATA_EMAIL | REASON_SYNC);
*/
public class TrafficFlags {
// Bits 0->15, account id
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)
private static final int DATA_SHIFT = 18;
private static final int DATA_MASK = 3 << DATA_SHIFT;
@ -56,7 +68,8 @@ public class TrafficFlags {
* @return flags for syncing this 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
*/
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
*/
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) {
@ -87,6 +101,8 @@ public class TrafficFlags {
sb.append(flags & ACCOUNT_MASK);
sb.append(',');
sb.append(REASONS[(flags & REASON_MASK) >> REASON_SHIFT]);
sb.append(',');
sb.append(PROTOCOLS[(flags & PROTOCOL_MASK) >> PROTOCOL_SHIFT]);
int maskedData = flags & DATA_MASK;
if (maskedData != 0) {
sb.append(',');

View File

@ -67,7 +67,6 @@ public class MimeMessage extends Message {
private Body mBody;
protected int mSize;
private boolean mInhibitLocalMessageId = false;
private boolean mComplete = true;
// Shared random source for generating local message-id values
private static final java.util.Random sRandom = new java.util.Random();
@ -120,7 +119,7 @@ public class MimeMessage extends Message {
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
// the new incoming message.
getMimeHeaders().clear();
@ -135,20 +134,7 @@ public class MimeMessage extends Message {
MimeStreamParser parser = new MimeStreamParser();
parser.setContentHandler(new MimeMessageBuilder());
return parser;
}
protected void parse(InputStream in) throws IOException, MessagingException {
MimeStreamParser parser = init();
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 {
return MimeUtility.getHeaderParameter(getContentType(), null);
}

View File

@ -16,7 +16,6 @@
package com.android.emailcommon.internet;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Base64DataException;
import android.util.Base64InputStream;
@ -407,12 +406,33 @@ public class MimeUtility {
public static void collectParts(Part part, ArrayList<Part> viewables,
ArrayList<Part> attachments) throws MessagingException {
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"
boolean inline =
TextUtils.isEmpty(dispositionType) || "inline".equalsIgnoreCase(dispositionType);
// The lower-case mime type
String mimeType = part.getMimeType().toLowerCase();
boolean inlineDisposition = dispositionType == null
|| "inline".equalsIgnoreCase(dispositionType);
// 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 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.
Message message = (Message)part.getBody();
collectParts(message, viewables, attachments);
} else if (inline && (mimeType.startsWith("text") || (mimeType.startsWith("image")))) {
// We'll treat text and images as viewables
} else if ((!attachmentOrInline) && ("text/html".equalsIgnoreCase(part.getMimeType()))) {
// If the part is HTML and we got this far, it's a viewable part of a mixed
viewables.add(part);
} else {
// Everything else is an attachment.
} else if ((!attachmentOrInline) && ("text/plain".equalsIgnoreCase(part.getMimeType()))) {
// 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);
}
}

View File

@ -16,8 +16,12 @@
package com.android.emailcommon.internet;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.text.Html;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Base64OutputStream;
@ -38,9 +42,7 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -50,17 +52,28 @@ import java.util.regex.Pattern;
*/
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
// "Jan", not the other localized format like "Ene" (meaning January in locale es).
private static final SimpleDateFormat DATE_FORMAT =
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 */
private static final Pattern BODY_PATTERN = Pattern.compile(
"(?:<\\s*body[^>]*>)(.*)(?:<\\s*/\\s*body\\s*>)",
Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
/** Match group in {@code BODDY_PATTERN} for the body HTML */
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 */
private final static int INDEX_BODY_TEXT = 0;
/** 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.
*/
/*package*/ static String[] buildBodyText(Body body, int flags, boolean useSmartReply) {
String[] messageBody = new String[] { null, null };
if (body == null) {
return new String[2];
return messageBody;
}
String[] messageBody = new String[] { body.mTextContent, body.mHtmlContent };
if (useSmartReply && body.mQuotedTextStartPos > 0) {
if (messageBody[0] != null) {
messageBody[0] = messageBody[0].substring(0, body.mQuotedTextStartPos);
} else if (messageBody[1] != null) {
messageBody[1] = messageBody[1].substring(0, body.mQuotedTextStartPos);
String text = body.mTextContent;
boolean isReply = (flags & Message.FLAG_TYPE_REPLY) != 0;
boolean isForward = (flags & Message.FLAG_TYPE_FORWARD) != 0;
// For all forwards/replies, we add the intro text
if (isReply || isForward) {
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;
}
@ -108,16 +179,9 @@ public class Rfc822Output {
* @param messageId the message to write out
* @param out the output stream to write the message to
* @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,
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);
if (message == null) {
// throw something?
@ -152,53 +216,59 @@ public class Rfc822Output {
Body body = Body.restoreBodyWithMessageId(context, message.mId);
String[] bodyText = buildBodyText(body, message.mFlags, useSmartReply);
// If a list of attachments hasn't been passed in, build one from the message
if (attachments == null) {
attachments =
Arrays.asList(Attachment.restoreAttachmentsWithMessageId(context, messageId));
}
Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, messageId);
Cursor attachmentsCursor = context.getContentResolver().query(uri,
Attachment.CONTENT_PROJECTION, WHERE_NOT_SMART_FORWARD, null, null);
boolean multipart = attachments.size() > 0;
String multipartBoundary = null;
String multipartType = "mixed";
try {
int attachmentCount = attachmentsCursor.getCount();
boolean multipart = attachmentCount > 0;
String multipartBoundary = null;
String multipartType = "mixed";
// Simplified case for no multipart - just emit text and be done.
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);
// Simplified case for no multipart - just emit text and be done.
if (!multipart) {
writeTextWithHeaders(writer, stream, bodyText);
}
} else {
// continue with multipart headers, then into multipart body
multipartBoundary = getNextBoundary();
// Write out the attachments until we run out
for (Attachment att: attachments) {
writeBoundary(writer, multipartBoundary, false);
writeOneAttachment(context, writer, stream, att);
// Move to the first attachment; this must succeed because multipart is true
attachmentsCursor.moveToFirst();
if (attachmentCount == 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 = 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");
}
// end of multipart section
writeBoundary(writer, multipartBoundary, true);
// first multipart element is the body
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();
@ -234,7 +304,7 @@ public class Rfc822Output {
inStream = new ByteArrayInputStream(attachment.mContentBytes);
} else {
// try to open the file
Uri fileUri = Uri.parse(attachment.getContentUri());
Uri fileUri = Uri.parse(attachment.mContentUri);
inStream = context.getContentResolver().openInputStream(fileUri);
}
// 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
* 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)
throws IOException {
boolean html = false;
String text = bodyText[INDEX_BODY_TEXT];
if (text == null) {
text = bodyText[INDEX_BODY_HTML];
html = true;
}
String html = bodyText[INDEX_BODY_HTML];
if (text == null) {
writer.write("\r\n"); // a truly empty message
} 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
String mimeType = "text/" + (html ? "html" : "plain");
writeHeader(writer, "Content-Type", mimeType + "; charset=utf-8");
writeHeader(writer, "Content-Type", "text/plain; charset=utf-8");
writeHeader(writer, "Content-Transfer-Encoding", "base64");
writer.write("\r\n");
byte[] textBytes = text.getBytes("UTF-8");
writer.flush();
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 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.
/**
@ -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
// if FLAGS_SUPPORTS_SEARCH is true
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)
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_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 mEmailAddress;
public String mSyncKey;
@ -134,6 +128,10 @@ public final class Account extends EmailContent implements AccountColumns, Parce
public String mSignature;
public long mPolicyKey;
// For compatibility with Email1
public long mNotifiedMessageId;
public int mNotifiedMessageCount;
// Convenience for creating/working with an account
public transient HostAuth mHostAuthRecv;
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_SIGNATURE_COLUMN = 16;
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[] {
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.RINGTONE_URI, AccountColumns.PROTOCOL_VERSION,
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;
@ -200,6 +201,13 @@ public final class Account extends EmailContent implements AccountColumns, Parce
MailboxColumns.TYPE + " = " + Mailbox.TYPE_INBOX +
" 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
*/
@ -266,6 +274,8 @@ public final class Account extends EmailContent implements AccountColumns, Parce
mSecuritySyncKey = cursor.getString(CONTENT_SECURITY_SYNC_KEY_COLUMN);
mSignature = cursor.getString(CONTENT_SIGNATURE_COLUMN);
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) {
@ -453,6 +463,21 @@ public final class Account extends EmailContent implements AccountColumns, Parce
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".
*/
@ -502,7 +527,7 @@ public final class Account extends EmailContent implements AccountColumns, Parce
public static long getAccountIdFromShortcutSafeUri(Context context, Uri uri) {
// Make sure the URI is in the correct format.
if (!"content".equals(uri.getScheme())
|| !EmailContent.AUTHORITY.equals(uri.getAuthority())) {
|| !AUTHORITY.equals(uri.getAuthority())) {
return -1;
}
@ -706,7 +731,7 @@ public final class Account extends EmailContent implements AccountColumns, Parce
.newUpdate(ContentUris.withAppendedId(CONTENT_URI, mId))
.withValues(cv).build());
try {
context.getContentResolver().applyBatch(EmailContent.AUTHORITY, ops);
context.getContentResolver().applyBatch(AUTHORITY, ops);
return 1;
} catch (RemoteException e) {
// 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 {
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 (recvIndex >= 0) {
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.SIGNATURE, mSignature);
values.put(AccountColumns.POLICY_KEY, mPolicyKey);
values.put(AccountColumns.NOTIFIED_MESSAGE_ID, mNotifiedMessageId);
values.put(AccountColumns.NOTIFIED_MESSAGE_COUNT, mNotifiedMessageCount);
return values;
}

View File

@ -29,7 +29,6 @@ import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;
import com.android.emailcommon.utility.TextUtilities;
import com.android.emailcommon.utility.Utility;
@ -60,12 +59,31 @@ import java.util.ArrayList;
*
*/
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 =
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_UNREAD_COUNT_COLUMN = 1;
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
public static final String RECORD_ID = "_id";
@ -113,57 +131,6 @@ public abstract class EmailContent {
// Read the Content from a ContentCursor
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
public Uri getUri() {
if (mUri == null) {
@ -293,17 +260,14 @@ public abstract class EmailContent {
// The plain text content itself
public static final String TEXT_CONTENT = "textContent";
// Replied-to or forwarded body (in html form)
@Deprecated
public static final String HTML_REPLY = "htmlReply";
// Replied-to or forwarded body (in text form)
@Deprecated
public static final String TEXT_REPLY = "textReply";
// 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
// deleted safely (i.e. isn't referenced by other messages)
public static final String SOURCE_MESSAGE_KEY = "sourceMessageKey";
// The text to be placed between a reply/forward response and the original message
@Deprecated
public static final String INTRO_TEXT = "introText";
// The start of quoted text within our text content
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 String TABLE_NAME = "Body";
public static Uri CONTENT_URI;
public static void initBody() {
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/body");
}
@SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/body");
public static final int CONTENT_ID_COLUMN = 0;
public static final int CONTENT_MESSAGE_KEY_COLUMN = 1;
public static final int CONTENT_HTML_CONTENT_COLUMN = 2;
public static final int CONTENT_TEXT_CONTENT_COLUMN = 3;
@Deprecated
public static final int CONTENT_HTML_REPLY_COLUMN = 4;
@Deprecated
public static final int CONTENT_TEXT_REPLY_COLUMN = 5;
public static final int CONTENT_SOURCE_KEY_COLUMN = 6;
@Deprecated
public static final int CONTENT_INTRO_TEXT_COLUMN = 7;
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[] {
RECORD_ID, BodyColumns.HTML_CONTENT
};
@Deprecated
public static final String[] COMMON_PROJECTION_REPLY_TEXT = new String[] {
RECORD_ID, BodyColumns.TEXT_REPLY
};
@Deprecated
public static final String[] COMMON_PROJECTION_REPLY_HTML = new String[] {
RECORD_ID, BodyColumns.HTML_REPLY
};
@Deprecated
public static final String[] COMMON_PROJECTION_INTRO = new String[] {
RECORD_ID, BodyColumns.INTRO_TEXT
};
public static final String[] COMMON_PROJECTION_SOURCE = new String[] {
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 =
new String[] { BodyColumns.SOURCE_MESSAGE_KEY };
@ -366,9 +321,7 @@ public abstract class EmailContent {
public long mMessageKey;
public String mHtmlContent;
public String mTextContent;
@Deprecated
public String mHtmlReply;
@Deprecated
public String mTextReply;
public int mQuotedTextStartPos;
@ -378,7 +331,6 @@ public abstract class EmailContent {
* want to include quoted text.
*/
public long mSourceKey;
@Deprecated
public String mIntroText;
public Body() {
@ -493,17 +445,14 @@ public abstract class EmailContent {
return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_HTML);
}
@Deprecated
public static String restoreReplyTextWithMessageId(Context context, long messageId) {
return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_REPLY_TEXT);
}
@Deprecated
public static String restoreReplyHtmlWithMessageId(Context context, long messageId) {
return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_REPLY_HTML);
}
@Deprecated
public static String restoreIntroTextWithMessageId(Context context, long messageId) {
return restoreTextWithMessageId(context, messageId, Body.COMMON_PROJECTION_INTRO);
}
@ -548,8 +497,8 @@ public abstract class EmailContent {
public static final String FLAGS = "flags";
// Sync related identifiers
// Saved draft info (reusing the never-used "clientId" column)
public static final String DRAFT_INFO = "clientId";
// Any client-required identifier
public static final String CLIENT_ID = "clientId";
// The message-id in the message's header
public static final String MESSAGE_ID = "messageId";
@ -576,8 +525,6 @@ public abstract class EmailContent {
public static final String PROTOCOL_SEARCH_INFO = "protocolSearchInfo";
// Simple thread topic
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 {
@ -586,28 +533,17 @@ public abstract class EmailContent {
public static final String DELETED_TABLE_NAME = "Message_Deletes";
// To refer to a specific message, use ContentUris.withAppendedId(CONTENT_URI, id)
public static Uri CONTENT_URI;
public static Uri CONTENT_URI_LIMIT_1;
public static Uri SYNCED_CONTENT_URI;
public static Uri SELECTED_MESSAGE_CONTENT_URI ;
public static Uri DELETED_CONTENT_URI;
public static Uri UPDATED_CONTENT_URI;
public static Uri NOTIFIER_URI;
public static void initMessage() {
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/message");
CONTENT_URI_LIMIT_1 = uriWithLimit(CONTENT_URI, 1);
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");
}
@SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/message");
public static final Uri CONTENT_URI_LIMIT_1 = uriWithLimit(CONTENT_URI, 1);
public static final Uri SYNCED_CONTENT_URI =
Uri.parse(EmailContent.CONTENT_URI + "/syncedMessage");
public static final Uri DELETED_CONTENT_URI =
Uri.parse(EmailContent.CONTENT_URI + "/deletedMessage");
public static final Uri UPDATED_CONTENT_URI =
Uri.parse(EmailContent.CONTENT_URI + "/updatedMessage");
public static final Uri NOTIFIER_URI =
Uri.parse(EmailContent.CONTENT_NOTIFIER_URI + "/message");
public static final String KEY_TIMESTAMP_DESC = MessageColumns.TIMESTAMP + " desc";
@ -621,7 +557,7 @@ public abstract class EmailContent {
public static final int CONTENT_FLAG_ATTACHMENT_COLUMN = 7;
public static final int CONTENT_FLAGS_COLUMN = 8;
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_MAILBOX_KEY_COLUMN = 12;
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_PROTOCOL_SEARCH_INFO_COLUMN = 22;
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[] {
RECORD_ID,
@ -643,14 +578,14 @@ public abstract class EmailContent {
MessageColumns.SUBJECT, MessageColumns.FLAG_READ,
MessageColumns.FLAG_LOADED, MessageColumns.FLAG_FAVORITE,
MessageColumns.FLAG_ATTACHMENT, MessageColumns.FLAGS,
SyncColumns.SERVER_ID, MessageColumns.DRAFT_INFO,
SyncColumns.SERVER_ID, MessageColumns.CLIENT_ID,
MessageColumns.MESSAGE_ID, MessageColumns.MAILBOX_KEY,
MessageColumns.ACCOUNT_KEY, MessageColumns.FROM_LIST,
MessageColumns.TO_LIST, MessageColumns.CC_LIST,
MessageColumns.BCC_LIST, MessageColumns.REPLY_TO_LIST,
SyncColumns.SERVER_TIMESTAMP, MessageColumns.MEETING_INFO,
MessageColumns.SNIPPET, MessageColumns.PROTOCOL_SEARCH_INFO,
MessageColumns.THREAD_TOPIC, MessageColumns.SYNC_DATA
MessageColumns.THREAD_TOPIC
};
public static final int LIST_ID_COLUMN = 0;
@ -766,7 +701,7 @@ public abstract class EmailContent {
public String mServerId;
public long mServerTimeStamp;
public int mDraftInfo;
public String mClientId;
public String mMessageId;
public long mMailboxKey;
@ -787,8 +722,6 @@ public abstract class EmailContent {
public String mThreadTopic;
public String mSyncData;
/**
* Base64-encoded representation of the byte array provided by servers for identifying
* 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_PARTIAL = 2;
public static final int FLAG_LOADED_DELETED = 3;
public static final int FLAG_LOADED_UNKNOWN = 4;
// Bits used in mFlags
// The following three states are mutually exclusive, and indicate whether the message is an
@ -858,10 +790,6 @@ public abstract class EmailContent {
// compatibility
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". */
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_ATTACHMENT, mFlagAttachment);
values.put(MessageColumns.FLAGS, mFlags);
values.put(SyncColumns.SERVER_ID, mServerId);
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.MAILBOX_KEY, mMailboxKey);
values.put(MessageColumns.ACCOUNT_KEY, mAccountKey);
values.put(MessageColumns.FROM_LIST, mFrom);
values.put(MessageColumns.TO_LIST, mTo);
values.put(MessageColumns.CC_LIST, mCc);
values.put(MessageColumns.BCC_LIST, mBcc);
values.put(MessageColumns.REPLY_TO_LIST, mReplyTo);
values.put(MessageColumns.MEETING_INFO, mMeetingInfo);
values.put(MessageColumns.SNIPPET, mSnippet);
values.put(MessageColumns.PROTOCOL_SEARCH_INFO, mProtocolSearchInfo);
values.put(MessageColumns.THREAD_TOPIC, mThreadTopic);
values.put(MessageColumns.SYNC_DATA, mSyncData);
return values;
}
@ -920,7 +854,7 @@ public abstract class EmailContent {
mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN);
mServerId = cursor.getString(CONTENT_SERVER_ID_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);
mMailboxKey = cursor.getLong(CONTENT_MAILBOX_KEY_COLUMN);
mAccountKey = cursor.getLong(CONTENT_ACCOUNT_KEY_COLUMN);
@ -933,7 +867,6 @@ public abstract class EmailContent {
mSnippet = cursor.getString(CONTENT_SNIPPET_COLUMN);
mProtocolSearchInfo = cursor.getString(CONTENT_PROTOCOL_SEARCH_INFO_COLUMN);
mThreadTopic = cursor.getString(CONTENT_THREAD_TOPIC_COLUMN);
mSyncData = cursor.getString(CONTENT_SYNC_DATA_COLUMN);
}
public boolean update() {
@ -1030,31 +963,37 @@ public abstract class EmailContent {
if (mHtml != null) {
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) {
cv.put(Body.SOURCE_MESSAGE_KEY, mSourceKey);
}
if (mIntroText != null) {
cv.put(Body.INTRO_TEXT, mIntroText);
}
if (mQuotedTextStartPos != 0) {
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
int messageBackValue = ops.size() - 1;
// Only create a body if we've got some data
if (!cv.keySet().isEmpty()) {
b = ContentProviderOperation.newInsert(Body.CONTENT_URI);
// Put our message id in the Body
if (!isNew) {
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());
// 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());
// Create the attaachments, if any
if (mAttachments != null) {
@ -1181,30 +1120,17 @@ public abstract class EmailContent {
public static final class Attachment extends EmailContent
implements AttachmentColumns, Parcelable {
public static final String TABLE_NAME = "Attachment";
public static final String ATTACHMENT_PROVIDER_LEGACY_URI_PREFIX =
"content://com.android.email.attachmentprovider";
public static Uri CONTENT_URI;
@SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/attachment");
// This must be used with an appended id: ContentUris.withAppendedId(MESSAGE_ID_URI, id)
public static Uri MESSAGE_ID_URI;
public static String ATTACHMENT_PROVIDER_URI_PREFIX;
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 static final Uri MESSAGE_ID_URI = Uri.parse(
EmailContent.CONTENT_URI + "/attachment/message");
public String mFileName;
public String mMimeType;
public long mSize;
public String mContentId;
private String mContentUri;
public String mContentUri;
public long mMessageKey;
public String mLocation;
public String mEncoding;
@ -1280,31 +1206,6 @@ public abstract class EmailContent {
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
* @param context
@ -1534,6 +1435,10 @@ public abstract class EmailContent {
public static final String SIGNATURE = "signature";
// A foreign key into the Policy table
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 {
@ -1591,8 +1496,8 @@ public abstract class EmailContent {
public static final String LAST_NOTIFIED_MESSAGE_COUNT = "lastNotifiedMessageCount";
// The total number of messages in the remote mailbox
public static final String TOTAL_COUNT = "totalCount";
// The full hierarchical name of this folder, in the form a/b/c
public static final String HIERARCHICAL_NAME = "hierarchicalName";
// For compatibility with Email1
public static final String LAST_SEEN_MESSAGE_KEY = "lastSeenMessageKey";
}
public interface HostAuthColumns {
@ -1615,8 +1520,6 @@ public abstract class EmailContent {
static final String CLIENT_CERT_ALIAS = "certAlias";
// DEPRECATED - Will not be set or stored
static final String ACCOUNT_KEY = "accountKey";
// A blob containing an X509 server certificate
static final String SERVER_CERT = "serverCert";
}
public interface PolicyColumns {

View File

@ -34,15 +34,14 @@ import java.net.URISyntaxException;
public final class HostAuth extends EmailContent implements HostAuthColumns, Parcelable {
public static final String TABLE_NAME = "HostAuth";
public static Uri CONTENT_URI;
public static void initHostAuth() {
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/hostauth");
}
// These legacy constants should be used in code created prior to Email2
public static final String LEGACY_SCHEME_SMTP = "smtp";
@SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/hostauth");
// TODO the three following constants duplicate constants in Store.java; remove those and
// just reference these.
public static final String SCHEME_IMAP = "imap";
public static final String SCHEME_POP3 = "pop3";
public static final String SCHEME_EAS = "eas";
public static final String SCHEME_SMTP = "smtp";
public static final String SCHEME_TRUST_ALL_CERTS = "trustallcerts";
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 mDomain;
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_PROTOCOL_COLUMN = 1;
@ -272,7 +269,19 @@ public final class HostAuth extends EmailContent implements HostAuthColumns, Par
mPort = port;
if (mPort == PORT_UNKNOWN) {
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;
}
}
@ -280,6 +289,10 @@ public final class HostAuth extends EmailContent implements HostAuthColumns, Par
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. */
public boolean shouldUseSsl() {
@ -355,7 +368,6 @@ public final class HostAuth extends EmailContent implements HostAuthColumns, Par
}
HostAuth that = (HostAuth)o;
return mPort == that.mPort
&& mId == that.mId
&& mFlags == that.mFlags
&& Utility.areStringsEqual(mProtocol, that.mProtocol)
&& 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(mDomain, that.mDomain)
&& 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 static final String TABLE_NAME = "Mailbox";
public static Uri CONTENT_URI;
public static Uri ADD_TO_FIELD_URI;
public static Uri FROM_ACCOUNT_AND_TYPE_URI;
public static void initMailbox() {
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");
}
@SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/mailbox");
public static final Uri ADD_TO_FIELD_URI =
Uri.parse(EmailContent.CONTENT_URI + "/mailboxIdAddToField");
public static final Uri FROM_ACCOUNT_AND_TYPE_URI =
Uri.parse(EmailContent.CONTENT_URI + "/mailboxIdFromAccountAndType");
public String mDisplayName;
public String mServerId;
@ -66,7 +61,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
public long mLastNotifiedMessageKey;
public int mLastNotifiedMessageCount;
public int mTotalCount;
public String mHierarchicalName;
public long mLastSeenMessageKey;
public static final int CONTENT_ID_COLUMN = 0;
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_COUNT_COLUMN = 20;
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()}
@ -105,7 +100,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
MailboxColumns.SYNC_STATUS, MailboxColumns.PARENT_KEY, MailboxColumns.LAST_TOUCHED_TIME,
MailboxColumns.UI_SYNC_STATUS, MailboxColumns.UI_LAST_SYNC_RESULT,
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 =
@ -337,7 +332,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
mLastNotifiedMessageKey = cursor.getLong(CONTENT_LAST_NOTIFIED_MESSAGE_KEY_COLUMN);
mLastNotifiedMessageCount = cursor.getInt(CONTENT_LAST_NOTIFIED_MESSAGE_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
@ -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_COUNT, mLastNotifiedMessageCount);
values.put(MailboxColumns.TOTAL_COUNT, mTotalCount);
values.put(MailboxColumns.HIERARCHICAL_NAME, mHierarchicalName);
values.put(MailboxColumns.LAST_SEEN_MESSAGE_KEY, mLastSeenMessageKey);
return values;
}
@ -497,9 +492,43 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
case TYPE_MAIL:
case TYPE_TRASH:
case TYPE_JUNK:
case TYPE_SENT:
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;
hash[CONTENT_TOTAL_COUNT_COLUMN]
= mTotalCount;
hash[CONTENT_HIERARCHICAL_NAME_COLUMN]
= mHierarchicalName;
return hash;
}
@ -597,7 +624,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
dest.writeLong(mLastNotifiedMessageKey);
dest.writeInt(mLastNotifiedMessageCount);
dest.writeInt(mTotalCount);
dest.writeString(mHierarchicalName);
dest.writeLong(mLastSeenMessageKey);
}
public Mailbox(Parcel in) {
@ -624,7 +651,7 @@ public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns
mLastNotifiedMessageKey = in.readLong();
mLastNotifiedMessageCount = in.readInt();
mTotalCount = in.readInt();
mHierarchicalName = in.readString();
mLastSeenMessageKey = in.readLong();
}
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 TABLE_NAME = "Policy";
public static Uri CONTENT_URI;
public static void initPolicy() {
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/policy");
}
@SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/policy");
/* Convert days to mSec (used for password expiration) */
private static final long DAYS_TO_MSEC = 24 * 60 * 60 * 1000;

View File

@ -17,6 +17,10 @@
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.ContentValues;
import android.content.Context;
@ -25,9 +29,6 @@ import android.net.Uri;
import android.os.Parcel;
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 message. Tied to a specific account.
@ -35,13 +36,11 @@ import com.google.common.base.Objects;
public final class QuickResponse extends EmailContent
implements QuickResponseColumns, Parcelable {
public static final String TABLE_NAME = "QuickResponse";
public static Uri CONTENT_URI;
public static Uri ACCOUNT_ID_URI;
public static void initQuickResponse() {
CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/quickresponse");
ACCOUNT_ID_URI = Uri.parse(EmailContent.CONTENT_URI + "/quickresponse/account");
}
@SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI
+ "/quickresponse");
public static final Uri ACCOUNT_ID_URI = Uri.parse(
EmailContent.CONTENT_URI + "/quickresponse/account");
private String mText;
private long mAccountKey;

View File

@ -24,13 +24,14 @@ import android.os.RemoteException;
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;
private IAccountService mService = null;
private Object mReturn;
public AccountServiceProxy(Context _context) {
super(_context, getIntentForEmailPackage(_context, "ACCOUNT_INTENT"));
super(_context, new Intent(ACCOUNT_INTENT));
}
@Override
@ -44,11 +45,11 @@ public class AccountServiceProxy extends ServiceProxy implements IAccountService
}
@Override
public void notifyLoginFailed(final long accountId, final String reason) {
public void notifyLoginFailed(final long accountId) {
setTask(new ProxyTask() {
@Override
public void run() throws RemoteException {
mService.notifyLoginFailed(accountId, reason);
mService.notifyLoginFailed(accountId);
}
}, "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.TempDirectory;
import com.android.emailcommon.mail.MessagingException;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.HostAuth;
import com.android.emailcommon.provider.Policy;
@ -51,6 +50,10 @@ import java.io.IOException;
public class EmailServiceProxy extends ServiceProxy implements IEmailService {
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_HOST_AUTH = "autodiscover_host_auth";
@ -411,7 +414,9 @@ public class EmailServiceProxy extends ServiceProxy implements IEmailService {
}, "deleteAccountPIMData");
}
/**
* PRELIMINARY
* 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
* specified account (including subfolders, as specified by the includeSubfolders parameter).
@ -459,40 +464,6 @@ public class EmailServiceProxy extends ServiceProxy implements IEmailService {
}, "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
public IBinder asBinder() {
return null;

View File

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

View File

@ -18,7 +18,6 @@
package com.android.emailcommon.service;
import com.android.emailcommon.provider.HostAuth;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.service.IEmailServiceCallback;
import com.android.emailcommon.service.SearchParams;
import android.os.Bundle;
@ -60,9 +59,4 @@ interface IEmailService {
int searchMessages(long accountId, in SearchParams params, long destMailboxId);
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;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
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 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 Object mReturn = null;
public PolicyServiceProxy(Context _context) {
super(_context, getIntentForEmailPackage(_context, "POLICY_INTENT"));
super(_context, new Intent(POLICY_INTENT));
}
@Override

View File

@ -56,27 +56,6 @@ public abstract class ServiceProxy {
private long mStartTime;
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 ServiceProxy(Context _context, Intent _intent) {
@ -92,8 +71,7 @@ public abstract class ServiceProxy {
public void onServiceConnected(ComponentName name, IBinder binder) {
onConnected(binder);
if (DEBUG_PROXY) {
Log.v(mTag, "Connected: " + name.getShortClassName() + " at " +
(System.currentTimeMillis() - mStartTime) + "ms");
Log.v(mTag, "Connected: " + name.getShortClassName());
}
// Run our task on a new thread
new Thread(new Runnable() {
@ -108,8 +86,7 @@ public abstract class ServiceProxy {
public void onServiceDisconnected(ComponentName name) {
if (DEBUG_PROXY) {
Log.v(mTag, "Disconnected: " + name.getShortClassName() + " at " +
(System.currentTimeMillis() - mStartTime) + "ms");
Log.v(mTag, "Disconnected: " + name.getShortClassName());
}
}
}
@ -192,9 +169,8 @@ public abstract class ServiceProxy {
// Can be ignored safely
}
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");
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_1_MONTH = 5;
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.provider.EmailContent.Attachment;
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.MessageColumns;
import com.android.mail.providers.UIProvider;
@ -44,9 +42,10 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
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_THUMBNAIL = "THUMBNAIL";
@ -135,16 +134,23 @@ public class AttachmentUtilities {
*/
public static final int MAX_ATTACHMENT_UPLOAD_SIZE = (5 * 1024 * 1024);
private static Uri sUri;
public static Uri getAttachmentUri(long accountId, long id) {
if (sUri == null) {
sUri = Uri.parse(Attachment.ATTACHMENT_PROVIDER_URI_PREFIX);
}
return sUri.buildUpon()
.appendPath(Long.toString(accountId))
.appendPath(Long.toString(id))
.appendPath(FORMAT_RAW)
.build();
return CONTENT_URI.buildUpon()
.appendPath(Long.toString(accountId))
.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 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 "."
*
@ -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);
in.close();
out.flush();
@ -354,20 +379,24 @@ public class AttachmentUtilities {
ContentValues cv = new ContentValues();
long attachmentId = attachment.mId;
long accountId = attachment.mAccountKey;
String contentUri = null;
String contentUri;
long size;
try {
ContentResolver resolver = context.getContentResolver();
if (attachment.mUiDestination == UIProvider.AttachmentDestination.CACHE) {
Uri attUri = getAttachmentUri(accountId, attachmentId);
size = copyFile(in, resolver.openOutputStream(attUri));
contentUri = attUri.toString();
File saveIn = getAttachmentDirectory(context, accountId);
if (!saveIn.exists()) {
saveIn.mkdirs();
}
File file = getAttachmentFilename(context, accountId, attachmentId);
file.createNewFile();
size = copyFile(in, file);
contentUri = getAttachmentUri(accountId, attachmentId).toString();
} else if (Utility.isExternalStorageMounted()) {
File downloads = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS);
downloads.mkdirs();
File file = Utility.createUniqueFile(downloads, attachment.mFileName);
size = copyFile(in, new FileOutputStream(file));
size = copyFile(in, file);
String absolutePath = file.getAbsolutePath();
// 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);
// 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 {
private static final boolean LOG_ENABLED = false;
private static final int STANDARD_PORT = 80;
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.
*/
@ -60,11 +59,9 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager {
mTrackingKeyManager = keyManager;
}
public static EmailClientConnectionManager newInstance(Context context, HttpParams params,
HostAuth hostAuth) {
public static EmailClientConnectionManager newInstance(HttpParams params, boolean ssl,
int port) {
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
SchemeRegistry registry = new SchemeRegistry();
@ -72,11 +69,10 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager {
ssl ? STANDARD_PORT : port));
// Register https with the secure factory
registry.register(new Scheme("https",
SSLUtils.getHttpSocketFactory(context, hostAuth, keyManager, false),
ssl ? port : STANDARD_SSL_PORT));
SSLUtils.getHttpSocketFactory(false, keyManager), ssl ? port : STANDARD_SSL_PORT));
// Register the httpts scheme with our insecure factory
registry.register(new Scheme("httpts",
SSLUtils.getHttpSocketFactory(context, hostAuth, keyManager, true),
SSLUtils.getHttpSocketFactory(true /*insecure*/, keyManager),
ssl ? port : STANDARD_SSL_PORT));
return new EmailClientConnectionManager(params, registry, keyManager);
@ -102,10 +98,11 @@ public class EmailClientConnectionManager extends ThreadSafeClientConnManager {
}
KeyManager keyManager =
KeyChainKeyManager.fromAlias(context, hostAuth.mClientCertAlias);
boolean insecure = hostAuth.shouldTrustAllServerCerts();
SSLSocketFactory ssf =
SSLUtils.getHttpSocketFactory(context, hostAuth, keyManager, insecure);
registry.register(new Scheme(schemeName, ssf, hostAuth.mPort));
SSLCertificateSocketFactory underlying = SSLUtils.getSSLSocketFactory(
hostAuth.shouldTrustAllServerCerts());
underlying.setKeyManagers(new KeyManager[] { keyManager });
registry.register(
new Scheme(schemeName, new SSLSocketFactory(underlying), hostAuth.mPort));
}
}

View File

@ -16,139 +16,46 @@
package com.android.emailcommon.utility;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.SSLCertificateSocketFactory;
import android.security.KeyChain;
import android.security.KeyChainException;
import android.util.Log;
import com.android.emailcommon.provider.EmailContent.HostAuthColumns;
import com.android.emailcommon.provider.HostAuth;
import com.google.common.annotations.VisibleForTesting;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.Principal;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import javax.net.ssl.KeyManager;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedKeyManager;
import javax.net.ssl.X509TrustManager;
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 final boolean LOG_ENABLED = false;
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}.
* Optionally bypass all SSL certificate checks.
*
* @param insecure if true, bypass all SSL certificate checks
*/
public synchronized static SSLCertificateSocketFactory getSSLSocketFactory(Context context,
HostAuth hostAuth, boolean insecure) {
public synchronized static SSLCertificateSocketFactory getSSLSocketFactory(
boolean insecure) {
if (insecure) {
SSLCertificateSocketFactory insecureFactory = (SSLCertificateSocketFactory)
SSLCertificateSocketFactory.getDefault(0, null);
insecureFactory.setTrustManagers(
new TrustManager[] {
new SameCertificateCheckingTrustManager(context, hostAuth)});
return insecureFactory;
if (sInsecureFactory == null) {
sInsecureFactory = (SSLCertificateSocketFactory)
SSLCertificateSocketFactory.getInsecure(0, null);
}
return sInsecureFactory;
} else {
if (sSecureFactory == null) {
sSecureFactory = (SSLCertificateSocketFactory)
@ -162,9 +69,8 @@ public class SSLUtils {
* Returns a {@link org.apache.http.conn.ssl.SSLSocketFactory SSLSocketFactory} for use with the
* Apache HTTP stack.
*/
public static SSLSocketFactory getHttpSocketFactory(Context context, HostAuth hostAuth,
KeyManager keyManager, boolean insecure) {
SSLCertificateSocketFactory underlying = getSSLSocketFactory(context, hostAuth, insecure);
public static SSLSocketFactory getHttpSocketFactory(boolean insecure, KeyManager keyManager) {
SSLCertificateSocketFactory underlying = getSSLSocketFactory(insecure);
if (keyManager != null) {
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.MailboxColumns;
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.Mailbox;
import com.android.emailcommon.provider.ProviderUnavailableException;
@ -363,7 +364,6 @@ public class Utility {
cal.setTimeZone(TimeZone.getTimeZone("GMT"));
return cal;
}
/**
* 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)
@ -745,28 +745,25 @@ public class Utility {
return false;
} else if (attachment.mContentBytes != null) {
return true;
} else {
String contentUri = attachment.getContentUri();
if (TextUtils.isEmpty(contentUri)) {
return false;
}
} else if (TextUtils.isEmpty(attachment.mContentUri)) {
return false;
}
try {
Uri fileUri = Uri.parse(attachment.mContentUri);
try {
Uri fileUri = Uri.parse(contentUri);
InputStream inStream = context.getContentResolver().openInputStream(fileUri);
try {
InputStream inStream = context.getContentResolver().openInputStream(fileUri);
try {
inStream.close();
} catch (IOException e) {
// Nothing to be done if can't close the stream
}
return true;
} catch (FileNotFoundException e) {
return false;
inStream.close();
} catch (IOException e) {
// Nothing to be done if can't close the stream
}
} catch (RuntimeException re) {
Log.w(Logging.LOG_TAG, "attachmentExists RuntimeException=" + re);
return true;
} catch (FileNotFoundException e) {
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) {
Log.d(Logging.LOG_TAG, "Unloaded attachment isn't marked for download: " +
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);
} 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
// contentUri; this should be a very unusual case
ContentValues cv = new ContentValues();
@ -1163,4 +1150,72 @@ public class Utility {
sb.append(')');
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.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