1
0
mirror of https://github.com/moparisthebest/k-9 synced 2025-01-09 12:48:02 -05:00

Merge remote-tracking branch 'origin/issue-619_AndroidWearSupport' into issue-619_AndroidWearSupport

# Conflicts:
#	k9mail/src/main/java/com/fsck/k9/controller/MessagingController.java
#	k9mail/src/main/java/com/fsck/k9/service/NotificationActionService.java
This commit is contained in:
Marcus Wolschon 2015-07-05 11:23:20 +02:00
commit 92cfe149e2
44 changed files with 268 additions and 275 deletions

View File

@ -4,7 +4,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:1.0.0' classpath 'com.android.tools.build:gradle:1.2.3'
classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.0' classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.0'
} }
} }

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<lint> <lint>
<issue id="MissingTranslation" severity="ignore" /> <issue id="MissingTranslation" severity="ignore" />
<issue id="OldTargetApi" severity="ignore" />
<!-- Transifex and Lint disagree on what quantities are necessary --> <!-- Transifex and Lint disagree on what quantities are necessary -->
<issue id="UnusedQuantity" severity="warning"> <issue id="UnusedQuantity" severity="warning">

View File

@ -1,14 +1,29 @@
apply plugin: 'findbugs' apply plugin: 'findbugs'
check.dependsOn 'findbugs' afterEvaluate {
task findbugs(type: FindBugs, dependsOn: ['compileDebugJava', 'compileDebugTestJava']) { def variants = plugins.hasPlugin('com.android.application') ?
ignoreFailures = true android.applicationVariants : android.libraryVariants
classes = fileTree('build/intermediates/classes/debug/') +
fileTree('build/intermediates/classes/test/debug/') variants.each { variant ->
source = project.android.sourceSets.main.java.getSrcDirs() + def task = project.task("findBugs${variant.name.capitalize()}", type: FindBugs) {
project.android.sourceSets.androidTest.java.getSrcDirs() group = 'verification'
classpath = files() description = "Run FindBugs for the ${variant.description}."
effort = 'max' effort = 'max'
ignoreFailures = true
includeFilter = file("$rootProject.projectDir/config/findbugs/include_filter.xml") includeFilter = file("$rootProject.projectDir/config/findbugs/include_filter.xml")
excludeFilter = file("$rootProject.projectDir/config/findbugs/exclude_filter.xml") excludeFilter = file("$rootProject.projectDir/config/findbugs/exclude_filter.xml")
def variantCompile = variant.javaCompile
classes = fileTree(variantCompile.destinationDir)
source = variantCompile.source
classpath = variantCompile.classpath.plus(project.files(android.bootClasspath))
dependsOn(variantCompile)
}
tasks.getByName('check').dependsOn(task)
}
} }

View File

@ -1,6 +1,7 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply from: '../gradle/plugins/checkstyle-android.gradle' apply from: '../gradle/plugins/checkstyle-android.gradle'
apply from: '../gradle/plugins/findbugs-android.gradle' apply from: '../gradle/plugins/findbugs-android.gradle'
apply plugin: 'jacoco'
repositories { repositories {
jcenter() jcenter()
@ -12,6 +13,17 @@ dependencies {
compile 'commons-io:commons-io:2.4' compile 'commons-io:commons-io:2.4'
compile 'com.jcraft:jzlib:1.0.7' compile 'com.jcraft:jzlib:1.0.7'
compile 'com.beetstra.jutf7:jutf7:1.0.0' compile 'com.beetstra.jutf7:jutf7:1.0.0'
androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
androidTestCompile 'com.madgag.spongycastle:pg:1.51.0.0'
testCompile('org.robolectric:robolectric:3.0-rc3') {
exclude group: 'org.hamcrest', module: 'hamcrest-core'
}
testCompile 'org.hamcrest:hamcrest-core:1.3'
testCompile('junit:junit:4.10') {
exclude group: 'org.hamcrest', module: 'hamcrest-core'
}
} }
android { android {
@ -21,6 +33,14 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 15 minSdkVersion 15
targetSdkVersion 21 targetSdkVersion 21
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
testCoverageEnabled rootProject.testCoverage
}
} }
lintOptions { lintOptions {
@ -40,5 +60,6 @@ android {
exclude 'META-INF/LICENSE.txt' exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE' exclude 'META-INF/NOTICE'
exclude 'META-INF/NOTICE.txt' exclude 'META-INF/NOTICE.txt'
exclude 'LICENSE.txt'
} }
} }

View File

@ -55,7 +55,7 @@ public class K9MailLib {
} }
} }
public static interface DebugStatus { public interface DebugStatus {
boolean enabled(); boolean enabled();
boolean debugSensitive(); boolean debugSensitive();
@ -68,7 +68,7 @@ public class K9MailLib {
debugStatus = status; debugStatus = status;
} }
private static interface WritableDebugStatus extends DebugStatus { private interface WritableDebugStatus extends DebugStatus {
void setEnabled(boolean enabled); void setEnabled(boolean enabled);
void setSensitive(boolean sensitive); void setSensitive(boolean sensitive);

View File

@ -46,24 +46,26 @@ public class BinaryTempFileBody implements RawDataBody, SizeAware {
try { try {
File newFile = File.createTempFile("body", null, mTempDirectory); File newFile = File.createTempFile("body", null, mTempDirectory);
OutputStream out = new FileOutputStream(newFile); final OutputStream out = new FileOutputStream(newFile);
try { try {
OutputStream wrappedOut = null;
if (MimeUtil.ENC_QUOTED_PRINTABLE.equals(encoding)) { if (MimeUtil.ENC_QUOTED_PRINTABLE.equals(encoding)) {
out = new QuotedPrintableOutputStream(out, false); wrappedOut = new QuotedPrintableOutputStream(out, false);
} else if (MimeUtil.ENC_BASE64.equals(encoding)) { } else if (MimeUtil.ENC_BASE64.equals(encoding)) {
out = new Base64OutputStream(out); wrappedOut = new Base64OutputStream(out);
} else { } else {
throw new RuntimeException("Target encoding not supported: " + encoding); throw new RuntimeException("Target encoding not supported: " + encoding);
} }
InputStream in = getInputStream(); InputStream in = getInputStream();
try { try {
IOUtils.copy(in, out); IOUtils.copy(in, wrappedOut);
} finally { } finally {
in.close(); IOUtils.closeQuietly(in);
IOUtils.closeQuietly(wrappedOut);
} }
} finally { } finally {
out.close(); IOUtils.closeQuietly(out);
} }
mFile = newFile; mFile = newFile;
@ -100,7 +102,7 @@ public class BinaryTempFileBody implements RawDataBody, SizeAware {
try { try {
IOUtils.copy(in, out); IOUtils.copy(in, out);
} finally { } finally {
in.close(); IOUtils.closeQuietly(in);
} }
} }

View File

@ -46,7 +46,7 @@ public class BinaryTempFileMessageBody extends BinaryTempFileBody implements Com
IOUtils.copy(in, out); IOUtils.copy(in, out);
} }
} finally { } finally {
in.close(); IOUtils.closeQuietly(in);
} }
} }

View File

@ -1,16 +1,5 @@
package com.fsck.k9.mail.ssl; package com.fsck.k9.mail.ssl;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import com.fsck.k9.mail.MessagingException;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.IOException; import java.io.IOException;
import java.net.Socket; import java.net.Socket;
@ -20,6 +9,17 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import com.fsck.k9.mail.MessagingException;
import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import static com.fsck.k9.mail.K9MailLib.LOG_TAG; import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
@ -27,11 +27,16 @@ import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
* Filter and reorder list of cipher suites and TLS versions. * Filter and reorder list of cipher suites and TLS versions.
*/ */
public class DefaultTrustedSocketFactory implements TrustedSocketFactory { public class DefaultTrustedSocketFactory implements TrustedSocketFactory {
protected static final String ENABLED_CIPHERS[]; protected static final String[] ENABLED_CIPHERS;
protected static final String ENABLED_PROTOCOLS[]; protected static final String[] ENABLED_PROTOCOLS;
// Order taken from OpenSSL 1.0.1c protected static final String[] ORDERED_KNOWN_CIPHERS = {
protected static final String ORDERED_KNOWN_CIPHERS[] = { "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_DHE_RSA_WITH_AES_256_GCM_SHA384",
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_256_CBC_SHA", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA",
@ -43,7 +48,6 @@ public class DefaultTrustedSocketFactory implements TrustedSocketFactory {
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
"SSL_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA",
@ -51,14 +55,6 @@ public class DefaultTrustedSocketFactory implements TrustedSocketFactory {
"TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
"TLS_ECDH_RSA_WITH_RC4_128_SHA",
"TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
"SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
"SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
"SSL_RSA_WITH_RC4_128_SHA",
"SSL_RSA_WITH_RC4_128_MD5",
}; };
protected static final String[] BLACKLISTED_CIPHERS = { protected static final String[] BLACKLISTED_CIPHERS = {
@ -69,10 +65,23 @@ public class DefaultTrustedSocketFactory implements TrustedSocketFactory {
"SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
"SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA",
"SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA",
"SSL_RSA_WITH_3DES_EDE_CBC_SHA",
"SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA",
"SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_RSA_WITH_RC4_128_SHA",
"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
"TLS_ECDH_RSA_WITH_RC4_128_SHA",
"TLS_ECDH_ECDSA_WITH_RC4_128_SHA",
"SSL_RSA_WITH_RC4_128_SHA",
"SSL_RSA_WITH_RC4_128_MD5",
}; };
protected static final String ORDERED_KNOWN_PROTOCOLS[] = { protected static final String[] ORDERED_KNOWN_PROTOCOLS = {
"TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3" "TLSv1.2", "TLSv1.1", "TLSv1"
};
protected static final String[] BLACKLISTED_PROTOCOLS = {
"SSLv3"
}; };
static { static {
@ -101,7 +110,7 @@ public class DefaultTrustedSocketFactory implements TrustedSocketFactory {
reorder(enabledCiphers, ORDERED_KNOWN_CIPHERS, BLACKLISTED_CIPHERS); reorder(enabledCiphers, ORDERED_KNOWN_CIPHERS, BLACKLISTED_CIPHERS);
ENABLED_PROTOCOLS = (supportedProtocols == null) ? null : ENABLED_PROTOCOLS = (supportedProtocols == null) ? null :
reorder(supportedProtocols, ORDERED_KNOWN_PROTOCOLS, null); reorder(supportedProtocols, ORDERED_KNOWN_PROTOCOLS, BLACKLISTED_PROTOCOLS);
} }
public DefaultTrustedSocketFactory(Context context) { public DefaultTrustedSocketFactory(Context context) {

View File

@ -2,10 +2,15 @@ package com.fsck.k9.mail;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class AddressTest { public class AddressTest {
/** /**
* test the possibility to parse "From:" fields with no email. * test the possibility to parse "From:" fields with no email.

View File

@ -2,10 +2,15 @@ package com.fsck.k9.mail;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class Address_quoteAtoms { public class Address_quoteAtoms {
@Test @Test
public void testNoQuote() { public void testNoQuote() {

View File

@ -2,10 +2,15 @@ package com.fsck.k9.mail.internet;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class CharsetSupportTest { public class CharsetSupportTest {
@Test @Test

View File

@ -2,10 +2,15 @@ package com.fsck.k9.mail.internet;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class DecoderUtilTest { public class DecoderUtilTest {
@Test @Test

View File

@ -18,10 +18,15 @@ import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.BodyPart; import com.fsck.k9.mail.BodyPart;
import com.fsck.k9.mail.Message.RecipientType; import com.fsck.k9.mail.Message.RecipientType;
import com.fsck.k9.mail.Multipart; import com.fsck.k9.mail.Multipart;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class MimeMessageParseTest { public class MimeMessageParseTest {
@Before @Before
public void setup() { public void setup() {

View File

@ -3,6 +3,9 @@ package com.fsck.k9.mail.store.imap;
import com.fsck.k9.mail.filter.PeekableInputStream; import com.fsck.k9.mail.filter.PeekableInputStream;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
@ -16,6 +19,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class ImapResponseParserTest { public class ImapResponseParserTest {
@Test public void testSimpleOkResponse() throws IOException { @Test public void testSimpleOkResponse() throws IOException {

View File

@ -18,12 +18,17 @@
package com.fsck.k9.mail.store.imap; package com.fsck.k9.mail.store.imap;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.List; import java.util.List;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class ImapUtilityTest { public class ImapUtilityTest {
@Test @Test
public void testGetImapSequenceValues() { public void testGetImapSequenceValues() {

View File

@ -2,6 +2,7 @@ apply plugin: 'android-sdk-manager'
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply from: '../gradle/plugins/checkstyle-android.gradle' apply from: '../gradle/plugins/checkstyle-android.gradle'
apply from: '../gradle/plugins/findbugs-android.gradle' apply from: '../gradle/plugins/findbugs-android.gradle'
apply plugin: 'jacoco'
repositories { repositories {
jcenter() jcenter()
@ -24,7 +25,14 @@ dependencies {
androidTestCompile('com.icegreen:greenmail:1.4.1') { androidTestCompile('com.icegreen:greenmail:1.4.1') {
exclude group: 'junit' exclude group: 'junit'
} }
androidTestCompile 'com.madgag.spongycastle:pg:1.51.0.0'
testCompile('org.robolectric:robolectric:3.0-rc3') {
exclude group: 'org.hamcrest', module: 'hamcrest-core'
}
testCompile 'org.hamcrest:hamcrest-core:1.3'
testCompile('junit:junit:4.10') {
exclude group: 'org.hamcrest', module: 'hamcrest-core'
}
} }
android { android {

View File

@ -2,8 +2,8 @@
<manifest <manifest
package="com.fsck.k9" package="com.fsck.k9"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="23050" android:versionCode="23060"
android:versionName="5.105"> android:versionName="5.106">
<uses-feature <uses-feature
android:name="android.hardware.touchscreen" android:name="android.hardware.touchscreen"

View File

@ -1404,7 +1404,7 @@ public class Account implements BaseAccount, StoreConfig {
if (i < identities.size()) { if (i < identities.size()) {
return identities.get(i); return identities.get(i);
} }
return null; throw new IllegalArgumentException("Identity with index " + i + " not found");
} }
public boolean isAnIdentity(Address[] addrs) { public boolean isAnIdentity(Address[] addrs) {

View File

@ -1183,10 +1183,8 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
if (menuInfo != null) { if (menuInfo != null) {
mSelectedContextAccount = (BaseAccount)getListView().getItemAtPosition(menuInfo.position); mSelectedContextAccount = (BaseAccount)getListView().getItemAtPosition(menuInfo.position);
} }
Account realAccount = null;
if (mSelectedContextAccount instanceof Account) { if (mSelectedContextAccount instanceof Account) {
realAccount = (Account)mSelectedContextAccount; Account realAccount = (Account)mSelectedContextAccount;
}
switch (item.getItemId()) { switch (item.getItemId()) {
case R.id.delete_account: case R.id.delete_account:
onDeleteAccount(realAccount); onDeleteAccount(realAccount);
@ -1219,6 +1217,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
onMove(realAccount, false); onMove(realAccount, false);
break; break;
} }
}
return true; return true;
} }

View File

@ -220,10 +220,6 @@ public class MessagingController implements Runnable {
* {@link #removeMatchingMessage(android.content.Context, com.fsck.k9.activity.MessageReference)} instead. * {@link #removeMatchingMessage(android.content.Context, com.fsck.k9.activity.MessageReference)} instead.
*/ */
LinkedList<LocalMessage> messages; LinkedList<LocalMessage> messages;
/**
* Stacked notifications that share this notification as ther summary-notification.
*/
Map<String, Integer> stackedNotifications = new HashMap<String, Integer>();
/** /**
* List of references for messages that the user is still to be notified of, * List of references for messages that the user is still to be notified of,
* but which don't fit into the inbox style anymore. It's sorted from newest * but which don't fit into the inbox style anymore. It's sorted from newest
@ -307,8 +303,7 @@ public class MessagingController implements Runnable {
/** /**
* Remove a certain message from the message list. * Remove a certain message from the message list.
* @see #getStackedChildNotification(com.fsck.k9.activity.MessageReference) for stacked *
* notifications you may consider to cancel.
* @param context A context. * @param context A context.
* @param ref Reference of the message to remove * @param ref Reference of the message to remove
* @return true if message was found and removed, false otherwise * @return true if message was found and removed, false otherwise
@ -1343,16 +1338,17 @@ public class MessagingController implements Runnable {
Log.d(K9.LOG_TAG, "SYNC: About to fetch " + unsyncedMessages.size() + " unsynced messages for folder " + folder); Log.d(K9.LOG_TAG, "SYNC: About to fetch " + unsyncedMessages.size() + " unsynced messages for folder " + folder);
fetchUnsyncedMessages(account, remoteFolder, localFolder, unsyncedMessages, smallMessages, largeMessages, progress, todo, fp); fetchUnsyncedMessages(account, remoteFolder, unsyncedMessages, smallMessages, largeMessages, progress, todo, fp);
// If a message didn't exist, messageFinished won't be called, but we shouldn't try again String updatedPushState = localFolder.getPushState();
// If we got here, nothing failed
for (Message message : unsyncedMessages) { for (Message message : unsyncedMessages) {
String newPushState = remoteFolder.getNewPushState(localFolder.getPushState(), message); String newPushState = remoteFolder.getNewPushState(updatedPushState, message);
if (newPushState != null) { if (newPushState != null) {
localFolder.setPushState(newPushState); updatedPushState = newPushState;
} }
} }
localFolder.setPushState(updatedPushState);
if (K9.DEBUG) { if (K9.DEBUG) {
Log.d(K9.LOG_TAG, "SYNC: Synced unsynced messages for folder " + folder); Log.d(K9.LOG_TAG, "SYNC: Synced unsynced messages for folder " + folder);
} }
@ -1490,7 +1486,6 @@ public class MessagingController implements Runnable {
} }
private <T extends Message> void fetchUnsyncedMessages(final Account account, final Folder<T> remoteFolder, private <T extends Message> void fetchUnsyncedMessages(final Account account, final Folder<T> remoteFolder,
final LocalFolder localFolder,
List<T> unsyncedMessages, List<T> unsyncedMessages,
final List<Message> smallMessages, final List<Message> smallMessages,
final List<Message> largeMessages, final List<Message> largeMessages,
@ -1501,22 +1496,12 @@ public class MessagingController implements Runnable {
final Date earliestDate = account.getEarliestPollDate(); final Date earliestDate = account.getEarliestPollDate();
/*
* Messages to be batch written
*/
final List<Message> chunk = new ArrayList<Message>(UNSYNC_CHUNK_SIZE);
remoteFolder.fetch(unsyncedMessages, fp, remoteFolder.fetch(unsyncedMessages, fp,
new MessageRetrievalListener<T>() { new MessageRetrievalListener<T>() {
@Override @Override
public void messageFinished(T message, int number, int ofTotal) { public void messageFinished(T message, int number, int ofTotal) {
try { try {
String newPushState = remoteFolder.getNewPushState(localFolder.getPushState(), message);
if (newPushState != null) {
localFolder.setPushState(newPushState);
}
if (message.isSet(Flag.DELETED) || message.olderThan(earliestDate)) { if (message.isSet(Flag.DELETED) || message.olderThan(earliestDate)) {
if (K9.DEBUG) { if (K9.DEBUG) {
if (message.isSet(Flag.DELETED)) { if (message.isSet(Flag.DELETED)) {
Log.v(K9.LOG_TAG, "Newly downloaded message " + account + ":" + folder + ":" + message.getUid() Log.v(K9.LOG_TAG, "Newly downloaded message " + account + ":" + folder + ":" + message.getUid()
@ -1539,24 +1524,6 @@ public class MessagingController implements Runnable {
} else { } else {
smallMessages.add(message); smallMessages.add(message);
} }
// And include it in the view
if (message.getSubject() != null && message.getFrom() != null) {
/*
* We check to make sure that we got something worth
* showing (subject and from) because some protocols
* (POP) may not be able to give us headers for
* ENVELOPE, only size.
*/
// keep message for delayed storing
chunk.add(message);
if (chunk.size() >= UNSYNC_CHUNK_SIZE) {
writeUnsyncedMessages(chunk, localFolder, account, folder);
chunk.clear();
}
}
} catch (Exception e) { } catch (Exception e) {
Log.e(K9.LOG_TAG, "Error while storing downloaded message.", e); Log.e(K9.LOG_TAG, "Error while storing downloaded message.", e);
addErrorMessage(account, null, e); addErrorMessage(account, null, e);
@ -1572,47 +1539,7 @@ public class MessagingController implements Runnable {
} }
}); });
if (!chunk.isEmpty()) {
writeUnsyncedMessages(chunk, localFolder, account, folder);
chunk.clear();
} }
}
/**
* Actual storing of messages
*
* <br>
* FIXME: <strong>This method should really be moved in the above MessageRetrievalListener once {@link MessageRetrievalListener#messagesFinished(int)} is properly invoked by various stores</strong>
*
* @param messages Never <code>null</code>.
* @param localFolder
* @param account
* @param folder
*/
private void writeUnsyncedMessages(final List<Message> messages, final LocalFolder localFolder, final Account account, final String folder) {
if (K9.DEBUG) {
Log.v(K9.LOG_TAG, "Batch writing " + Integer.toString(messages.size()) + " messages");
}
try {
// Store the new message locally
localFolder.appendMessages(messages);
for (final Message message : messages) {
final LocalMessage localMessage = localFolder.getMessage(message.getUid());
syncFlags(localMessage, message);
if (K9.DEBUG)
Log.v(K9.LOG_TAG, "About to notify listeners that we got a new unsynced message "
+ account + ":" + folder + ":" + message.getUid());
for (final MessagingListener l : getListeners()) {
l.synchronizeMailboxAddOrUpdateMessage(account, folder, localMessage);
}
}
} catch (final Exception e) {
Log.e(K9.LOG_TAG, "Error while storing downloaded message.", e);
addErrorMessage(account, null, e);
}
}
private boolean shouldImportMessage(final Account account, final String folder, final Message message, final AtomicInteger progress, final Date earliestDate) { private boolean shouldImportMessage(final Account account, final String folder, final Message message, final AtomicInteger progress, final Date earliestDate) {
@ -1879,7 +1806,6 @@ public class MessagingController implements Runnable {
synchronized (data) { synchronized (data) {
MessageReference ref = localMessage.makeMessageReference(); MessageReference ref = localMessage.makeMessageReference();
if (data.removeMatchingMessage(context, ref)) { if (data.removeMatchingMessage(context, ref)) {
synchronized (data) {
// if we remove a single message from the notification, // if we remove a single message from the notification,
// maybe there is a stacked notification active for that one message // maybe there is a stacked notification active for that one message
Integer childNotification = data.getStackedChildNotification(ref); Integer childNotification = data.getStackedChildNotification(ref);
@ -1895,7 +1821,6 @@ public class MessagingController implements Runnable {
} }
} }
} }
}
progress.incrementAndGet(); progress.incrementAndGet();
for (MessagingListener l : getListeners()) { for (MessagingListener l : getListeners()) {
l.synchronizeMailboxProgress(account, folder, progress.get(), todo); l.synchronizeMailboxProgress(account, folder, progress.get(), todo);
@ -4688,7 +4613,7 @@ public class MessagingController implements Runnable {
* @return A pending data instance, or null if one doesn't exist and * @return A pending data instance, or null if one doesn't exist and
* previousUnreadMessageCount was passed as null. * previousUnreadMessageCount was passed as null.
*/ */
private NotificationData getNotificationData(Account account, @Nullable Integer previousUnreadMessageCount) { private NotificationData getNotificationData(Account account, Integer previousUnreadMessageCount) {
NotificationData data; NotificationData data;
synchronized (notificationData) { synchronized (notificationData) {
@ -4977,7 +4902,7 @@ public class MessagingController implements Runnable {
// multiple messages pending, show inbox style // multiple messages pending, show inbox style
NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(builder); NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(builder);
int nID = account.getAccountNumber(); int nID = account.getAccountNumber();
for (Message m : data.messages) { for (LocalMessage m : data.messages) {
style.addLine(buildMessageSummary(context, style.addLine(buildMessageSummary(context,
getMessageSender(context, account, m), getMessageSender(context, account, m),
getMessageSubject(context, m))); getMessageSubject(context, m)));
@ -5079,8 +5004,29 @@ public class MessagingController implements Runnable {
context.getString(R.string.notification_action_delete), context.getString(R.string.notification_action_delete),
NotificationDeleteConfirmation.getIntent(context, account, allRefs, account.getAccountNumber())); NotificationDeleteConfirmation.getIntent(context, account, allRefs, account.getAccountNumber()));
} }
if (NotificationActionService.isArchiveAllMessagesWearAvaliable(context, account, data.messages)) {
} else { // no extended notifications supported // Archive on wear
NotificationCompat.Action wearActionArchive =
new NotificationCompat.Action.Builder(
R.drawable.ic_action_delete_dark,
context.getString(R.string.notification_action_archive),
NotificationActionService.getArchiveAllMessagesIntent(context, account, allRefs))
.build();
builder.extend(wearableExtender.addAction(wearActionArchive));
}
if (NotificationActionService.isSpamAllMessagesWearAvaliable(context, account, data.messages)) {
// Archive on wear
NotificationCompat.Action wearActionSpam =
new NotificationCompat.Action.Builder(
R.drawable.ic_action_delete_dark,
context.getString(R.string.notification_action_spam),
NotificationActionService.getSpamAllMessagesIntent(context, account, allRefs))
.build();
builder.extend(wearableExtender.addAction(wearActionSpam));
}
} else {
String accountNotice = context.getString(R.string.notification_new_one_account_fmt, String accountNotice = context.getString(R.string.notification_new_one_account_fmt,
unreadCount, accountDescr); unreadCount, accountDescr);
builder.setContentTitle(accountNotice); builder.setContentTitle(accountNotice);

View File

@ -1024,15 +1024,10 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
} }
private String getFolderNameById(Account account, long folderId) { private String getFolderNameById(Account account, long folderId) {
try {
Folder folder = getFolderById(account, folderId); Folder folder = getFolderById(account, folderId);
if (folder != null) { if (folder != null) {
return folder.getName(); return folder.getName();
} }
} catch (Exception e) {
Log.e(K9.LOG_TAG, "getFolderNameById() failed.", e);
}
return null; return null;
} }
@ -1042,9 +1037,8 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
LocalFolder localFolder = localStore.getFolderById(folderId); LocalFolder localFolder = localStore.getFolderById(folderId);
localFolder.open(Folder.OPEN_MODE_RO); localFolder.open(Folder.OPEN_MODE_RO);
return localFolder; return localFolder;
} catch (Exception e) { } catch (MessagingException e) {
Log.e(K9.LOG_TAG, "getFolderNameById() failed.", e); throw new RuntimeException(e);
return null;
} }
} }
@ -3162,10 +3156,8 @@ public class MessageListFragment extends Fragment implements OnItemClickListener
try { try {
return folder.getMessage(uid); return folder.getMessage(uid);
} catch (MessagingException e) { } catch (MessagingException e) {
Log.e(K9.LOG_TAG, "Something went wrong while fetching a message", e); throw new RuntimeException(e);
} }
return null;
} }
private List<LocalMessage> getCheckedMessages() { private List<LocalMessage> getCheckedMessages() {

View File

@ -2,6 +2,7 @@ package com.fsck.k9.service;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import com.fsck.k9.Account; import com.fsck.k9.Account;

View File

@ -18,6 +18,7 @@
package com.fsck.k9.view; package com.fsck.k9.view;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.webkit.WebView; import android.webkit.WebView;
@ -34,6 +35,7 @@ import com.fsck.k9.helper.Utility;
* contents with percent-based height will force the WebView to infinitely expand (or shrink). * contents with percent-based height will force the WebView to infinitely expand (or shrink).
*/ */
public class RigidWebView extends WebView { public class RigidWebView extends WebView {
private static final boolean NO_THROTTLE = Build.VERSION.SDK_INT >= 21; //Build.VERSION_CODES.LOLLIPOP
public RigidWebView(Context context) { public RigidWebView(Context context) {
super(context); super(context);
@ -64,8 +66,14 @@ public class RigidWebView extends WebView {
@Override @Override
protected void onSizeChanged(int w, int h, int ow, int oh) { protected void onSizeChanged(int w, int h, int ow, int oh) {
if (NO_THROTTLE) {
super.onSizeChanged(w, h, ow, oh);
return;
}
mRealWidth = w; mRealWidth = w;
mRealHeight = h; mRealHeight = h;
long now = mClock.getTime(); long now = mClock.getTime();
boolean recentlySized = (now - mLastSizeChangeTime < MIN_RESIZE_INTERVAL); boolean recentlySized = (now - mLastSizeChangeTime < MIN_RESIZE_INTERVAL);

View File

@ -205,7 +205,7 @@ Please submit bug reports, contribute new features and ask questions at
<item quantity="other"><xliff:g id="new_message_count">%d</xliff:g> new messages</item> <item quantity="other"><xliff:g id="new_message_count">%d</xliff:g> new messages</item>
</plurals> </plurals>
<string name="notification_new_one_account_fmt"><xliff:g id="unread_message_count">%d</xliff:g> Unread (<xliff:g id="account">%s</xliff:g>)</string> <string name="notification_new_one_account_fmt"><xliff:g id="unread_message_count">%d</xliff:g> Unread (<xliff:g id="account">%s</xliff:g>)</string>
<string name="notification_additional_messages">+ <xliff:g id="additional_messages">%d</xliff:g> more on <xliff:g id="account">%s</xliff:g></string> <string name="notification_additional_messages">+ <xliff:g id="additional_messages">%1$d</xliff:g> more on <xliff:g id="account">%2$s</xliff:g></string>
<string name="notification_action_reply">Reply</string> <string name="notification_action_reply">Reply</string>
<string name="notification_action_mark_as_read">Mark Read</string> <string name="notification_action_mark_as_read">Mark Read</string>

View File

@ -8,6 +8,9 @@
They are automatically updated with "ant bump-version". They are automatically updated with "ant bump-version".
--> -->
<changelog> <changelog>
<release version="5.106" versioncode="23060">
<change>Fixed a bug where messages where not always displayed on Android 5.x</change>
</release>
<release version="5.105" versioncode="23050"> <release version="5.105" versioncode="23050">
<change>Reverted all changes introduced with v5.104 except for the bugfixes related to Android 5.1</change> <change>Reverted all changes introduced with v5.104 except for the bugfixes related to Android 5.1</change>
</release> </release>

View File

@ -1,12 +1,12 @@
package com.fsck.k9.activity; package com.fsck.k9.activity;
import android.support.test.runner.AndroidJUnit4;
import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.MessagingException;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertFalse;
@ -14,7 +14,8 @@ import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertTrue;
@RunWith(AndroidJUnit4.class) @RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class MessageReferenceTest { public class MessageReferenceTest {
@Test @Test

View File

@ -3,8 +3,6 @@ package com.fsck.k9.crypto;
import java.util.List; import java.util.List;
import android.support.test.runner.AndroidJUnit4;
import com.fsck.k9.mail.Part; import com.fsck.k9.mail.Part;
import com.fsck.k9.mail.internet.MimeBodyPart; import com.fsck.k9.mail.internet.MimeBodyPart;
import com.fsck.k9.mail.internet.MimeMessage; import com.fsck.k9.mail.internet.MimeMessage;
@ -13,12 +11,15 @@ import com.fsck.k9.mail.internet.MimeMultipart;
import com.fsck.k9.mail.internet.TextBody; import com.fsck.k9.mail.internet.TextBody;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertSame; import static junit.framework.Assert.assertSame;
@RunWith(AndroidJUnit4.class) @RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class MessageDecryptVerifierTest { public class MessageDecryptVerifierTest {
@Test @Test

View File

@ -1,18 +1,22 @@
package com.fsck.k9.helper; package com.fsck.k9.helper;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.File; import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException;
import android.support.test.runner.AndroidJUnit4; import org.apache.commons.io.IOUtils;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertEquals;
@RunWith(AndroidJUnit4.class) @RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class HtmlConverterTest { public class HtmlConverterTest {
// Useful if you want to write stuff to a file for debugging in a browser. // Useful if you want to write stuff to a file for debugging in a browser.
private static final boolean WRITE_TO_FILE = Boolean.parseBoolean(System.getProperty("k9.htmlConverterTest.writeToFile", "false")); private static final boolean WRITE_TO_FILE = Boolean.parseBoolean(System.getProperty("k9.htmlConverterTest.writeToFile", "false"));
@ -134,18 +138,23 @@ public class HtmlConverterTest {
if (!WRITE_TO_FILE) { if (!WRITE_TO_FILE) {
return; return;
} }
FileWriter fstream = null;
try { try {
System.err.println(content); System.err.println(content);
File f = new File(OUTPUT_FILE); File f = new File(OUTPUT_FILE);
f.delete(); f.delete();
FileWriter fstream = new FileWriter(OUTPUT_FILE); fstream = new FileWriter(OUTPUT_FILE);
BufferedWriter out = new BufferedWriter(fstream); BufferedWriter out = new BufferedWriter(fstream);
out.write(content); out.write(content);
out.close(); out.close();
} catch (Exception e) { } catch (IOException e) {
e.printStackTrace(); throw new RuntimeException(e);
} finally {
IOUtils.closeQuietly(fstream);
} }
} }

View File

@ -3,27 +3,29 @@ package com.fsck.k9.helper;
import android.content.Context; import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.text.SpannableString; import android.text.SpannableString;
import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Address;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertTrue;
@RunWith(AndroidJUnit4.class) @RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class MessageHelperTest { public class MessageHelperTest {
private Contacts contacts; private Contacts contacts;
private Contacts mockContacts; private Contacts mockContacts;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
Context context = InstrumentationRegistry.getTargetContext(); Context context = RuntimeEnvironment.application;
contacts = new Contacts(context); contacts = new Contacts(context);
mockContacts = new Contacts(context) { mockContacts = new Contacts(context) {
@Override public String getNameForAddress(String address) { @Override public String getNameForAddress(String address) {

View File

@ -3,6 +3,7 @@ package com.fsck.k9.message;
import com.fsck.k9.Account.QuoteStyle; import com.fsck.k9.Account.QuoteStyle;
import com.fsck.k9.mail.internet.TextBody; import com.fsck.k9.mail.internet.TextBody;
import org.junit.Ignore;
import org.junit.experimental.theories.DataPoints; import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories; import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory; import org.junit.experimental.theories.Theory;
@ -12,6 +13,8 @@ import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
//TODO: Get rid of 'Theories' and write simple tests
@Ignore
@RunWith(Theories.class) @RunWith(Theories.class)
public class TextBodyBuilderTest { public class TextBodyBuilderTest {

View File

@ -3,4 +3,3 @@ include ':k9mail-library'
include ':plugins:Android-PullToRefresh:library' include ':plugins:Android-PullToRefresh:library'
include ':plugins:HoloColorPicker' include ':plugins:HoloColorPicker'
include ':plugins:openpgp-api-library' include ':plugins:openpgp-api-library'
include ':tests-on-jvm'

View File

@ -1,39 +0,0 @@
repositories {
mavenCentral()
}
apply plugin: 'java'
apply plugin: 'findbugs'
apply plugin: 'checkstyle'
apply plugin: 'jacoco'
dependencies {
testCompile project(':k9mail')
testCompile 'junit:junit:4.12'
}
sourceSets {
test {
compileClasspath += files(project(':k9mail').compileDebugJava.destinationDir)
compileClasspath += project(':k9mail').compileDebugJava.classpath
runtimeClasspath += files(project(':k9mail').compileDebugJava.destinationDir)
runtimeClasspath += project(':k9mail').compileDebugJava.classpath
}
}
checkstyle {
ignoreFailures = true
configFile file("$rootProject.projectDir/config/checkstyle/checkstyle.xml")
}
findbugs {
ignoreFailures = true
effort = 'max'
includeFilter = file("$rootProject.projectDir/config/findbugs/include_filter.xml")
excludeFilter = file("$rootProject.projectDir/config/findbugs/exclude_filter.xml")
}
check.dependsOn 'checkstyleTest'
check.dependsOn 'findbugsTest'
compileTestJava.dependsOn ':k9mail:compileDebugJava'

View File

@ -1,7 +0,0 @@
package android.text;
public class TextUtils {
public static boolean isEmpty(CharSequence str) {
return (str == null || str.length() == 0);
}
}

View File

@ -1,11 +0,0 @@
package android.util;
public class Log {
public static int v(String tag, String message) { return 0; }
public static int d(String tag, String message) { return 0; }
public static int d(String tag, String message, Throwable throwable) { return 0; }
public static int i(String tag, String message) { return 0; }
public static int w(String tag, String message) { return 0; }
public static int e(String tag, String message) { return 0; }
public static int e(String tag, String message, Throwable th) { return 0; }
}

View File

@ -1,5 +0,0 @@
package com.fsck.k9;
public class K9 {
public static boolean DEBUG = false;
}