1
0
mirror of https://github.com/moparisthebest/k-9 synced 2025-01-08 04:08:15 -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 {
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'
}
}

View File

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

View File

@ -1,14 +1,29 @@
apply plugin: 'findbugs'
check.dependsOn 'findbugs'
task findbugs(type: FindBugs, dependsOn: ['compileDebugJava', 'compileDebugTestJava']) {
ignoreFailures = true
classes = fileTree('build/intermediates/classes/debug/') +
fileTree('build/intermediates/classes/test/debug/')
source = project.android.sourceSets.main.java.getSrcDirs() +
project.android.sourceSets.androidTest.java.getSrcDirs()
classpath = files()
effort = 'max'
includeFilter = file("$rootProject.projectDir/config/findbugs/include_filter.xml")
excludeFilter = file("$rootProject.projectDir/config/findbugs/exclude_filter.xml")
afterEvaluate {
def variants = plugins.hasPlugin('com.android.application') ?
android.applicationVariants : android.libraryVariants
variants.each { variant ->
def task = project.task("findBugs${variant.name.capitalize()}", type: FindBugs) {
group = 'verification'
description = "Run FindBugs for the ${variant.description}."
effort = 'max'
ignoreFailures = true
includeFilter = file("$rootProject.projectDir/config/findbugs/include_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 from: '../gradle/plugins/checkstyle-android.gradle'
apply from: '../gradle/plugins/findbugs-android.gradle'
apply plugin: 'jacoco'
repositories {
jcenter()
@ -12,6 +13,17 @@ dependencies {
compile 'commons-io:commons-io:2.4'
compile 'com.jcraft:jzlib:1.0.7'
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 {
@ -21,6 +33,14 @@ android {
defaultConfig {
minSdkVersion 15
targetSdkVersion 21
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
testCoverageEnabled rootProject.testCoverage
}
}
lintOptions {
@ -40,5 +60,6 @@ android {
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE'
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 debugSensitive();
@ -68,7 +68,7 @@ public class K9MailLib {
debugStatus = status;
}
private static interface WritableDebugStatus extends DebugStatus {
private interface WritableDebugStatus extends DebugStatus {
void setEnabled(boolean enabled);
void setSensitive(boolean sensitive);

View File

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

View File

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

View File

@ -1,16 +1,5 @@
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.net.Socket;
@ -20,6 +9,17 @@ import java.util.ArrayList;
import java.util.Collections;
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;
@ -27,11 +27,16 @@ import static com.fsck.k9.mail.K9MailLib.LOG_TAG;
* Filter and reorder list of cipher suites and TLS versions.
*/
public class DefaultTrustedSocketFactory implements TrustedSocketFactory {
protected static final String ENABLED_CIPHERS[];
protected static final String ENABLED_PROTOCOLS[];
protected static final String[] ENABLED_CIPHERS;
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_ECDSA_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_ECDH_RSA_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_ECDSA_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_ECDSA_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 = {
@ -69,10 +65,23 @@ public class DefaultTrustedSocketFactory implements TrustedSocketFactory {
"SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
"SSL_DHE_RSA_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[] = {
"TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3"
protected static final String[] ORDERED_KNOWN_PROTOCOLS = {
"TLSv1.2", "TLSv1.1", "TLSv1"
};
protected static final String[] BLACKLISTED_PROTOCOLS = {
"SSLv3"
};
static {
@ -101,7 +110,7 @@ public class DefaultTrustedSocketFactory implements TrustedSocketFactory {
reorder(enabledCiphers, ORDERED_KNOWN_CIPHERS, BLACKLISTED_CIPHERS);
ENABLED_PROTOCOLS = (supportedProtocols == null) ? null :
reorder(supportedProtocols, ORDERED_KNOWN_PROTOCOLS, null);
reorder(supportedProtocols, ORDERED_KNOWN_PROTOCOLS, BLACKLISTED_PROTOCOLS);
}
public DefaultTrustedSocketFactory(Context context) {

View File

@ -2,10 +2,15 @@ package com.fsck.k9.mail;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.assertEquals;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class AddressTest {
/**
* 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.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static org.junit.Assert.assertEquals;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class Address_quoteAtoms {
@Test
public void testNoQuote() {

View File

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

View File

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

View File

@ -18,10 +18,15 @@ import com.fsck.k9.mail.Body;
import com.fsck.k9.mail.BodyPart;
import com.fsck.k9.mail.Message.RecipientType;
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;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class MimeMessageParseTest {
@Before
public void setup() {

View File

@ -3,6 +3,9 @@ package com.fsck.k9.mail.store.imap;
import com.fsck.k9.mail.filter.PeekableInputStream;
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.IOException;
@ -16,6 +19,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class ImapResponseParserTest {
@Test public void testSimpleOkResponse() throws IOException {

View File

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

View File

@ -2,6 +2,7 @@ apply plugin: 'android-sdk-manager'
apply plugin: 'com.android.application'
apply from: '../gradle/plugins/checkstyle-android.gradle'
apply from: '../gradle/plugins/findbugs-android.gradle'
apply plugin: 'jacoco'
repositories {
jcenter()
@ -24,7 +25,14 @@ dependencies {
androidTestCompile('com.icegreen:greenmail:1.4.1') {
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 {

View File

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

View File

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

View File

@ -1183,41 +1183,40 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
if (menuInfo != null) {
mSelectedContextAccount = (BaseAccount)getListView().getItemAtPosition(menuInfo.position);
}
Account realAccount = null;
if (mSelectedContextAccount instanceof Account) {
realAccount = (Account)mSelectedContextAccount;
}
switch (item.getItemId()) {
case R.id.delete_account:
onDeleteAccount(realAccount);
break;
case R.id.account_settings:
onEditAccount(realAccount);
break;
case R.id.activate:
onActivateAccount(realAccount);
break;
case R.id.clear_pending:
onClearCommands(realAccount);
break;
case R.id.empty_trash:
onEmptyTrash(realAccount);
break;
case R.id.clear:
onClear(realAccount);
break;
case R.id.recreate:
onRecreate(realAccount);
break;
case R.id.export:
onExport(false, realAccount);
break;
case R.id.move_up:
onMove(realAccount, true);
break;
case R.id.move_down:
onMove(realAccount, false);
break;
Account realAccount = (Account)mSelectedContextAccount;
switch (item.getItemId()) {
case R.id.delete_account:
onDeleteAccount(realAccount);
break;
case R.id.account_settings:
onEditAccount(realAccount);
break;
case R.id.activate:
onActivateAccount(realAccount);
break;
case R.id.clear_pending:
onClearCommands(realAccount);
break;
case R.id.empty_trash:
onEmptyTrash(realAccount);
break;
case R.id.clear:
onClear(realAccount);
break;
case R.id.recreate:
onRecreate(realAccount);
break;
case R.id.export:
onExport(false, realAccount);
break;
case R.id.move_up:
onMove(realAccount, true);
break;
case R.id.move_down:
onMove(realAccount, false);
break;
}
}
return true;
}

View File

@ -220,10 +220,6 @@ public class MessagingController implements Runnable {
* {@link #removeMatchingMessage(android.content.Context, com.fsck.k9.activity.MessageReference)} instead.
*/
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,
* 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.
* @see #getStackedChildNotification(com.fsck.k9.activity.MessageReference) for stacked
* notifications you may consider to cancel.
*
* @param context A context.
* @param ref Reference of the message to remove
* @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);
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
// If we got here, nothing failed
String updatedPushState = localFolder.getPushState();
for (Message message : unsyncedMessages) {
String newPushState = remoteFolder.getNewPushState(localFolder.getPushState(), message);
String newPushState = remoteFolder.getNewPushState(updatedPushState, message);
if (newPushState != null) {
localFolder.setPushState(newPushState);
updatedPushState = newPushState;
}
}
localFolder.setPushState(updatedPushState);
if (K9.DEBUG) {
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,
final LocalFolder localFolder,
List<T> unsyncedMessages,
final List<Message> smallMessages,
final List<Message> largeMessages,
@ -1501,22 +1496,12 @@ public class MessagingController implements Runnable {
final Date earliestDate = account.getEarliestPollDate();
/*
* Messages to be batch written
*/
final List<Message> chunk = new ArrayList<Message>(UNSYNC_CHUNK_SIZE);
remoteFolder.fetch(unsyncedMessages, fp,
new MessageRetrievalListener<T>() {
@Override
public void messageFinished(T message, int number, int ofTotal) {
try {
String newPushState = remoteFolder.getNewPushState(localFolder.getPushState(), message);
if (newPushState != null) {
localFolder.setPushState(newPushState);
}
if (message.isSet(Flag.DELETED) || message.olderThan(earliestDate)) {
if (K9.DEBUG) {
if (message.isSet(Flag.DELETED)) {
Log.v(K9.LOG_TAG, "Newly downloaded message " + account + ":" + folder + ":" + message.getUid()
@ -1539,24 +1524,6 @@ public class MessagingController implements Runnable {
} else {
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) {
Log.e(K9.LOG_TAG, "Error while storing downloaded message.", e);
addErrorMessage(account, null, e);
@ -1572,48 +1539,8 @@ 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) {
if (account.isSearchByDateCapable() && message.olderThan(earliestDate)) {
@ -1879,18 +1806,16 @@ public class MessagingController implements Runnable {
synchronized (data) {
MessageReference ref = localMessage.makeMessageReference();
if (data.removeMatchingMessage(context, ref)) {
synchronized (data) {
// if we remove a single message from the notification,
// maybe there is a stacked notification active for that one message
Integer childNotification = data.getStackedChildNotification(ref);
if (childNotification != null) {
NotificationManager notificationManager =
(NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(childNotification);
}
// update the (summary-) notification
notifyAccountWithDataLocked(context, account, null, data);
// if we remove a single message from the notification,
// maybe there is a stacked notification active for that one message
Integer childNotification = data.getStackedChildNotification(ref);
if (childNotification != null) {
NotificationManager notificationManager =
(NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(childNotification);
}
// update the (summary-) notification
notifyAccountWithDataLocked(context, account, null, data);
}
}
}
@ -4688,7 +4613,7 @@ public class MessagingController implements Runnable {
* @return A pending data instance, or null if one doesn't exist and
* previousUnreadMessageCount was passed as null.
*/
private NotificationData getNotificationData(Account account, @Nullable Integer previousUnreadMessageCount) {
private NotificationData getNotificationData(Account account, Integer previousUnreadMessageCount) {
NotificationData data;
synchronized (notificationData) {
@ -4977,7 +4902,7 @@ public class MessagingController implements Runnable {
// multiple messages pending, show inbox style
NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(builder);
int nID = account.getAccountNumber();
for (Message m : data.messages) {
for (LocalMessage m : data.messages) {
style.addLine(buildMessageSummary(context,
getMessageSender(context, account, m),
getMessageSubject(context, m)));
@ -5079,8 +5004,29 @@ public class MessagingController implements Runnable {
context.getString(R.string.notification_action_delete),
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,
unreadCount, accountDescr);
builder.setContentTitle(accountNotice);

View File

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

View File

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

View File

@ -18,6 +18,7 @@
package com.fsck.k9.view;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
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).
*/
public class RigidWebView extends WebView {
private static final boolean NO_THROTTLE = Build.VERSION.SDK_INT >= 21; //Build.VERSION_CODES.LOLLIPOP
public RigidWebView(Context context) {
super(context);
@ -64,8 +66,14 @@ public class RigidWebView extends WebView {
@Override
protected void onSizeChanged(int w, int h, int ow, int oh) {
if (NO_THROTTLE) {
super.onSizeChanged(w, h, ow, oh);
return;
}
mRealWidth = w;
mRealHeight = h;
long now = mClock.getTime();
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>
</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_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_mark_as_read">Mark Read</string>

View File

@ -8,6 +8,9 @@
They are automatically updated with "ant bump-version".
-->
<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">
<change>Reverted all changes introduced with v5.104 except for the bugfixes related to Android 5.1</change>
</release>

View File

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

View File

@ -3,8 +3,6 @@ package com.fsck.k9.crypto;
import java.util.List;
import android.support.test.runner.AndroidJUnit4;
import com.fsck.k9.mail.Part;
import com.fsck.k9.mail.internet.MimeBodyPart;
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 org.junit.Test;
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.assertSame;
@RunWith(AndroidJUnit4.class)
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class MessageDecryptVerifierTest {
@Test

View File

@ -1,18 +1,22 @@
package com.fsck.k9.helper;
import java.io.BufferedWriter;
import java.io.File;
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.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static junit.framework.Assert.assertEquals;
@RunWith(AndroidJUnit4.class)
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class HtmlConverterTest {
// 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"));
@ -134,18 +138,23 @@ public class HtmlConverterTest {
if (!WRITE_TO_FILE) {
return;
}
FileWriter fstream = null;
try {
System.err.println(content);
File f = new File(OUTPUT_FILE);
f.delete();
FileWriter fstream = new FileWriter(OUTPUT_FILE);
fstream = new FileWriter(OUTPUT_FILE);
BufferedWriter out = new BufferedWriter(fstream);
out.write(content);
out.close();
} catch (Exception e) {
e.printStackTrace();
} catch (IOException e) {
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.graphics.Color;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.text.SpannableString;
import com.fsck.k9.mail.Address;
import org.junit.Before;
import org.junit.Test;
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.assertTrue;
@RunWith(AndroidJUnit4.class)
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class MessageHelperTest {
private Contacts contacts;
private Contacts mockContacts;
@Before
public void setUp() throws Exception {
Context context = InstrumentationRegistry.getTargetContext();
Context context = RuntimeEnvironment.application;
contacts = new Contacts(context);
mockContacts = new Contacts(context) {
@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.mail.internet.TextBody;
import org.junit.Ignore;
import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
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.junit.Assert.assertThat;
//TODO: Get rid of 'Theories' and write simple tests
@Ignore
@RunWith(Theories.class)
public class TextBodyBuilderTest {

View File

@ -3,4 +3,3 @@ include ':k9mail-library'
include ':plugins:Android-PullToRefresh:library'
include ':plugins:HoloColorPicker'
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;
}