2010-08-17 22:49:13 -04:00
|
|
|
package com.fsck.k9.provider;
|
|
|
|
|
2010-10-05 16:23:07 -04:00
|
|
|
import java.lang.ref.WeakReference;
|
2010-08-17 22:49:13 -04:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collections;
|
2010-09-04 02:57:25 -04:00
|
|
|
import java.util.List;
|
2010-09-26 12:01:41 -04:00
|
|
|
import java.util.concurrent.BlockingQueue;
|
2010-10-05 16:23:07 -04:00
|
|
|
import java.util.concurrent.Executors;
|
|
|
|
import java.util.concurrent.ScheduledExecutorService;
|
|
|
|
import java.util.concurrent.Semaphore;
|
2010-09-26 12:01:41 -04:00
|
|
|
import java.util.concurrent.SynchronousQueue;
|
2010-10-05 16:23:07 -04:00
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
2010-08-17 22:49:13 -04:00
|
|
|
|
|
|
|
import android.content.ContentProvider;
|
2010-10-05 16:23:07 -04:00
|
|
|
import android.content.ContentResolver;
|
2010-08-17 22:49:13 -04:00
|
|
|
import android.content.ContentValues;
|
2010-10-03 06:56:16 -04:00
|
|
|
import android.content.Context;
|
2010-08-17 22:49:13 -04:00
|
|
|
import android.content.UriMatcher;
|
2010-10-05 16:23:07 -04:00
|
|
|
import android.database.CharArrayBuffer;
|
|
|
|
import android.database.ContentObserver;
|
|
|
|
import android.database.CrossProcessCursor;
|
2010-08-17 22:49:13 -04:00
|
|
|
import android.database.Cursor;
|
2010-10-05 16:23:07 -04:00
|
|
|
import android.database.CursorWindow;
|
|
|
|
import android.database.DataSetObserver;
|
2010-08-17 22:49:13 -04:00
|
|
|
import android.database.MatrixCursor;
|
|
|
|
import android.net.Uri;
|
2010-10-05 16:23:07 -04:00
|
|
|
import android.os.Bundle;
|
2010-10-06 16:14:33 -04:00
|
|
|
import android.provider.BaseColumns;
|
2010-08-17 22:49:13 -04:00
|
|
|
import android.util.Log;
|
|
|
|
|
|
|
|
import com.fsck.k9.Account;
|
|
|
|
import com.fsck.k9.AccountStats;
|
|
|
|
import com.fsck.k9.K9;
|
2010-09-04 02:57:25 -04:00
|
|
|
import com.fsck.k9.Preferences;
|
|
|
|
import com.fsck.k9.SearchAccount;
|
2010-10-03 06:56:16 -04:00
|
|
|
import com.fsck.k9.activity.FolderInfoHolder;
|
2010-09-04 02:57:25 -04:00
|
|
|
import com.fsck.k9.activity.MessageInfoHolder;
|
2010-09-21 18:12:45 -04:00
|
|
|
import com.fsck.k9.activity.MessageList;
|
2010-08-17 22:49:13 -04:00
|
|
|
import com.fsck.k9.controller.MessagingController;
|
|
|
|
import com.fsck.k9.controller.MessagingListener;
|
2010-10-03 06:56:16 -04:00
|
|
|
import com.fsck.k9.helper.MessageHelper;
|
2010-08-17 22:49:13 -04:00
|
|
|
import com.fsck.k9.mail.Folder;
|
|
|
|
import com.fsck.k9.mail.Message;
|
|
|
|
import com.fsck.k9.mail.MessagingException;
|
|
|
|
import com.fsck.k9.mail.store.LocalStore;
|
|
|
|
|
|
|
|
public class MessageProvider extends ContentProvider
|
|
|
|
{
|
|
|
|
|
2010-10-06 16:14:33 -04:00
|
|
|
public static interface MessageColumns extends BaseColumns
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* The number of milliseconds since Jan. 1, 1970, midnight GMT.
|
|
|
|
*
|
|
|
|
* <P>Type: INTEGER (long)</P>
|
|
|
|
*/
|
|
|
|
String SEND_DATE = "date";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <P>Type: TEXT</P>
|
|
|
|
*/
|
|
|
|
String SENDER = "sender";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <P>Type: TEXT</P>
|
|
|
|
*/
|
|
|
|
String SUBJECT = "subject";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* <P>Type: TEXT</P>
|
|
|
|
*/
|
|
|
|
String PREVIEW = "preview";
|
|
|
|
}
|
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
protected interface QueryHandler
|
2010-09-26 12:01:41 -04:00
|
|
|
{
|
2010-10-01 15:41:39 -04:00
|
|
|
/**
|
|
|
|
* The path this instance is able to respond to.
|
2010-10-05 02:04:28 -04:00
|
|
|
*
|
2010-10-01 15:41:39 -04:00
|
|
|
* @return Never <code>null</code>.
|
|
|
|
*/
|
|
|
|
String getPath();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param uri
|
|
|
|
* @param projection
|
|
|
|
* @param selection
|
|
|
|
* @param selectionArgs
|
|
|
|
* @param sortOrder
|
|
|
|
* @return
|
|
|
|
* @throws Exception
|
|
|
|
* @see {@link ContentProvider#query(Uri, String[], String, String[], String)}
|
|
|
|
*/
|
|
|
|
Cursor query(Uri uri, String[] projection,
|
2010-10-05 02:04:28 -04:00
|
|
|
String selection, String[] selectionArgs, String sortOrder) throws Exception;
|
2010-10-01 15:41:39 -04:00
|
|
|
}
|
2010-09-26 12:01:41 -04:00
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
/**
|
|
|
|
* Retrieve messages from the integrated inbox.
|
|
|
|
*/
|
|
|
|
protected class MessagesQueryHandler implements QueryHandler
|
|
|
|
{
|
2010-09-26 12:01:41 -04:00
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
@Override
|
|
|
|
public String getPath()
|
2010-09-26 12:01:41 -04:00
|
|
|
{
|
2010-10-01 15:41:39 -04:00
|
|
|
return "inbox_messages/";
|
2010-09-26 12:01:41 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2010-10-01 15:41:39 -04:00
|
|
|
public Cursor query(final Uri uri, final String[] projection, final String selection,
|
2010-10-05 02:04:28 -04:00
|
|
|
final String[] selectionArgs, final String sortOrder) throws Exception
|
2010-09-26 12:01:41 -04:00
|
|
|
{
|
2010-10-01 15:41:39 -04:00
|
|
|
return getMessages(projection);
|
2010-09-26 12:01:41 -04:00
|
|
|
}
|
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
/**
|
|
|
|
* @param projection
|
|
|
|
* Projection to use. If <code>null</code>, use the default
|
|
|
|
* projection.
|
|
|
|
* @return Never <code>null</code>.
|
|
|
|
* @throws InterruptedException
|
|
|
|
*/
|
|
|
|
protected MatrixCursor getMessages(final String[] projection) throws InterruptedException
|
2010-09-26 12:01:41 -04:00
|
|
|
{
|
2010-10-01 15:41:39 -04:00
|
|
|
// TODO use the given projection if prevent
|
|
|
|
final MatrixCursor cursor = new MatrixCursor(DEFAULT_MESSAGE_PROJECTION);
|
|
|
|
final BlockingQueue<List<MessageInfoHolder>> queue = new SynchronousQueue<List<MessageInfoHolder>>();
|
2010-09-26 12:01:41 -04:00
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
// new code for integrated inbox, only execute this once as it will be processed afterwards via the listener
|
|
|
|
final SearchAccount integratedInboxAccount = new SearchAccount(getContext(), true, null, null);
|
|
|
|
final MessagingController msgController = MessagingController.getInstance(K9.app);
|
2010-08-17 22:49:13 -04:00
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
msgController.searchLocalMessages(integratedInboxAccount, null,
|
2010-10-05 02:04:28 -04:00
|
|
|
new MesssageInfoHolderRetrieverListener(queue));
|
2010-08-17 22:49:13 -04:00
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
final List<MessageInfoHolder> holders = queue.take();
|
2010-08-17 22:49:13 -04:00
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
// TODO add sort order parameter
|
|
|
|
Collections.sort(holders, new MessageList.ReverseComparator<MessageInfoHolder>(
|
2010-10-05 02:04:28 -04:00
|
|
|
new MessageList.DateComparator()));
|
2010-08-17 22:49:13 -04:00
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
int id = -1;
|
|
|
|
for (final MessageInfoHolder holder : holders)
|
|
|
|
{
|
|
|
|
final Message message = holder.message;
|
|
|
|
id++;
|
2010-08-17 22:49:13 -04:00
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
cursor.addRow(new Object[]
|
2010-10-05 02:04:28 -04:00
|
|
|
{
|
|
|
|
id,
|
2010-10-05 18:33:20 -04:00
|
|
|
message.getSentDate().getTime(),
|
2010-10-05 02:04:28 -04:00
|
|
|
holder.sender,
|
|
|
|
holder.subject,
|
|
|
|
holder.preview,
|
|
|
|
holder.account,
|
|
|
|
holder.uri,
|
|
|
|
CONTENT_URI + "/delete_message/"
|
|
|
|
+ message.getFolder().getAccount().getAccountNumber() + "/"
|
|
|
|
+ message.getFolder().getName() + "/" + message.getUid()
|
|
|
|
});
|
2010-10-01 15:41:39 -04:00
|
|
|
}
|
|
|
|
return cursor;
|
|
|
|
}
|
2010-08-17 22:49:13 -04:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
/**
|
|
|
|
* Retrieve the account list.
|
|
|
|
*/
|
|
|
|
protected class AccountsQueryHandler implements QueryHandler
|
2010-08-17 22:49:13 -04:00
|
|
|
{
|
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
@Override
|
|
|
|
public String getPath()
|
2010-08-17 22:49:13 -04:00
|
|
|
{
|
2010-10-01 15:41:39 -04:00
|
|
|
return "accounts";
|
2010-08-17 22:49:13 -04:00
|
|
|
}
|
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
@Override
|
|
|
|
public Cursor query(final Uri uri, String[] projection, String selection,
|
2010-10-05 02:04:28 -04:00
|
|
|
String[] selectionArgs, String sortOrder) throws Exception
|
2010-10-01 15:41:39 -04:00
|
|
|
{
|
|
|
|
return getAllAccounts();
|
|
|
|
}
|
2010-08-17 22:49:13 -04:00
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
public Cursor getAllAccounts()
|
2010-08-17 22:49:13 -04:00
|
|
|
{
|
2010-10-01 15:41:39 -04:00
|
|
|
String[] projection = new String[] { "accountNumber", "accountName" };
|
|
|
|
|
|
|
|
MatrixCursor ret = new MatrixCursor(projection);
|
|
|
|
|
|
|
|
for (Account account : Preferences.getPreferences(getContext()).getAccounts())
|
|
|
|
{
|
|
|
|
Object[] values = new Object[2];
|
|
|
|
values[0] = account.getAccountNumber();
|
|
|
|
values[1] = account.getDescription();
|
|
|
|
ret.addRow(values);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2010-08-17 22:49:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
/**
|
|
|
|
* Retrieve the unread message count for a given account specified by its
|
|
|
|
* {@link Account#getAccountNumber() number}.
|
|
|
|
*/
|
|
|
|
protected class UnreadQueryHandler implements QueryHandler
|
2010-08-17 22:49:13 -04:00
|
|
|
{
|
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
@Override
|
|
|
|
public String getPath()
|
|
|
|
{
|
|
|
|
return "account_unread/#";
|
|
|
|
}
|
2010-08-17 22:49:13 -04:00
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
@Override
|
|
|
|
public Cursor query(final Uri uri, String[] projection, String selection,
|
2010-10-05 02:04:28 -04:00
|
|
|
String[] selectionArgs, String sortOrder) throws Exception
|
2010-10-01 15:41:39 -04:00
|
|
|
{
|
|
|
|
List<String> segments = null;
|
|
|
|
int accountId = -1;
|
|
|
|
segments = uri.getPathSegments();
|
|
|
|
accountId = Integer.parseInt(segments.get(1));
|
|
|
|
return getAccountUnread(accountId);
|
|
|
|
}
|
2010-08-17 22:49:13 -04:00
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
public Cursor getAccountUnread(int accountNumber)
|
2010-08-17 22:49:13 -04:00
|
|
|
{
|
2010-10-01 15:41:39 -04:00
|
|
|
String[] projection = new String[] { "accountName", "unread" };
|
|
|
|
|
|
|
|
MatrixCursor ret = new MatrixCursor(projection);
|
|
|
|
|
|
|
|
Account myAccount;
|
|
|
|
AccountStats myAccountStats = null;
|
|
|
|
|
|
|
|
Object[] values = new Object[2];
|
|
|
|
|
|
|
|
for (Account account : Preferences.getPreferences(getContext()).getAccounts())
|
2010-08-17 22:49:13 -04:00
|
|
|
{
|
2010-10-01 15:41:39 -04:00
|
|
|
if (account.getAccountNumber()==accountNumber)
|
2010-08-17 22:49:13 -04:00
|
|
|
{
|
2010-10-01 15:41:39 -04:00
|
|
|
myAccount = account;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
myAccountStats = account.getStats(getContext());
|
|
|
|
values[0] = myAccount.getDescription();
|
|
|
|
values[1] = myAccountStats.unreadMessageCount;
|
|
|
|
ret.addRow(values);
|
|
|
|
}
|
|
|
|
catch (MessagingException e)
|
|
|
|
{
|
|
|
|
Log.e(K9.LOG_TAG, e.getMessage());
|
|
|
|
values[0] = "Unknown";
|
|
|
|
values[1] = 0;
|
|
|
|
}
|
2010-08-17 22:49:13 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
return ret;
|
|
|
|
}
|
2010-08-17 22:49:13 -04:00
|
|
|
}
|
|
|
|
|
2010-10-05 16:23:07 -04:00
|
|
|
protected class ThrottlingQueryHandler implements QueryHandler
|
|
|
|
{
|
|
|
|
|
|
|
|
protected final class MonitoredCursor implements CrossProcessCursor
|
|
|
|
{
|
|
|
|
private CrossProcessCursor mCursor;
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public void close()
|
|
|
|
{
|
|
|
|
mCursor.close();
|
|
|
|
if (mClosed.compareAndSet(false, true))
|
|
|
|
{
|
|
|
|
Log.d(K9.LOG_TAG, "Cursor closed, releasing semaphore");
|
|
|
|
mSemaphore.release();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public void fillWindow(int pos, CursorWindow winow)
|
|
|
|
{
|
|
|
|
mCursor.fillWindow(pos, winow);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public CursorWindow getWindow()
|
|
|
|
{
|
|
|
|
return mCursor.getWindow();
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public boolean onMove(int oldPosition, int newPosition)
|
|
|
|
{
|
|
|
|
return mCursor.onMove(oldPosition, newPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
private AtomicBoolean mClosed = new AtomicBoolean(false);
|
|
|
|
|
|
|
|
|
|
|
|
protected MonitoredCursor(final CrossProcessCursor cursor)
|
|
|
|
{
|
|
|
|
this.mCursor = cursor;
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer)
|
|
|
|
{
|
|
|
|
mCursor.copyStringToBuffer(columnIndex, buffer);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public void deactivate()
|
|
|
|
{
|
|
|
|
mCursor.deactivate();
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public byte[] getBlob(int columnIndex)
|
|
|
|
{
|
|
|
|
return mCursor.getBlob(columnIndex);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public int getColumnCount()
|
|
|
|
{
|
|
|
|
return mCursor.getColumnCount();
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public int getColumnIndex(String columnName)
|
|
|
|
{
|
|
|
|
return mCursor.getColumnIndex(columnName);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException
|
|
|
|
{
|
|
|
|
return mCursor.getColumnIndexOrThrow(columnName);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public String getColumnName(int columnIndex)
|
|
|
|
{
|
|
|
|
return mCursor.getColumnName(columnIndex);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public String[] getColumnNames()
|
|
|
|
{
|
|
|
|
return mCursor.getColumnNames();
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public int getCount()
|
|
|
|
{
|
|
|
|
return mCursor.getCount();
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public double getDouble(int columnIndex)
|
|
|
|
{
|
|
|
|
return mCursor.getDouble(columnIndex);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public Bundle getExtras()
|
|
|
|
{
|
|
|
|
return mCursor.getExtras();
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public float getFloat(int columnIndex)
|
|
|
|
{
|
|
|
|
return mCursor.getFloat(columnIndex);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public int getInt(int columnIndex)
|
|
|
|
{
|
|
|
|
return mCursor.getInt(columnIndex);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public long getLong(int columnIndex)
|
|
|
|
{
|
|
|
|
return mCursor.getLong(columnIndex);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public int getPosition()
|
|
|
|
{
|
|
|
|
return mCursor.getPosition();
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public short getShort(int columnIndex)
|
|
|
|
{
|
|
|
|
return mCursor.getShort(columnIndex);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public String getString(int columnIndex)
|
|
|
|
{
|
|
|
|
return mCursor.getString(columnIndex);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public boolean getWantsAllOnMoveCalls()
|
|
|
|
{
|
|
|
|
return mCursor.getWantsAllOnMoveCalls();
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public boolean isAfterLast()
|
|
|
|
{
|
|
|
|
return mCursor.isAfterLast();
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public boolean isBeforeFirst()
|
|
|
|
{
|
|
|
|
return mCursor.isBeforeFirst();
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public boolean isClosed()
|
|
|
|
{
|
|
|
|
return mCursor.isClosed();
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public boolean isFirst()
|
|
|
|
{
|
|
|
|
return mCursor.isFirst();
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isLast()
|
|
|
|
{
|
|
|
|
return mCursor.isLast();
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public boolean isNull(int columnIndex)
|
|
|
|
{
|
|
|
|
return mCursor.isNull(columnIndex);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public boolean move(int offset)
|
|
|
|
{
|
|
|
|
return mCursor.move(offset);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public boolean moveToFirst()
|
|
|
|
{
|
|
|
|
return mCursor.moveToFirst();
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public boolean moveToLast()
|
|
|
|
{
|
|
|
|
return mCursor.moveToLast();
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public boolean moveToNext()
|
|
|
|
{
|
|
|
|
return mCursor.moveToNext();
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public boolean moveToPosition(int position)
|
|
|
|
{
|
|
|
|
return mCursor.moveToPosition(position);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public boolean moveToPrevious()
|
|
|
|
{
|
|
|
|
return mCursor.moveToPrevious();
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public void registerContentObserver(ContentObserver observer)
|
|
|
|
{
|
|
|
|
mCursor.registerContentObserver(observer);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public void registerDataSetObserver(DataSetObserver observer)
|
|
|
|
{
|
|
|
|
mCursor.registerDataSetObserver(observer);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public boolean requery()
|
|
|
|
{
|
|
|
|
return mCursor.requery();
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public Bundle respond(Bundle extras)
|
|
|
|
{
|
|
|
|
return mCursor.respond(extras);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public void setNotificationUri(ContentResolver cr, Uri uri)
|
|
|
|
{
|
|
|
|
mCursor.setNotificationUri(cr, uri);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public void unregisterContentObserver(ContentObserver observer)
|
|
|
|
{
|
|
|
|
mCursor.unregisterContentObserver(observer);
|
|
|
|
}
|
|
|
|
|
2010-10-06 13:51:35 -04:00
|
|
|
@Override
|
2010-10-05 16:23:07 -04:00
|
|
|
public void unregisterDataSetObserver(DataSetObserver observer)
|
|
|
|
{
|
|
|
|
mCursor.unregisterDataSetObserver(observer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private QueryHandler mDelegate;
|
|
|
|
|
|
|
|
public ThrottlingQueryHandler(final QueryHandler delegate)
|
|
|
|
{
|
|
|
|
mDelegate = delegate;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getPath()
|
|
|
|
{
|
|
|
|
return mDelegate.getPath();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
|
|
|
|
String sortOrder) throws Exception
|
|
|
|
{
|
|
|
|
mSemaphore.acquire();
|
|
|
|
|
|
|
|
final Cursor cursor;
|
|
|
|
cursor = mDelegate.query(uri, projection, selection, selectionArgs, sortOrder);
|
|
|
|
|
|
|
|
/* Android content resolvers can only process CrossProcessCursor instances */
|
|
|
|
if (cursor == null || !(cursor instanceof CrossProcessCursor))
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
final MonitoredCursor wrapped = new MonitoredCursor((CrossProcessCursor) cursor);
|
|
|
|
|
|
|
|
/* use a weak reference not to actively prevent garbage collection */
|
|
|
|
final WeakReference<MonitoredCursor> weakReference = new WeakReference<MonitoredCursor>(wrapped);
|
|
|
|
|
|
|
|
/* make sure the cursor is closed after 30 seconds */
|
|
|
|
mScheduledPool.schedule(new Runnable() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run()
|
|
|
|
{
|
|
|
|
final MonitoredCursor monitored = weakReference.get();
|
|
|
|
if (monitored != null && monitored.mClosed.compareAndSet(false, true))
|
|
|
|
{
|
|
|
|
Log.w(K9.LOG_TAG, "Forcibly closing remotely exposed cursor");
|
|
|
|
try
|
|
|
|
{
|
|
|
|
monitored.close();
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Log.w(K9.LOG_TAG, "Exception while forcibly closing cursor", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}}, 30, TimeUnit.SECONDS);
|
|
|
|
|
|
|
|
return wrapped;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
/**
|
|
|
|
* Synchronized listener used to retrieve {@link MessageInfoHolder}s using a
|
|
|
|
* given {@link BlockingQueue}.
|
|
|
|
*/
|
|
|
|
protected class MesssageInfoHolderRetrieverListener extends MessagingListener
|
2010-08-17 22:49:13 -04:00
|
|
|
{
|
2010-10-01 15:41:39 -04:00
|
|
|
private final BlockingQueue<List<MessageInfoHolder>> queue;
|
|
|
|
|
2010-10-03 06:56:16 -04:00
|
|
|
private List<MessageInfoHolder> mHolders = new ArrayList<MessageInfoHolder>();
|
2010-10-01 15:41:39 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param queue
|
|
|
|
* Never <code>null</code>. The synchronized channel to use
|
|
|
|
* to retrieve {@link MessageInfoHolder}s.
|
|
|
|
*/
|
2010-10-03 06:56:16 -04:00
|
|
|
public MesssageInfoHolderRetrieverListener(final BlockingQueue<List<MessageInfoHolder>> queue)
|
2010-08-29 12:57:13 -04:00
|
|
|
{
|
2010-10-01 15:41:39 -04:00
|
|
|
this.queue = queue;
|
2010-08-29 12:57:13 -04:00
|
|
|
}
|
2010-10-01 15:41:39 -04:00
|
|
|
|
|
|
|
@Override
|
2010-10-03 06:56:16 -04:00
|
|
|
public void listLocalMessagesAddMessages(final Account account,
|
|
|
|
final String folderName, final List<Message> messages)
|
2010-08-17 22:49:13 -04:00
|
|
|
{
|
2010-10-03 06:56:16 -04:00
|
|
|
// cache fields into local variables for faster access on JVM without JIT
|
|
|
|
final MessageHelper helper = mMessageHelper;
|
|
|
|
final List<MessageInfoHolder> holders = mHolders;
|
|
|
|
|
|
|
|
final Context context = getContext();
|
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
for (final Message message : messages)
|
2010-08-17 22:49:13 -04:00
|
|
|
{
|
2010-10-03 06:56:16 -04:00
|
|
|
final MessageInfoHolder messageInfoHolder = new MessageInfoHolder();
|
|
|
|
final Folder messageFolder = message.getFolder();
|
|
|
|
final Account messageAccount = messageFolder.getAccount();
|
|
|
|
|
|
|
|
helper.populate(messageInfoHolder, message, new FolderInfoHolder(context,
|
2010-10-05 02:04:28 -04:00
|
|
|
messageFolder, messageAccount), messageAccount);
|
2010-10-03 06:56:16 -04:00
|
|
|
|
|
|
|
holders.add(messageInfoHolder);
|
2010-08-17 22:49:13 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
@Override
|
|
|
|
public void searchStats(AccountStats stats)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2010-10-03 06:56:16 -04:00
|
|
|
queue.put(mHolders);
|
2010-10-01 15:41:39 -04:00
|
|
|
}
|
|
|
|
catch (InterruptedException e)
|
|
|
|
{
|
|
|
|
Log.e(K9.LOG_TAG, "Unable to return message list back to caller", e);
|
|
|
|
}
|
|
|
|
}
|
2010-08-17 22:49:13 -04:00
|
|
|
}
|
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
public static final String AUTHORITY = "com.fsck.k9.messageprovider";
|
|
|
|
|
|
|
|
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
|
|
|
|
|
|
|
|
private static final String[] DEFAULT_MESSAGE_PROJECTION = new String[]
|
|
|
|
{
|
2010-10-06 16:14:33 -04:00
|
|
|
MessageColumns._ID,
|
|
|
|
MessageColumns.SEND_DATE,
|
|
|
|
MessageColumns.SENDER,
|
|
|
|
MessageColumns.SUBJECT,
|
|
|
|
MessageColumns.PREVIEW,
|
2010-10-01 15:41:39 -04:00
|
|
|
"account",
|
|
|
|
"uri",
|
|
|
|
"delUri"
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* URI matcher used for
|
|
|
|
* {@link #query(Uri, String[], String, String[], String)}
|
|
|
|
*/
|
|
|
|
private UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handlers registered to respond to
|
|
|
|
* {@link #query(Uri, String[], String, String[], String)}
|
|
|
|
*/
|
|
|
|
private List<QueryHandler> mQueryHandlers = new ArrayList<QueryHandler>();
|
|
|
|
|
2010-10-03 06:56:16 -04:00
|
|
|
private MessageHelper mMessageHelper;
|
|
|
|
|
2010-10-05 16:23:07 -04:00
|
|
|
/* package */ Semaphore mSemaphore = new Semaphore(1);
|
|
|
|
|
|
|
|
/* package */ ScheduledExecutorService mScheduledPool = Executors.newScheduledThreadPool(1);
|
|
|
|
|
2010-08-17 22:49:13 -04:00
|
|
|
@Override
|
|
|
|
public boolean onCreate()
|
|
|
|
{
|
2010-10-03 06:56:16 -04:00
|
|
|
mMessageHelper = MessageHelper.getInstance(getContext());
|
|
|
|
|
2010-10-05 16:23:07 -04:00
|
|
|
registerQueryHandler(new ThrottlingQueryHandler(new AccountsQueryHandler()));
|
|
|
|
registerQueryHandler(new ThrottlingQueryHandler(new MessagesQueryHandler()));
|
|
|
|
registerQueryHandler(new ThrottlingQueryHandler(new UnreadQueryHandler()));
|
2010-08-17 22:49:13 -04:00
|
|
|
|
2010-10-06 16:14:33 -04:00
|
|
|
K9.registerApplicationAware(new K9.ApplicationAware()
|
|
|
|
{
|
|
|
|
@Override
|
|
|
|
public void initializeComponent(final K9 application) throws Exception
|
|
|
|
{
|
|
|
|
Log.v(K9.LOG_TAG, "Registering content resolver notifier");
|
|
|
|
|
|
|
|
MessagingController.getInstance(application).addListener(new MessagingListener() {
|
|
|
|
@Override
|
|
|
|
public void searchStats(final AccountStats stats)
|
|
|
|
{
|
|
|
|
application.getContentResolver().notifyChange(CONTENT_URI, null);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
return true;
|
2010-08-17 22:49:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int delete(Uri uri, String selection, String[] selectionArgs)
|
|
|
|
{
|
2010-10-05 01:09:17 -04:00
|
|
|
if (K9.app == null)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-08-17 22:49:13 -04:00
|
|
|
if (K9.DEBUG)
|
|
|
|
{
|
2010-10-05 01:09:17 -04:00
|
|
|
Log.v(K9.LOG_TAG, "MessageProvider/delete: " + uri);
|
2010-08-17 22:49:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Nota : can only delete a message
|
|
|
|
|
|
|
|
List<String> segments = null;
|
|
|
|
int accountId = -1;
|
|
|
|
String folderName = null;
|
|
|
|
String msgUid = null;
|
|
|
|
|
|
|
|
segments = uri.getPathSegments();
|
|
|
|
accountId = Integer.parseInt(segments.get(1));
|
|
|
|
folderName = segments.get(2);
|
|
|
|
msgUid = segments.get(3);
|
|
|
|
|
|
|
|
// get account
|
|
|
|
Account myAccount = null;
|
|
|
|
for (Account account : Preferences.getPreferences(getContext()).getAccounts())
|
|
|
|
{
|
|
|
|
if (account.getAccountNumber() == accountId)
|
|
|
|
{
|
|
|
|
myAccount = account;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// get localstore parameter
|
|
|
|
Message msg = null;
|
|
|
|
try
|
|
|
|
{
|
2010-10-01 15:41:39 -04:00
|
|
|
Folder lf = LocalStore.getLocalInstance(myAccount, K9.app).getFolder(folderName);
|
2010-08-17 22:49:13 -04:00
|
|
|
int msgCount = lf.getMessageCount();
|
|
|
|
if (K9.DEBUG)
|
2010-10-01 15:41:39 -04:00
|
|
|
{
|
2010-08-17 22:49:13 -04:00
|
|
|
Log.d(K9.LOG_TAG, "folder msg count = " + msgCount);
|
2010-10-01 15:41:39 -04:00
|
|
|
}
|
2010-08-17 22:49:13 -04:00
|
|
|
msg = lf.getMessage(msgUid);
|
|
|
|
}
|
|
|
|
catch (MessagingException e)
|
|
|
|
{
|
2010-10-01 15:41:39 -04:00
|
|
|
Log.e(K9.LOG_TAG, "Unable to retrieve message", e);
|
2010-08-17 22:49:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// launch command to delete the message
|
|
|
|
if ((myAccount != null) && (msg != null))
|
|
|
|
{
|
2010-10-01 15:41:39 -04:00
|
|
|
MessagingController.getInstance(K9.app).deleteMessages(new Message[] { msg }, null);
|
2010-08-17 22:49:13 -04:00
|
|
|
}
|
|
|
|
|
2010-10-05 01:09:17 -04:00
|
|
|
// FIXME return the actual number of deleted messages
|
2010-08-17 22:49:13 -04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getType(Uri uri)
|
|
|
|
{
|
2010-10-05 01:09:17 -04:00
|
|
|
if (K9.app == null)
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (K9.DEBUG)
|
|
|
|
{
|
|
|
|
Log.v(K9.LOG_TAG, "MessageProvider/getType: " + uri);
|
|
|
|
}
|
|
|
|
|
2010-08-17 22:49:13 -04:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public Uri insert(Uri uri, ContentValues values)
|
|
|
|
{
|
2010-10-05 01:09:17 -04:00
|
|
|
if (K9.app == null)
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (K9.DEBUG)
|
|
|
|
{
|
|
|
|
Log.v(K9.LOG_TAG, "MessageProvider/insert: " + uri);
|
|
|
|
}
|
|
|
|
|
2010-08-17 22:49:13 -04:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2010-10-01 15:41:39 -04:00
|
|
|
public Cursor query(final Uri uri, final String[] projection, final String selection,
|
2010-10-05 02:04:28 -04:00
|
|
|
final String[] selectionArgs, final String sortOrder)
|
2010-08-17 22:49:13 -04:00
|
|
|
{
|
2010-10-05 01:09:17 -04:00
|
|
|
if (K9.app == null)
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (K9.DEBUG)
|
|
|
|
{
|
|
|
|
Log.v(K9.LOG_TAG, "MessageProvider/query: " + uri);
|
|
|
|
}
|
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
final Cursor cursor;
|
2010-08-17 22:49:13 -04:00
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
final int code = mUriMatcher.match(uri);
|
2010-08-17 22:49:13 -04:00
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
if (code == -1)
|
2010-08-17 22:49:13 -04:00
|
|
|
{
|
2010-10-01 15:41:39 -04:00
|
|
|
throw new IllegalStateException("Unrecognized URI: " + uri);
|
2010-08-17 22:49:13 -04:00
|
|
|
}
|
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
try
|
2010-08-17 22:49:13 -04:00
|
|
|
{
|
2010-10-01 15:41:39 -04:00
|
|
|
// since we used the list index as the UriMatcher code, using it
|
|
|
|
// back to retrieve the handler from the list
|
|
|
|
final QueryHandler handler = mQueryHandlers.get(code);
|
|
|
|
cursor = handler.query(uri, projection, selection, selectionArgs, sortOrder);
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Log.e(K9.LOG_TAG, "Unable to execute query for URI: " + uri, e);
|
|
|
|
return null;
|
2010-08-17 22:49:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return cursor;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
|
|
|
|
{
|
2010-10-05 01:09:17 -04:00
|
|
|
if (K9.app == null)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2010-08-17 22:49:13 -04:00
|
|
|
|
|
|
|
if (K9.DEBUG)
|
2010-10-01 15:41:39 -04:00
|
|
|
{
|
2010-10-05 01:09:17 -04:00
|
|
|
Log.v(K9.LOG_TAG, "MessageProvider/update: " + uri);
|
2010-10-01 15:41:39 -04:00
|
|
|
}
|
2010-08-17 22:49:13 -04:00
|
|
|
|
|
|
|
//TBD
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
/**
|
|
|
|
* Register a {@link QueryHandler} to handle a certain {@link Uri} for
|
|
|
|
* {@link #query(Uri, String[], String, String[], String)}
|
2010-10-05 02:04:28 -04:00
|
|
|
*
|
2010-10-01 15:41:39 -04:00
|
|
|
* @param handler
|
|
|
|
* Never <code>null</code>.
|
|
|
|
*/
|
|
|
|
protected void registerQueryHandler(final QueryHandler handler)
|
2010-08-17 22:49:13 -04:00
|
|
|
{
|
2010-10-01 15:41:39 -04:00
|
|
|
if (mQueryHandlers.contains(handler))
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mQueryHandlers.add(handler);
|
2010-08-17 22:49:13 -04:00
|
|
|
|
2010-10-01 15:41:39 -04:00
|
|
|
// use the index inside the list as the UriMatcher code for that handler
|
|
|
|
final int code = mQueryHandlers.indexOf(handler);
|
|
|
|
mUriMatcher.addURI(AUTHORITY, handler.getPath(), code);
|
2010-08-17 22:49:13 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|