mirror of
https://github.com/moparisthebest/k-9
synced 2025-01-12 06:08:25 -05:00
Merge branch 'master' into pgp-fixes
This commit is contained in:
commit
e9c8ec5598
@ -8,5 +8,6 @@
|
||||
<classpathentry kind="lib" path="libs/commons-io-2.0.1.jar"/>
|
||||
<classpathentry kind="lib" path="libs/jzlib-1.0.7.jar"/>
|
||||
<classpathentry kind="lib" path="libs/jutf7-1.0.1-SNAPSHOT.jar"/>
|
||||
<classpathentry kind="lib" path="libs/htmlcleaner-2.2.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
@ -340,6 +340,7 @@ K-9 Mail セットアップにようこそ。\nK-9 は標準のAndroidメール
|
||||
<string name="global_settings_confirm_actions_summary">選択した動作を実行するときに常にダイアログを表示する</string>
|
||||
<string name="global_settings_confirm_action_archive">アーカイブ</string>
|
||||
<string name="global_settings_confirm_action_delete">削除(メッセージ表示画面のみ)</string>
|
||||
<string name="global_settings_confirm_action_delete_starred">スター付きメッセージの削除 (メッセージ表示画面のみ)</string>
|
||||
<string name="global_settings_confirm_action_spam">迷惑メール</string>
|
||||
<string name="global_settings_confirm_action_mark_all_as_read">すべて既読にする</string>
|
||||
<string name="global_settings_confirm_action_send">送信</string>
|
||||
@ -564,9 +565,13 @@ K-9 Mail セットアップにようこそ。\nK-9 は標準のAndroidメール
|
||||
<string name="account_settings_reply_after_quote_label">引用テキストの後に返信を書く</string>
|
||||
<string name="account_settings_reply_after_quote_summary">返信するときに元のメッセージを返信の上に表示</string>
|
||||
|
||||
<string name="account_settings_strip_signature_label">引用から署名を除く</string>
|
||||
<string name="account_settings_strip_signature_summary">メッセージの返信時に引用文にある署名を取り除く</string>
|
||||
|
||||
<string name="account_settings_message_format_label">メッセージの形式</string>
|
||||
<string name="account_settings_message_format_text">テキスト(画像や書式は取り除く)</string>
|
||||
<string name="account_settings_message_format_html">HTML (画像や書式を保持)</string>
|
||||
<string name="account_settings_message_format_auto">自動 (HTMLメールに対する返信でなければテキスト)</string>
|
||||
|
||||
<string name="account_settings_message_read_receipt_label">開封確認</string>
|
||||
<string name="account_settings_message_read_receipt_summary">常に開封確認を要求する</string>
|
||||
|
@ -2,7 +2,6 @@
|
||||
package com.fsck.k9;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -23,7 +22,6 @@ import android.os.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.text.format.Time;
|
||||
import android.text.style.AbsoluteSizeSpan;
|
||||
import android.util.Log;
|
||||
|
||||
import com.fsck.k9.activity.MessageCompose;
|
||||
@ -68,11 +66,6 @@ public class K9 extends Application {
|
||||
*/
|
||||
private static List<ApplicationAware> observers = new ArrayList<ApplicationAware>();
|
||||
|
||||
/**
|
||||
* @see K9#createAbsoluteSizeSpan(int)
|
||||
*/
|
||||
private static Constructor<AbsoluteSizeSpan> sAbsoluteSizeSpanConstructor;
|
||||
|
||||
public enum BACKGROUND_OPS {
|
||||
WHEN_CHECKED, ALWAYS, NEVER, WHEN_CHECKED_AUTO_SYNC
|
||||
}
|
||||
@ -1023,48 +1016,4 @@ public class K9 extends Application {
|
||||
public static void setAttachmentDefaultPath(String attachmentDefaultPath) {
|
||||
K9.mAttachmentDefaultPath = attachmentDefaultPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link AbsoluteSizeSpan} object.
|
||||
*
|
||||
* <p>
|
||||
* Android versions prior to 2.0 don't support the constructor with two parameters
|
||||
* ({@link AbsoluteSizeSpan#AbsoluteSizeSpan(int, boolean)}). So we have to perform some
|
||||
* reflection magic to dynamically load the new constructor on devices that support it.
|
||||
* For devices with old Android versions we just use the size as pixels (instead of dip).
|
||||
* </p>
|
||||
*
|
||||
* @param size This is used as the {@code size} parameter for the AbsoluteSizeSpan constructor.
|
||||
* @return a AbsoluteSizeSpan object with the specified text size.
|
||||
*/
|
||||
public static AbsoluteSizeSpan createAbsoluteSizeSpan(int size) {
|
||||
if (Integer.parseInt(android.os.Build.VERSION.SDK) < 5) {
|
||||
// For Android 1.5/1.6 simply use the constructor with only the size parameter.
|
||||
// Yes, that will most likely look wrong!
|
||||
return new AbsoluteSizeSpan(size);
|
||||
}
|
||||
|
||||
if (sAbsoluteSizeSpanConstructor == null) {
|
||||
try {
|
||||
sAbsoluteSizeSpanConstructor = AbsoluteSizeSpan.class.getConstructor(int.class, boolean.class);
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Couldn't get the AbsoluteSizeSpan(int, boolean) constructor", e);
|
||||
|
||||
// Fallback
|
||||
return new AbsoluteSizeSpan(size);
|
||||
}
|
||||
}
|
||||
|
||||
AbsoluteSizeSpan result;
|
||||
try {
|
||||
result = sAbsoluteSizeSpanConstructor.newInstance(size, true);
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Couldn't call the AbsoluteSizeSpan(int, boolean) constructor", e);
|
||||
|
||||
// Fallback
|
||||
result = new AbsoluteSizeSpan(size);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package com.fsck.k9.activity;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
@ -125,12 +124,9 @@ public class EditIdentity extends K9Activity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
public void onBackPressed() {
|
||||
saveIdentity();
|
||||
return true;
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -356,38 +356,18 @@ public class FolderList extends K9ListActivity {
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// This will be called either automatically for you on 2.0
|
||||
// or later, or by the code above on earlier versions of the
|
||||
// platform.
|
||||
if (K9.manageBack()) {
|
||||
onAccounts();
|
||||
} else {
|
||||
// TODO - when we move to android 2.0, uncomment this instead.
|
||||
// super.onBackPressed()
|
||||
finish();
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
//Shortcuts that work no matter what is selected
|
||||
|
||||
if (
|
||||
// TODO - when we move to android 2.0, uncomment this.
|
||||
// android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ECLAIR &&
|
||||
|
||||
keyCode == KeyEvent.KEYCODE_BACK
|
||||
&& event.getRepeatCount() == 0
|
||||
&& K9.manageBack()) {
|
||||
// Take care of calling this method on earlier versions of
|
||||
// the platform where it doesn't exist.
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_Q:
|
||||
//case KeyEvent.KEYCODE_BACK:
|
||||
{
|
||||
onAccounts();
|
||||
return true;
|
||||
|
@ -119,11 +119,9 @@ public class ManageIdentities extends ChooseIdentity {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
public void onBackPressed() {
|
||||
saveIdentities();
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
private void saveIdentities() {
|
||||
|
@ -30,7 +30,6 @@ import android.os.Parcelable;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.text.util.Rfc822Tokenizer;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
@ -717,9 +716,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
||||
* it wrong! So go fix your program or get AOSP to change the documentation.
|
||||
*/
|
||||
}
|
||||
//TODO: Use constant Intent.ACTION_SEND_MULTIPLE once we drop Android 1.5 support
|
||||
else if (Intent.ACTION_SEND.equals(action) ||
|
||||
"android.intent.action.SEND_MULTIPLE".equals(action)) {
|
||||
else if (Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action)) {
|
||||
/*
|
||||
* Note: Here we allow a slight deviation from the documentated behavior.
|
||||
* EXTRA_TEXT is used as message body (if available) regardless of the MIME
|
||||
@ -1980,16 +1977,13 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// This will be called either automatically for you on 2.0
|
||||
// or later, or by the code above on earlier versions of the
|
||||
// platform.
|
||||
if (mEncryptCheckbox.isChecked()) {
|
||||
showDialog(DIALOG_REFUSE_TO_SAVE_DRAFT_MARKED_ENCRYPTED);
|
||||
}
|
||||
else if (mDraftNeedsSaving) {
|
||||
showDialog(DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE);
|
||||
} else {
|
||||
finish();
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@ -2045,24 +2039,6 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
|
||||
return super.onCreateDialog(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (
|
||||
// TODO - when we move to android 2.0, uncomment this.
|
||||
// android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ECLAIR &&
|
||||
|
||||
keyCode == KeyEvent.KEYCODE_BACK
|
||||
&& event.getRepeatCount() == 0
|
||||
&& K9.manageBack()) {
|
||||
// Take care of calling this method on earlier versions of
|
||||
// the platform where it doesn't exist.
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if all attachments were able to be attached, otherwise returns false.
|
||||
*/
|
||||
|
@ -18,6 +18,7 @@ import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.style.AbsoluteSizeSpan;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.util.Log;
|
||||
@ -897,9 +898,6 @@ public class MessageList
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// This will be called either automatically for you on 2.0
|
||||
// or later, or by the code above on earlier versions of the
|
||||
// platform.
|
||||
if (K9.manageBack()) {
|
||||
if (mQueryString == null) {
|
||||
onShowFolderList();
|
||||
@ -907,24 +905,12 @@ public class MessageList
|
||||
onAccounts();
|
||||
}
|
||||
} else {
|
||||
finish();
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (
|
||||
// XXX TODO - when we go to android 2.0, uncomment this
|
||||
// android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ECLAIR &&
|
||||
keyCode == KeyEvent.KEYCODE_BACK
|
||||
&& event.getRepeatCount() == 0
|
||||
) {
|
||||
// Take care of calling this method on earlier versions of
|
||||
// the platform where it doesn't exist.
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Shortcuts that work no matter what is selected
|
||||
switch (keyCode) {
|
||||
|
||||
@ -2181,7 +2167,7 @@ public class MessageList
|
||||
0,
|
||||
noSender.length(),
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
str.setSpan(K9.createAbsoluteSizeSpan(mFontSizes.getMessageListSender()),
|
||||
str.setSpan(new AbsoluteSizeSpan(mFontSizes.getMessageListSender(), true),
|
||||
0,
|
||||
noSender.length(),
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
@ -2270,7 +2256,7 @@ public class MessageList
|
||||
0,
|
||||
message.sender.length() + 1,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
str.setSpan(K9.createAbsoluteSizeSpan(mFontSizes.getMessageListSender()),
|
||||
str.setSpan(new AbsoluteSizeSpan(mFontSizes.getMessageListSender(), true),
|
||||
0,
|
||||
message.sender.length() + 1,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
@ -27,8 +27,6 @@ import com.fsck.k9.view.SingleMessageView;
|
||||
import com.fsck.k9.view.AttachmentView.AttachmentFileDownloadCallback;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
public class MessageView extends K9Activity implements OnClickListener {
|
||||
@ -42,27 +40,6 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
private static final int ACTIVITY_CHOOSE_FOLDER_COPY = 2;
|
||||
private static final int ACTIVITY_CHOOSE_DIRECTORY = 3;
|
||||
|
||||
/**
|
||||
* Whether parent class have the onBackPressed() method (with no argument)
|
||||
*/
|
||||
private static final boolean HAS_SUPER_ON_BACK_METHOD;
|
||||
static {
|
||||
boolean hasOnBackMethod;
|
||||
try {
|
||||
final Class <? super MessageView > superClass = MessageView.class.getSuperclass();
|
||||
final Method method = superClass.getMethod("onBackPressed", new Class[] {});
|
||||
hasOnBackMethod = (method.getModifiers() & Modifier.PUBLIC) == Modifier.PUBLIC;
|
||||
} catch (final SecurityException e) {
|
||||
if (K9.DEBUG) {
|
||||
Log.v(K9.LOG_TAG, "Security exception while checking for 'onBackPressed' method", e);
|
||||
}
|
||||
hasOnBackMethod = false;
|
||||
} catch (final NoSuchMethodException e) {
|
||||
hasOnBackMethod = false;
|
||||
}
|
||||
HAS_SUPER_ON_BACK_METHOD = hasOnBackMethod;
|
||||
}
|
||||
|
||||
private SingleMessageView mMessageView;
|
||||
|
||||
private PgpData mPgpData;
|
||||
@ -140,7 +117,7 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
boolean ret = false;
|
||||
if (KeyEvent.ACTION_DOWN == event.getAction()) {
|
||||
ret = onKeyDown(event.getKeyCode(), event);
|
||||
ret = onCustomKeyDown(event.getKeyCode(), event);
|
||||
}
|
||||
if (!ret) {
|
||||
ret = super.dispatchKeyEvent(event);
|
||||
@ -148,29 +125,36 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(final int keyCode, final KeyEvent event) {
|
||||
if (
|
||||
// XXX TODO - when we go to android 2.0, uncomment this
|
||||
// android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ECLAIR &&
|
||||
keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
|
||||
// Take care of calling this method on earlier versions of
|
||||
// the platform where it doesn't exist.
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Handle hotkeys
|
||||
*
|
||||
* <p>
|
||||
* This method is called by {@link #dispatchKeyEvent(KeyEvent)} before any view had the chance
|
||||
* to consume this key event.
|
||||
* </p>
|
||||
*
|
||||
* @param keyCode
|
||||
* The value in {@code event.getKeyCode()}.
|
||||
* @param event
|
||||
* Description of the key event.
|
||||
*
|
||||
* @return {@code true} if this event was consumed.
|
||||
*/
|
||||
public boolean onCustomKeyDown(final int keyCode, final KeyEvent event) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_VOLUME_UP: {
|
||||
if (K9.useVolumeKeysForNavigationEnabled()) {
|
||||
onNext();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KeyEvent.KEYCODE_VOLUME_DOWN: {
|
||||
if (K9.useVolumeKeysForNavigationEnabled()) {
|
||||
onPrevious();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case KeyEvent.KEYCODE_SHIFT_LEFT:
|
||||
case KeyEvent.KEYCODE_SHIFT_RIGHT: {
|
||||
@ -245,7 +229,7 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -263,17 +247,12 @@ public class MessageView extends K9Activity implements OnClickListener {
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// This will be called either automatically for you on 2.0
|
||||
// or later, or by the code above on earlier versions of the
|
||||
// platform.
|
||||
if (K9.manageBack()) {
|
||||
String folder = (mMessage != null) ? mMessage.getFolder().getName() : null;
|
||||
MessageList.actionHandleFolder(this, mAccount, folder);
|
||||
finish();
|
||||
} else if (HAS_SUPER_ON_BACK_METHOD) {
|
||||
super.onBackPressed();
|
||||
} else {
|
||||
finish();
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@ import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
import android.preference.*;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
@ -823,11 +822,9 @@ public class AccountSettings extends K9PreferenceActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
public void onBackPressed() {
|
||||
saveSettings();
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
private void onCompositionSettings() {
|
||||
@ -924,8 +921,8 @@ public class AccountSettings extends K9PreferenceActivity {
|
||||
}
|
||||
}
|
||||
|
||||
allFolderValues = new String[folders.size()+1];
|
||||
allFolderLabels = new String[folders.size()+1];
|
||||
allFolderValues = new String[folders.size() + 1];
|
||||
allFolderLabels = new String[folders.size() + 1];
|
||||
|
||||
allFolderValues[0] = K9.FOLDER_NONE;
|
||||
allFolderLabels[0] = K9.FOLDER_NONE;
|
||||
|
@ -3,7 +3,6 @@ package com.fsck.k9.activity.setup;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.CheckBox;
|
||||
@ -121,11 +120,9 @@ public class AccountSetupComposition extends K9Activity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
public void onBackPressed() {
|
||||
saveSettings();
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -8,7 +8,6 @@ import android.preference.CheckBoxPreference;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import com.fsck.k9.*;
|
||||
import com.fsck.k9.activity.K9PreferenceActivity;
|
||||
import com.fsck.k9.mail.Folder.FolderClass;
|
||||
@ -151,16 +150,13 @@ public class FolderSettings extends K9PreferenceActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
public void onBackPressed() {
|
||||
try {
|
||||
saveSettings();
|
||||
} catch (MessagingException e) {
|
||||
Log.e(K9.LOG_TAG, "Saving folder settings failed", e);
|
||||
}
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
import android.os.Bundle;
|
||||
import android.preference.*;
|
||||
import android.view.KeyEvent;
|
||||
import com.fsck.k9.*;
|
||||
import com.fsck.k9.activity.K9PreferenceActivity;
|
||||
|
||||
@ -160,10 +159,8 @@ public class FontSizeSettings extends K9PreferenceActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
public void onBackPressed() {
|
||||
saveSettings();
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ import android.preference.CheckBoxPreference;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.Preference.OnPreferenceClickListener;
|
||||
import android.view.KeyEvent;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.fsck.k9.K9;
|
||||
@ -268,7 +267,7 @@ public class Prefs extends K9PreferenceActivity {
|
||||
mZoomControlsEnabled.setChecked(K9.zoomControlsEnabled());
|
||||
|
||||
mMobileOptimizedLayout = (CheckBoxPreference) findPreference(PREFERENCE_MESSAGEVIEW_MOBILE_LAYOUT);
|
||||
if (Integer.parseInt(Build.VERSION.SDK) <= 7) {
|
||||
if (Build.VERSION.SDK_INT <= 7) {
|
||||
mMobileOptimizedLayout.setEnabled(false);
|
||||
}
|
||||
|
||||
@ -404,17 +403,16 @@ public class Prefs extends K9PreferenceActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
public void onBackPressed() {
|
||||
saveSettings();
|
||||
|
||||
if (K9.manageBack()) {
|
||||
Accounts.listAccounts(this);
|
||||
finish();
|
||||
return true;
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
private void onFontSizeSettings() {
|
||||
FontSizeSettings.actionEditSettings(this);
|
||||
|
@ -1,115 +0,0 @@
|
||||
package com.fsck.k9.helper;
|
||||
|
||||
import com.fsck.k9.K9;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Helper class to get the current state of the auto-sync setting.
|
||||
*/
|
||||
public class AutoSyncHelper {
|
||||
/**
|
||||
* False, if we never tried to load the class for this SDK version.
|
||||
* True, otherwise.
|
||||
*
|
||||
* Note: if sAutoSync is null and sChecked is true, then an error occurred
|
||||
* while loading the class for the SDK version we're running on.
|
||||
*/
|
||||
private static boolean sChecked = false;
|
||||
|
||||
/**
|
||||
* Instance of the SDK specific class that implements the IAutoSync
|
||||
* interface.
|
||||
*/
|
||||
private static IAutoSync sAutoSync = null;
|
||||
|
||||
/**
|
||||
* String for the auto-sync changed Intent. This isn't currently exposed by the API
|
||||
*/
|
||||
public static String SYNC_CONN_STATUS_CHANGE = "com.android.sync.SYNC_CONN_STATUS_CHANGED";
|
||||
/**
|
||||
* Try loading the class that implements IAutoSync for this SDK version.
|
||||
*
|
||||
* @return the IAutoSync object for this SDK version, or null if something
|
||||
* went wrong.
|
||||
*/
|
||||
private static IAutoSync loadAutoSync() {
|
||||
/*
|
||||
* We're trying to load the class for this SDK version. If anything
|
||||
* goes wrong after this point, we don't want to try again.
|
||||
*/
|
||||
sChecked = true;
|
||||
|
||||
/*
|
||||
* Check the version of the SDK we are running on. Choose an
|
||||
* implementation class designed for that version of the SDK.
|
||||
*/
|
||||
int sdkVersion = Integer.parseInt(Build.VERSION.SDK);
|
||||
|
||||
String className = null;
|
||||
if (sdkVersion == Build.VERSION_CODES.CUPCAKE) {
|
||||
className = "com.fsck.k9.helper.AutoSyncSdk3";
|
||||
} else if (sdkVersion == Build.VERSION_CODES.DONUT) {
|
||||
className = "com.fsck.k9.helper.AutoSyncSdk4";
|
||||
} else if (sdkVersion >= Build.VERSION_CODES.ECLAIR) {
|
||||
className = "com.fsck.k9.helper.AutoSyncSdk5";
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the required class by name and instantiate it.
|
||||
*/
|
||||
try {
|
||||
Class <? extends IAutoSync > clazz =
|
||||
Class.forName(className).asSubclass(IAutoSync.class);
|
||||
|
||||
IAutoSync autoSync = clazz.newInstance();
|
||||
autoSync.initialize(K9.app);
|
||||
|
||||
return autoSync;
|
||||
} catch (ClassNotFoundException e) {
|
||||
Log.e(K9.LOG_TAG, "Couldn't find class: " + className, e);
|
||||
} catch (InstantiationException e) {
|
||||
Log.e(K9.LOG_TAG, "Couldn't instantiate class: " + className, e);
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.e(K9.LOG_TAG, "Couldn't access class: " + className, e);
|
||||
} catch (NoSuchMethodException e) {
|
||||
if (K9.DEBUG) {
|
||||
Log.d(K9.LOG_TAG, "Couldn't load method to get auto-sync state", e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether we can query the auto-sync state using
|
||||
* getMasterSyncAutomatically() or not.
|
||||
*
|
||||
* @return true, if calls to getMasterSyncAutomatically() will return the
|
||||
* state of the auto-sync setting. false, otherwise.
|
||||
*/
|
||||
public static boolean isAvailable() {
|
||||
if (!sChecked) {
|
||||
sAutoSync = loadAutoSync();
|
||||
}
|
||||
return (sAutoSync != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the state of the auto-sync setting.
|
||||
*
|
||||
* @return the state of the auto-sync setting.
|
||||
* @see IAutoSync
|
||||
*/
|
||||
public static boolean getMasterSyncAutomatically() {
|
||||
if (!sChecked) {
|
||||
sAutoSync = loadAutoSync();
|
||||
}
|
||||
|
||||
if (sAutoSync == null) {
|
||||
throw new RuntimeException(
|
||||
"Called getMasterSyncAutomatically() before checking if it's available.");
|
||||
}
|
||||
|
||||
return sAutoSync.getMasterSyncAutomatically();
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package com.fsck.k9.helper;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
|
||||
public class AutoSyncSdk3 implements IAutoSync {
|
||||
private Method mGetListenForNetworkTickles;
|
||||
private Object mQueryMap;
|
||||
|
||||
public void initialize(Context context) throws NoSuchMethodException {
|
||||
/*
|
||||
* There's no documented/official way to query the state of the
|
||||
* auto-sync setting for a normal application in SDK 1.5/API 3.
|
||||
*
|
||||
* We use reflection to get an Sync.Settings.QueryMap" object, so we
|
||||
* can call its getListenForNetworkTickles() method. This will return
|
||||
* the current auto-sync state.
|
||||
*/
|
||||
try {
|
||||
Class<?> clazz = Class.forName("android.provider.Sync$Settings$QueryMap");
|
||||
Constructor<?> c = clazz.getConstructor(ContentResolver.class, boolean.class, Handler.class);
|
||||
mQueryMap = c.newInstance(context.getContentResolver(), true, null);
|
||||
mGetListenForNetworkTickles = mQueryMap.getClass().getMethod("getListenForNetworkTickles");
|
||||
} catch (Exception e) {
|
||||
throw new NoSuchMethodException();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getMasterSyncAutomatically() {
|
||||
try {
|
||||
return (Boolean) mGetListenForNetworkTickles.invoke(mQueryMap);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
package com.fsck.k9.helper;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import com.fsck.k9.K9;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
public class AutoSyncSdk4 implements IAutoSync {
|
||||
private Method mGetListenForNetworkTickles;
|
||||
private Object mContentService;
|
||||
|
||||
public void initialize(Context context) throws NoSuchMethodException {
|
||||
/*
|
||||
* There's no documented/official way to query the state of the
|
||||
* auto-sync setting for a normal application in SDK 1.6/API 4.
|
||||
*
|
||||
* We use reflection to get an ContentService object, so we can call its
|
||||
* getListenForNetworkTickles() method. This will return the current
|
||||
* auto-sync state.
|
||||
*/
|
||||
try {
|
||||
Method getContentService = ContentResolver.class.getMethod("getContentService");
|
||||
mContentService = getContentService.invoke(null);
|
||||
mGetListenForNetworkTickles = mContentService.getClass().getMethod("getListenForNetworkTickles");
|
||||
} catch (Exception e) {
|
||||
throw new NoSuchMethodException();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getMasterSyncAutomatically() {
|
||||
try {
|
||||
return (Boolean) mGetListenForNetworkTickles.invoke(mContentService);
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Could not query for network tickle", e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
package com.fsck.k9.helper;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
|
||||
public class AutoSyncSdk5 implements IAutoSync {
|
||||
public void initialize(Context context) throws NoSuchMethodException {
|
||||
// Nothing to do here
|
||||
}
|
||||
|
||||
public boolean getMasterSyncAutomatically() {
|
||||
/*
|
||||
* SDK 2.0/API 5 introduced an official method to query the auto-sync
|
||||
* state.
|
||||
*/
|
||||
return ContentResolver.getMasterSyncAutomatically();
|
||||
}
|
||||
}
|
@ -18,8 +18,8 @@ import com.fsck.k9.mail.Address;
|
||||
* A class that uses the latest contacts API available on the device will be
|
||||
* loaded at runtime.
|
||||
*
|
||||
* @see ContactsSdk3_4
|
||||
* @see ContactsSdk5
|
||||
* @see ContactsSdk5p
|
||||
*/
|
||||
public abstract class Contacts {
|
||||
/**
|
||||
@ -40,12 +40,8 @@ public abstract class Contacts {
|
||||
* Check the version of the SDK we are running on. Choose an
|
||||
* implementation class designed for that version of the SDK.
|
||||
*/
|
||||
int sdkVersion = Integer.parseInt(Build.VERSION.SDK);
|
||||
|
||||
String className = null;
|
||||
if (sdkVersion <= Build.VERSION_CODES.DONUT) {
|
||||
className = "com.fsck.k9.helper.ContactsSdk3_4";
|
||||
} else if (sdkVersion <= Build.VERSION_CODES.ECLAIR_MR1) {
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ECLAIR_MR1) {
|
||||
/*
|
||||
* The new API was introduced with SDK 5. But Android versions < 2.2
|
||||
* need some additional code to be able to search for phonetic names.
|
||||
|
@ -1,263 +0,0 @@
|
||||
package com.fsck.k9.helper;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.provider.Contacts;
|
||||
import android.provider.Contacts.ContactMethods;
|
||||
import com.fsck.k9.mail.Address;
|
||||
import com.fsck.k9.K9;
|
||||
|
||||
|
||||
/**
|
||||
* Access the contacts on the device using the old API (introduced in SDK 1).
|
||||
*
|
||||
* @see android.provider.Contacts
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ContactsSdk3_4 extends com.fsck.k9.helper.Contacts {
|
||||
/**
|
||||
* The order in which the search results are returned by
|
||||
* {@link #searchContacts(CharSequence)}.
|
||||
*/
|
||||
private static final String SORT_ORDER =
|
||||
Contacts.ContactMethods.TIMES_CONTACTED + " DESC, " +
|
||||
Contacts.ContactMethods.DISPLAY_NAME + ", " +
|
||||
Contacts.ContactMethods._ID;
|
||||
|
||||
/**
|
||||
* Array of columns to load from the database.
|
||||
*
|
||||
* Important: The _ID field is needed by
|
||||
* {@link com.fsck.k9.EmailAddressAdapter} or more specificly by
|
||||
* {@link android.widget.ResourceCursorAdapter}.
|
||||
*/
|
||||
private static final String PROJECTION[] = {
|
||||
Contacts.ContactMethods._ID,
|
||||
Contacts.ContactMethods.DISPLAY_NAME,
|
||||
Contacts.ContactMethods.DATA,
|
||||
Contacts.ContactMethods.PERSON_ID
|
||||
};
|
||||
|
||||
/**
|
||||
* Index of the name field in the projection. This must match the order in
|
||||
* {@link #PROJECTION}.
|
||||
*/
|
||||
private static final int NAME_INDEX = 1;
|
||||
|
||||
/**
|
||||
* Index of the email address field in the projection. This must match the
|
||||
* order in {@link #PROJECTION}.
|
||||
*/
|
||||
private static final int EMAIL_INDEX = 2;
|
||||
|
||||
/**
|
||||
* Index of the contact id field in the projection. This must match the order in
|
||||
* {@link #PROJECTION}.
|
||||
*/
|
||||
private static final int CONTACT_ID_INDEX = 3;
|
||||
|
||||
|
||||
public ContactsSdk3_4(final Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createContact(final Address email) {
|
||||
final Uri contactUri = Uri.fromParts("mailto", email.getAddress(), null);
|
||||
|
||||
final Intent contactIntent = new Intent(Contacts.Intents.SHOW_OR_CREATE_CONTACT);
|
||||
contactIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
contactIntent.setData(contactUri);
|
||||
|
||||
// Pass along full E-mail string for possible create dialog
|
||||
contactIntent.putExtra(Contacts.Intents.EXTRA_CREATE_DESCRIPTION,
|
||||
email.toString());
|
||||
|
||||
// Only provide personal name hint if we have one
|
||||
final String senderPersonal = email.getPersonal();
|
||||
if (senderPersonal != null) {
|
||||
contactIntent.putExtra(Contacts.Intents.Insert.NAME, senderPersonal);
|
||||
}
|
||||
|
||||
mContext.startActivity(contactIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInContacts(final String emailAddress) {
|
||||
boolean result = false;
|
||||
|
||||
final Cursor c = getContactByAddress(emailAddress);
|
||||
|
||||
if (c != null) {
|
||||
if (c.getCount() > 0) {
|
||||
result = true;
|
||||
}
|
||||
c.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor searchContacts(final CharSequence constraint) {
|
||||
final String where;
|
||||
final String[] args;
|
||||
if (constraint == null) {
|
||||
where = Contacts.ContactMethods.KIND + " = " + Contacts.KIND_EMAIL;
|
||||
args = null;
|
||||
} else {
|
||||
where = Contacts.ContactMethods.KIND + " = " + Contacts.KIND_EMAIL +
|
||||
" AND " +
|
||||
"(" +
|
||||
// Match if name starts with "constraint"
|
||||
Contacts.People.NAME + " LIKE ?" +
|
||||
" OR " +
|
||||
// Match if name contains a word that starts with "constraint"
|
||||
Contacts.People.NAME + " LIKE ?" +
|
||||
" OR " +
|
||||
// Match if phonetic name starts with "constraint"
|
||||
Contacts.People.PHONETIC_NAME + " LIKE ?" +
|
||||
" OR " +
|
||||
// Match if phonetic name contains a word that starts with "constraint"
|
||||
Contacts.People.PHONETIC_NAME + " LIKE ?" +
|
||||
" OR " +
|
||||
// Match if email address starts with "constraint"
|
||||
Contacts.ContactMethods.DATA + " LIKE ?" +
|
||||
")";
|
||||
final String filter = constraint.toString() + "%";
|
||||
final String filter2 = "% " + filter;
|
||||
args = new String[] {filter, filter2, filter, filter2, filter};
|
||||
}
|
||||
|
||||
final Cursor c = mContentResolver.query(
|
||||
Contacts.ContactMethods.CONTENT_URI,
|
||||
PROJECTION,
|
||||
where,
|
||||
args,
|
||||
SORT_ORDER);
|
||||
|
||||
if (c != null) {
|
||||
/*
|
||||
* To prevent expensive execution in the UI thread:
|
||||
* Cursors get lazily executed, so if you don't call anything on
|
||||
* the cursor before returning it from the background thread you'll
|
||||
* have a complied program for the cursor, but it won't have been
|
||||
* executed to generate the data yet. Often the execution is more
|
||||
* expensive than the compilation...
|
||||
*/
|
||||
c.getCount();
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNameForAddress(String address) {
|
||||
if (address == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Cursor c = getContactByAddress(address);
|
||||
|
||||
String name = null;
|
||||
if (c != null) {
|
||||
if (c.getCount() > 0) {
|
||||
c.moveToFirst();
|
||||
name = getName(c);
|
||||
}
|
||||
c.close();
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName(Cursor c) {
|
||||
return c.getString(NAME_INDEX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEmail(Cursor c) {
|
||||
return c.getString(EMAIL_INDEX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void markAsContacted(final Address[] addresses) {
|
||||
//TODO: Optimize! Potentially a lot of database queries
|
||||
for (final Address address : addresses) {
|
||||
final Cursor c = getContactByAddress(address.getAddress());
|
||||
|
||||
if (c != null) {
|
||||
if (c.getCount() > 0) {
|
||||
c.moveToFirst();
|
||||
final long personId = c.getLong(CONTACT_ID_INDEX);
|
||||
Contacts.People.markAsContacted(mContentResolver, personId);
|
||||
}
|
||||
c.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Intent contactPickerIntent() {
|
||||
return new Intent(Intent.ACTION_PICK, Contacts.People.CONTENT_URI);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEmailFromContactPicker(final Intent data) {
|
||||
Cursor cursor = null;
|
||||
Cursor cursor2 = null;
|
||||
String email = "";
|
||||
|
||||
try {
|
||||
Uri result = data.getData();
|
||||
cursor = mContentResolver.query(result, null, null, null, null);
|
||||
if (cursor.moveToFirst()) {
|
||||
String emailId = cursor.getString(cursor.getColumnIndex(Contacts.People.PRIMARY_EMAIL_ID));
|
||||
cursor2 = mContext.getContentResolver().query(
|
||||
ContactMethods.CONTENT_EMAIL_URI,
|
||||
new String[] { ContactMethods.DATA },
|
||||
"contact_methods._id=?",
|
||||
new String[] { emailId },
|
||||
null);
|
||||
|
||||
if (cursor2.moveToFirst()) {
|
||||
email = cursor2.getString(0);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(K9.LOG_TAG, "Failed to get email data", e);
|
||||
} finally {
|
||||
Utility.closeQuietly(cursor);
|
||||
Utility.closeQuietly(cursor2);
|
||||
}
|
||||
|
||||
return email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link Cursor} instance that can be used to fetch information
|
||||
* about the contact with the given email address.
|
||||
*
|
||||
* @param address The email address to search for.
|
||||
* @return A {@link Cursor} instance that can be used to fetch information
|
||||
* about the contact with the given email address
|
||||
*/
|
||||
private Cursor getContactByAddress(String address) {
|
||||
final String where = Contacts.ContactMethods.KIND + " = " + Contacts.KIND_EMAIL +
|
||||
" AND " +
|
||||
Contacts.ContactMethods.DATA + " = ?";
|
||||
final String[] args = new String[] {address};
|
||||
|
||||
final Cursor c = mContentResolver.query(
|
||||
Contacts.ContactMethods.CONTENT_URI,
|
||||
PROJECTION,
|
||||
where,
|
||||
args,
|
||||
SORT_ORDER);
|
||||
return c;
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package com.fsck.k9.helper;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
/**
|
||||
* Classes that implement this interface know how to query the system for the
|
||||
* current state of the auto-sync setting. This method differs from SDK 3 to
|
||||
* SDK 5, so there are specialized implementations for each SDK version.
|
||||
*/
|
||||
public interface IAutoSync {
|
||||
/**
|
||||
* Do the necessary reflection magic to get the necessary objects and/or
|
||||
* methods to later query the state of the auto-sync setting.
|
||||
*
|
||||
* @param context The application context object.
|
||||
* @throws NoSuchMethodException if something went wrong.
|
||||
*/
|
||||
public void initialize(Context context) throws NoSuchMethodException;
|
||||
|
||||
/**
|
||||
* Query the state of the auto-sync setting.
|
||||
*
|
||||
* @return the state of the auto-sync setting.
|
||||
*/
|
||||
public boolean getMasterSyncAutomatically();
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
package com.fsck.k9.mail.store;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import com.fsck.k9.mail.MessagingException;
|
||||
import com.fsck.k9.mail.filter.FixedLengthInputStream;
|
||||
import com.fsck.k9.mail.filter.PeekableInputStream;
|
||||
@ -44,10 +45,8 @@ public class ImapResponseParser {
|
||||
parseUntaggedResponse();
|
||||
readTokens(response);
|
||||
} else if (ch == '+') {
|
||||
response.mCommandContinuationRequested =
|
||||
parseCommandContinuationRequest();
|
||||
//TODO: Add special "resp-text" parsing
|
||||
readTokens(response);
|
||||
response.mCommandContinuationRequested = parseCommandContinuationRequest();
|
||||
parseResponseText(response);
|
||||
} else {
|
||||
response.mTag = parseTaggedResponse();
|
||||
readTokens(response);
|
||||
@ -67,24 +66,72 @@ public class ImapResponseParser {
|
||||
|
||||
private void readTokens(ImapResponse response) throws IOException {
|
||||
response.clear();
|
||||
|
||||
String firstToken = (String) readToken(response);
|
||||
response.add(firstToken);
|
||||
|
||||
if (isStatusResponse(firstToken)) {
|
||||
parseResponseText(response);
|
||||
} else {
|
||||
Object token;
|
||||
while ((token = readToken(response)) != null) {
|
||||
if (!(token instanceof ImapList)) {
|
||||
response.add(token);
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Check for responses ("OK", "PREAUTH", "BYE", "NO", "BAD")
|
||||
* that can contain resp-text tokens. If found, hand over to a special
|
||||
* method that parses a resp-text token. There's no need to use
|
||||
* readToken()/parseToken() on that data.
|
||||
*
|
||||
* See RFC 3501, Section 9 Formal Syntax (resp-text)
|
||||
*/
|
||||
}
|
||||
}
|
||||
response.mCompleted = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse {@code resp-text} tokens
|
||||
*
|
||||
* <p>
|
||||
* Responses "OK", "PREAUTH", "BYE", "NO", "BAD", and continuation request responses can
|
||||
* contain {@code resp-text} tokens. We parse the {@code resp-text-code} part as tokens and
|
||||
* read the rest as sequence of characters to avoid the parser interpreting things like
|
||||
* "{123}" as start of a literal.
|
||||
* </p>
|
||||
* <p>Example:</p>
|
||||
* <p>
|
||||
* {@code * OK [UIDVALIDITY 3857529045] UIDs valid}
|
||||
* </p>
|
||||
* <p>
|
||||
* See RFC 3501, Section 9 Formal Syntax (resp-text)
|
||||
* </p>
|
||||
*
|
||||
* @param parent
|
||||
* The {@link ImapResponse} instance that holds the parsed tokens of the response.
|
||||
*
|
||||
* @throws IOException
|
||||
* If there's a network error.
|
||||
*
|
||||
* @see #isStatusResponse(String)
|
||||
*/
|
||||
private void parseResponseText(ImapResponse parent) throws IOException {
|
||||
skipIfSpace();
|
||||
|
||||
int next = mIn.peek();
|
||||
if (next == '[') {
|
||||
parseSequence(parent);
|
||||
skipIfSpace();
|
||||
}
|
||||
|
||||
String rest = readStringUntil('\r');
|
||||
expect('\n');
|
||||
|
||||
if (!TextUtils.isEmpty(rest)) {
|
||||
// The rest is free-form text.
|
||||
parent.add(rest);
|
||||
}
|
||||
}
|
||||
|
||||
private void skipIfSpace() throws IOException {
|
||||
if (mIn.peek() == ' ') {
|
||||
expect(' ');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next token of the response. The token can be one of: String -
|
||||
* for NIL, QUOTED, NUMBER, ATOM. Object - for LITERAL.
|
||||
@ -480,6 +527,14 @@ public class ImapResponseParser {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isStatusResponse(String symbol) {
|
||||
return symbol.equalsIgnoreCase("OK") ||
|
||||
symbol.equalsIgnoreCase("NO") ||
|
||||
symbol.equalsIgnoreCase("BAD") ||
|
||||
symbol.equalsIgnoreCase("PREAUTH") ||
|
||||
symbol.equalsIgnoreCase("BYE");
|
||||
}
|
||||
|
||||
public static boolean equalsIgnoreCase(Object o1, Object o2) {
|
||||
if (o1 != null && o2 != null && o1 instanceof String && o2 instanceof String) {
|
||||
String s1 = (String)o1;
|
||||
|
@ -12,7 +12,6 @@ import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import com.fsck.k9.K9;
|
||||
import com.fsck.k9.helper.AutoSyncHelper;
|
||||
|
||||
public class BootReceiver extends CoreReceiver {
|
||||
|
||||
@ -41,7 +40,7 @@ public class BootReceiver extends CoreReceiver {
|
||||
} else if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
|
||||
MailService.connectivityChange(context, tmpWakeLockId);
|
||||
tmpWakeLockId = null;
|
||||
} else if (AutoSyncHelper.SYNC_CONN_STATUS_CHANGE.equals(action)) {
|
||||
} else if ("com.android.sync.SYNC_CONN_STATUS_CHANGED".equals(action)) {
|
||||
K9.BACKGROUND_OPS bOps = K9.getBackgroundOps();
|
||||
if (bOps == K9.BACKGROUND_OPS.WHEN_CHECKED_AUTO_SYNC) {
|
||||
MailService.actionReset(context, tmpWakeLockId);
|
||||
|
@ -199,9 +199,7 @@ public abstract class CoreService extends Service {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void onStart(Intent intent, int startId) {
|
||||
// deprecated method but still used for backwards compatibility with Android version <2.0
|
||||
|
||||
public final int onStartCommand(Intent intent, int flags, int startId) {
|
||||
/*
|
||||
* When a process is killed due to low memory, it's later restarted and services that were
|
||||
* started with START_STICKY are started with the intent being null.
|
||||
@ -213,7 +211,7 @@ public abstract class CoreService extends Service {
|
||||
*/
|
||||
if (intent == null) {
|
||||
stopSelf(startId);
|
||||
return;
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
// Acquire new wake lock
|
||||
@ -253,9 +251,9 @@ public abstract class CoreService extends Service {
|
||||
|
||||
// Run the actual start-code of the service
|
||||
mImmediateShutdown = true;
|
||||
int startFlag;
|
||||
try {
|
||||
super.onStart(intent, startId);
|
||||
startService(intent, startId);
|
||||
startFlag = startService(intent, startId);
|
||||
} finally {
|
||||
try {
|
||||
// Release the wake lock acquired at the start of this method
|
||||
@ -267,9 +265,12 @@ public abstract class CoreService extends Service {
|
||||
// this service.
|
||||
if (mAutoShutdown && mImmediateShutdown && startId != -1) {
|
||||
stopSelf(startId);
|
||||
startFlag = START_NOT_STICKY;
|
||||
}
|
||||
} catch (Exception e) { /* ignore */ }
|
||||
}
|
||||
|
||||
return startFlag;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -371,7 +372,7 @@ public abstract class CoreService extends Service {
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses need to implement this instead of overriding {@link #onStart(Intent, int)}.
|
||||
* Subclasses need to implement this instead of overriding {@link #onStartCommand(Intent, int, int)}.
|
||||
*
|
||||
* <p>
|
||||
* This allows {@link CoreService} to manage the service lifecycle, incl. wake lock management.
|
||||
@ -382,8 +383,12 @@ public abstract class CoreService extends Service {
|
||||
* @param startId
|
||||
* A unique integer representing this specific request to start. Use with
|
||||
* {@link #stopSelfResult(int)}.
|
||||
*
|
||||
* @return The return value indicates what semantics the system should use for the service's
|
||||
* current started state. It may be one of the constants associated with the
|
||||
* {@link Service#START_CONTINUATION_MASK} bits.
|
||||
*/
|
||||
public abstract void startService(Intent intent, int startId);
|
||||
public abstract int startService(Intent intent, int startId);
|
||||
|
||||
@Override
|
||||
public void onLowMemory() {
|
||||
|
@ -4,6 +4,7 @@ package com.fsck.k9.service;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
@ -18,7 +19,6 @@ import com.fsck.k9.K9;
|
||||
import com.fsck.k9.Preferences;
|
||||
import com.fsck.k9.Account.FolderMode;
|
||||
import com.fsck.k9.controller.MessagingController;
|
||||
import com.fsck.k9.helper.AutoSyncHelper;
|
||||
import com.fsck.k9.mail.Pusher;
|
||||
|
||||
public class MailService extends CoreService {
|
||||
@ -84,7 +84,7 @@ public class MailService extends CoreService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startService(Intent intent, int startId) {
|
||||
public int startService(Intent intent, int startId) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
boolean oldIsSyncDisabled = isSyncDisabled();
|
||||
ConnectivityManager connectivityManager = (ConnectivityManager)getApplication().getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
@ -98,12 +98,7 @@ public class MailService extends CoreService {
|
||||
hasConnectivity = state == State.CONNECTED;
|
||||
}
|
||||
boolean backgroundData = connectivityManager.getBackgroundDataSetting();
|
||||
boolean autoSync = true;
|
||||
if (AutoSyncHelper.isAvailable()) {
|
||||
autoSync = AutoSyncHelper.getMasterSyncAutomatically();
|
||||
|
||||
Log.i(K9.LOG_TAG, "AutoSync help is available, autoSync = " + autoSync);
|
||||
}
|
||||
boolean autoSync = ContentResolver.getMasterSyncAutomatically();
|
||||
|
||||
K9.BACKGROUND_OPS bOps = K9.getBackgroundOps();
|
||||
|
||||
@ -170,6 +165,8 @@ public class MailService extends CoreService {
|
||||
|
||||
if (K9.DEBUG)
|
||||
Log.i(K9.LOG_TAG, "MailService.onStart took " + (System.currentTimeMillis() - startTime) + "ms");
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -42,7 +42,7 @@ public class PollService extends CoreService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startService(Intent intent, int startId) {
|
||||
public int startService(Intent intent, int startId) {
|
||||
if (START_SERVICE.equals(intent.getAction())) {
|
||||
if (K9.DEBUG)
|
||||
Log.i(K9.LOG_TAG, "PollService started with startId = " + startId);
|
||||
@ -68,6 +68,7 @@ public class PollService extends CoreService {
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -27,7 +27,8 @@ public class PushService extends CoreService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startService(Intent intent, int startId) {
|
||||
public int startService(Intent intent, int startId) {
|
||||
int startFlag = START_STICKY;
|
||||
if (START_SERVICE.equals(intent.getAction())) {
|
||||
if (K9.DEBUG)
|
||||
Log.i(K9.LOG_TAG, "PushService started with startId = " + startId);
|
||||
@ -35,8 +36,10 @@ public class PushService extends CoreService {
|
||||
if (K9.DEBUG)
|
||||
Log.i(K9.LOG_TAG, "PushService stopping with startId = " + startId);
|
||||
stopSelf(startId);
|
||||
startFlag = START_NOT_STICKY;
|
||||
}
|
||||
|
||||
return startFlag;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -34,7 +34,7 @@ public class RemoteControlService extends CoreService {
|
||||
public static final int REMOTE_CONTROL_SERVICE_WAKE_LOCK_TIMEOUT = 20000;
|
||||
|
||||
@Override
|
||||
public void startService(final Intent intent, final int startId) {
|
||||
public int startService(final Intent intent, final int startId) {
|
||||
if (K9.DEBUG)
|
||||
Log.i(K9.LOG_TAG, "RemoteControlService started with startId = " + startId);
|
||||
final Preferences preferences = Preferences.getPreferences(this);
|
||||
@ -155,6 +155,8 @@ public class RemoteControlService extends CoreService {
|
||||
}
|
||||
, RemoteControlService.REMOTE_CONTROL_SERVICE_WAKE_LOCK_TIMEOUT, startId);
|
||||
}
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -113,12 +113,13 @@ public class SleepService extends CoreService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startService(Intent intent, int startId) {
|
||||
public int startService(Intent intent, int startId) {
|
||||
try {
|
||||
if (intent.getAction().startsWith(ALARM_FIRED)) {
|
||||
Integer id = intent.getIntExtra(LATCH_ID, -1);
|
||||
endSleep(id);
|
||||
}
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
finally {
|
||||
stopSelf(startId);
|
||||
|
@ -91,9 +91,7 @@ public class MessageWebView extends WebView {
|
||||
|
||||
// SINGLE_COLUMN layout was broken on Android < 2.2, so we
|
||||
// administratively disable it
|
||||
if (
|
||||
(Integer.parseInt(Build.VERSION.SDK) > 7)
|
||||
&& K9.mobileOptimizedLayout()) {
|
||||
if (Build.VERSION.SDK_INT > 7 && K9.mobileOptimizedLayout()) {
|
||||
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
|
||||
} else {
|
||||
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
|
||||
|
67
tests/src/com/fsck/k9/mail/store/ImapResponseParserTest.java
Normal file
67
tests/src/com/fsck/k9/mail/store/ImapResponseParserTest.java
Normal file
@ -0,0 +1,67 @@
|
||||
package com.fsck.k9.mail.store;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import com.fsck.k9.mail.filter.PeekableInputStream;
|
||||
import com.fsck.k9.mail.store.ImapResponseParser.ImapList;
|
||||
import com.fsck.k9.mail.store.ImapResponseParser.ImapResponse;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class ImapResponseParserTest extends TestCase {
|
||||
|
||||
public void testSimpleOkResponse() throws IOException {
|
||||
ImapResponseParser parser = createParser("* OK\r\n");
|
||||
ImapResponse response = parser.readResponse();
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(1, response.size());
|
||||
assertEquals("OK", response.get(0));
|
||||
}
|
||||
|
||||
public void testOkResponseWithText() throws IOException {
|
||||
ImapResponseParser parser = createParser("* OK Some text here\r\n");
|
||||
ImapResponse response = parser.readResponse();
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(2, response.size());
|
||||
assertEquals("OK", response.get(0));
|
||||
assertEquals("Some text here", response.get(1));
|
||||
}
|
||||
|
||||
public void testOkResponseWithRespTextCode() throws IOException {
|
||||
ImapResponseParser parser = createParser("* OK [UIDVALIDITY 3857529045]\r\n");
|
||||
ImapResponse response = parser.readResponse();
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(2, response.size());
|
||||
assertEquals("OK", response.get(0));
|
||||
assertTrue(response.get(1) instanceof ImapList);
|
||||
|
||||
ImapList respTextCode = (ImapList) response.get(1);
|
||||
assertEquals(2, respTextCode.size());
|
||||
assertEquals("UIDVALIDITY", respTextCode.get(0));
|
||||
assertEquals("3857529045", respTextCode.get(1));
|
||||
}
|
||||
|
||||
public void testOkResponseWithRespTextCodeAndText() throws IOException {
|
||||
ImapResponseParser parser = createParser("* OK [token1 token2] {x} test [...]\r\n");
|
||||
ImapResponse response = parser.readResponse();
|
||||
|
||||
assertNotNull(response);
|
||||
assertEquals(3, response.size());
|
||||
assertEquals("OK", response.get(0));
|
||||
assertTrue(response.get(1) instanceof ImapList);
|
||||
assertEquals("{x} test [...]", response.get(2));
|
||||
|
||||
ImapList respTextCode = (ImapList) response.get(1);
|
||||
assertEquals(2, respTextCode.size());
|
||||
assertEquals("token1", respTextCode.get(0));
|
||||
assertEquals("token2", respTextCode.get(1));
|
||||
}
|
||||
|
||||
private ImapResponseParser createParser(String response) {
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(response.getBytes());
|
||||
PeekableInputStream pin = new PeekableInputStream(in);
|
||||
return new ImapResponseParser(pin);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user