1
0
mirror of https://github.com/moparisthebest/k-9 synced 2025-01-09 20:58:07 -05:00

Merge branch 'master' into issue-162

This commit is contained in:
ashley willis 2012-02-16 17:38:31 -06:00
commit ffb8227c45
22 changed files with 944 additions and 303 deletions

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="15007"
android:versionName="4.107" package="com.fsck.k9"
android:versionCode="15009"
android:versionName="4.109" package="com.fsck.k9"
>
<uses-sdk
android:minSdkVersion="7"
@ -43,13 +43,13 @@
<uses-permission android:name="com.fsck.k9.permission.REMOTE_CONTROL"/>
<permission android:name="com.fsck.k9.permission.READ_MESSAGES"
android:permissionGroup="android.permission-group.MESSAGES"
android:protectionLevel="normal"
android:protectionLevel="dangerous"
android:label="@string/read_messages_label"
android:description="@string/read_messages_desc"/>
<uses-permission android:name="com.fsck.k9.permission.READ_MESSAGES"/>
<permission android:name="com.fsck.k9.permission.DELETE_MESSAGES"
android:permissionGroup="android.permission-group.MESSAGES"
android:protectionLevel="normal"
android:protectionLevel="dangerous"
android:label="@string/delete_messages_label"
android:description="@string/read_messages_desc"/>
<uses-permission android:name="com.fsck.k9.permission.DELETE_MESSAGES"/>
@ -208,6 +208,10 @@
android:launchMode="singleTask"
android:configChanges="locale"
>
<intent-filter>
<!-- This action is only to allow an entry point for launcher shortcuts -->
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity
android:name="com.fsck.k9.activity.MessageView"
@ -387,5 +391,25 @@ otherwise it would make K-9 start at the wrong time
android:readPermission="com.fsck.k9.permission.READ_MESSAGES"
android:writePermission="com.fsck.k9.permission.DELETE_MESSAGES"
/>
<receiver
android:name=".provider.UnreadWidgetProvider"
android:label="@string/unread_widget_label"
android:icon="@drawable/icon">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/unread_widget_info" />
</receiver>
<activity android:name=".activity.UnreadWidgetConfiguration">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
<activity
android:name=".activity.AccountList">
</activity>
</application>
</manifest>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#a0000000"/>
<corners android:radius="7dp"/>
</shape>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#ffcc0000"/>
<corners android:radius="17dp"/>
</shape>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- FIXME: find a nicer looking way than using 'menuitem_background' -->
<item android:drawable="@android:drawable/menuitem_background"
android:state_pressed="true" />
<item android:drawable="@android:drawable/menuitem_background"
android:state_focused="true"
android:state_enabled="true"
android:state_window_focused="true" />
<item android:drawable="@android:color/transparent" />
</selector>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
<LinearLayout
android:id="@android:id/empty"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:gravity="center_vertical|center_horizontal">
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"/>
</LinearLayout>
</LinearLayout>

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/unread_widget_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="2dp"
android:orientation="vertical"
android:clickable="true"
android:focusable="true"
android:background="@drawable/unread_widget_background"
android:gravity="bottom|center_horizontal">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:layout_width="@android:dimen/app_icon_size"
android:layout_height="@android:dimen/app_icon_size"
android:scaleType="fitCenter"
android:src="@drawable/icon" />
<TextView
android:id="@+id/unread_count"
android:visibility="gone"
android:textSize="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|bottom"
android:paddingTop="0.5dp"
android:paddingBottom="0.5dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:background="@drawable/unread_count_background"
android:textColor="#ffffff"/>
</FrameLayout>
<TextView
android:id="@+id/account_name"
android:text="@string/app_name"
android:ellipsize="marquee"
android:textSize="12dp"
android:singleLine="true"
android:paddingTop="1dp"
android:paddingBottom="1dp"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp"
android:background="@drawable/rounded_corners"
android:textColor="#ffffff"
android:shadowColor="#000000"
android:shadowRadius="2.0"/>
</LinearLayout>

View File

@ -1145,4 +1145,12 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
<string name="manage_accounts_move_down_action">Move down</string>
<string name="manage_accounts_moving_message">Moving account...</string>
<string name="unread_widget_label">K-9 Unread</string>
<string name="unread_widget_select_account">Show unread count for…</string>
<string name="import_dialog_error_title">Missing File Manager Application</string>
<string name="import_dialog_error_message">There is no suitable application to handle
the import operation. Please install a file manager application from Android Market</string>
<string name="open_market">Open Market</string>
<string name="close">Close</string>
</resources>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/unread_widget_layout"
android:minHeight="72dp"
android:minWidth="72dp"
android:configure="com.fsck.k9.activity.UnreadWidgetConfiguration"
android:updatePeriodMillis="0">
</appwidget-provider>

View File

@ -48,7 +48,7 @@ public class EmailAddressAdapter extends ResourceCursorAdapter {
final String name = mContacts.getName(cursor);
final String address = mContacts.getEmail(cursor);
return new Address(address, name).toString();
return (address == null) ? "" : new Address(address, name).toString();
}
@Override

View File

@ -31,6 +31,7 @@ import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.internet.BinaryTempFileBody;
import com.fsck.k9.provider.UnreadWidgetProvider;
import com.fsck.k9.service.BootReceiver;
import com.fsck.k9.service.MailService;
import com.fsck.k9.service.ShutdownReceiver;
@ -505,19 +506,38 @@ public class K9 extends Application {
}
}
private void updateUnreadWidget() {
try {
UnreadWidgetProvider.updateUnreadCount(K9.this);
} catch (Exception e) {
if (K9.DEBUG) {
Log.e(LOG_TAG, "Error while updating unread widget(s)", e);
}
}
}
@Override
public void synchronizeMailboxRemovedMessage(Account account, String folder, Message message) {
broadcastIntent(K9.Intents.EmailReceived.ACTION_EMAIL_DELETED, account, folder, message);
updateUnreadWidget();
}
@Override
public void messageDeleted(Account account, String folder, Message message) {
broadcastIntent(K9.Intents.EmailReceived.ACTION_EMAIL_DELETED, account, folder, message);
updateUnreadWidget();
}
@Override
public void synchronizeMailboxNewMessage(Account account, String folder, Message message) {
broadcastIntent(K9.Intents.EmailReceived.ACTION_EMAIL_RECEIVED, account, folder, message);
updateUnreadWidget();
}
@Override
public void folderStatusChanged(Account account, String folderName,
int unreadMessageCount) {
updateUnreadWidget();
}
@Override

View File

@ -0,0 +1,192 @@
package com.fsck.k9.activity;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import com.fsck.k9.Account;
import com.fsck.k9.BaseAccount;
import com.fsck.k9.FontSizes;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.R;
import com.fsck.k9.SearchAccount;
/**
* Activity displaying the list of accounts.
*
* <p>
* Classes extending this abstract class have to provide an {@link #onAccountSelected(BaseAccount)}
* method to perform an action when an account is selected.
* </p>
*/
public abstract class AccountList extends K9ListActivity implements OnItemClickListener {
private FontSizes mFontSizes = K9.getFontSizes();
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setResult(RESULT_CANCELED);
setContentView(R.layout.account_list);
ListView listView = getListView();
listView.setOnItemClickListener(this);
listView.setItemsCanFocus(false);
}
/**
* Reload list of accounts when this activity is resumed.
*/
@Override
public void onResume() {
super.onResume();
new LoadAccounts().execute();
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
BaseAccount account = (BaseAccount) parent.getItemAtPosition(position);
onAccountSelected(account);
}
/**
* Create a new {@link AccountsAdapter} instance and assign it to the {@link ListView}.
*
* @param realAccounts
* An array of accounts to display.
*/
public void populateListView(Account[] realAccounts) {
List<BaseAccount> accounts = new ArrayList<BaseAccount>();
if (displaySpecialAccounts() && !K9.isHideSpecialAccounts()) {
BaseAccount integratedInboxAccount = new SearchAccount(this, true, null, null);
integratedInboxAccount.setDescription(getString(R.string.integrated_inbox_title));
integratedInboxAccount.setEmail(getString(R.string.integrated_inbox_detail));
BaseAccount unreadAccount = new SearchAccount(this, false, null, null);
unreadAccount.setDescription(getString(R.string.search_all_messages_title));
unreadAccount.setEmail(getString(R.string.search_all_messages_detail));
accounts.add(integratedInboxAccount);
accounts.add(unreadAccount);
}
accounts.addAll(Arrays.asList(realAccounts));
AccountsAdapter adapter = new AccountsAdapter(accounts);
ListView listView = getListView();
listView.setAdapter(adapter);
listView.invalidate();
}
/**
* Implementing decide whether or not to display special accounts in the list.
*
* @return {@code true}, if special accounts should be listed. {@code false}, otherwise.
*/
protected abstract boolean displaySpecialAccounts();
/**
* This method will be called when an account was selected.
*
* @param account
* The account the user selected.
*/
protected abstract void onAccountSelected(BaseAccount account);
class AccountsAdapter extends ArrayAdapter<BaseAccount> {
public AccountsAdapter(List<BaseAccount> accounts) {
super(AccountList.this, 0, accounts);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final BaseAccount account = getItem(position);
final View view;
if (convertView != null) {
view = convertView;
} else {
view = getLayoutInflater().inflate(R.layout.accounts_item, parent, false);
view.findViewById(R.id.active_icons).setVisibility(View.GONE);
view.findViewById(R.id.folders).setVisibility(View.GONE);
view.getBackground().setAlpha(0);
}
AccountViewHolder holder = (AccountViewHolder) view.getTag();
if (holder == null) {
holder = new AccountViewHolder();
holder.description = (TextView) view.findViewById(R.id.description);
holder.email = (TextView) view.findViewById(R.id.email);
holder.chip = view.findViewById(R.id.chip);
view.setTag(holder);
}
String description = account.getDescription();
if (account.getEmail().equals(description)) {
holder.email.setVisibility(View.GONE);
} else {
holder.email.setVisibility(View.VISIBLE);
holder.email.setText(account.getEmail());
}
if (description == null || description.length() == 0) {
description = account.getEmail();
}
holder.description.setText(description);
if (account instanceof Account) {
Account realAccount = (Account) account;
holder.chip.setBackgroundColor(realAccount.getChipColor());
} else {
holder.chip.setBackgroundColor(0xff999999);
}
holder.chip.getBackground().setAlpha(255);
holder.description.setTextSize(TypedValue.COMPLEX_UNIT_SP,
mFontSizes.getAccountName());
holder.email.setTextSize(TypedValue.COMPLEX_UNIT_SP,
mFontSizes.getAccountDescription());
return view;
}
class AccountViewHolder {
public TextView description;
public TextView email;
public View chip;
}
}
/**
* Load accounts in a background thread
*/
class LoadAccounts extends AsyncTask<Void, Void, Account[]> {
@Override
protected Account[] doInBackground(Void... params) {
Account[] accounts = Preferences.getPreferences(getApplicationContext()).getAccounts();
return accounts;
}
@Override
protected void onPostExecute(Account[] accounts) {
populateListView(accounts);
}
}
}

View File

@ -26,6 +26,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@ -108,10 +109,22 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
*/
private static final Flag[] EMPTY_FLAG_ARRAY = new Flag[0];
/**
* URL used to open Android Market application
*/
private static final String ANDROID_MARKET_URL = "https://market.android.com/search?q=oi+file+manager&c=apps";
/**
* Number of special accounts ('Unified Inbox' and 'All Messages')
*/
private static final int SPECIAL_ACCOUNTS_COUNT = 2;
private static final int DIALOG_REMOVE_ACCOUNT = 1;
private static final int DIALOG_CLEAR_ACCOUNT = 2;
private static final int DIALOG_RECREATE_ACCOUNT = 3;
private static final int DIALOG_NO_FILE_MANAGER = 4;
private static final String TRUE = "true";
private ConcurrentHashMap<String, AccountStats> accountStats = new ConcurrentHashMap<String, AccountStats>();
private ConcurrentHashMap<BaseAccount, String> pendingWork = new ConcurrentHashMap<BaseAccount, String>();
@ -328,13 +341,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
super.onCreate(icicle);
if (!K9.isHideSpecialAccounts()) {
unreadAccount = new SearchAccount(this, false, null, null);
unreadAccount.setDescription(getString(R.string.search_all_messages_title));
unreadAccount.setEmail(getString(R.string.search_all_messages_detail));
integratedInboxAccount = new SearchAccount(this, true, null, null);
integratedInboxAccount.setDescription(getString(R.string.integrated_inbox_title));
integratedInboxAccount.setEmail(getString(R.string.integrated_inbox_detail));
createSpecialAccounts();
}
Account[] accounts = Preferences.getPreferences(this).getAccounts();
@ -377,6 +384,19 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
}
}
/**
* Creates and initializes the special accounts ('Integrated Inbox' and 'All Messages')
*/
private void createSpecialAccounts() {
unreadAccount = new SearchAccount(this, false, null, null);
unreadAccount.setDescription(getString(R.string.search_all_messages_title));
unreadAccount.setEmail(getString(R.string.search_all_messages_detail));
integratedInboxAccount = new SearchAccount(this, true, null, null);
integratedInboxAccount.setDescription(getString(R.string.integrated_inbox_title));
integratedInboxAccount.setEmail(getString(R.string.integrated_inbox_detail));
}
@SuppressWarnings("unchecked")
private void restoreAccountStats(Bundle icicle) {
if (icicle != null) {
@ -461,9 +481,13 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
accounts = Preferences.getPreferences(this).getAccounts();
List<BaseAccount> newAccounts;
if (!K9.isHideSpecialAccounts()
&& accounts.length > 0) {
newAccounts = new ArrayList<BaseAccount>(accounts.length + 2);
if (!K9.isHideSpecialAccounts() && accounts.length > 0) {
if (integratedInboxAccount == null || unreadAccount == null) {
createSpecialAccounts();
}
newAccounts = new ArrayList<BaseAccount>(accounts.length +
SPECIAL_ACCOUNTS_COUNT);
newAccounts.add(integratedInboxAccount);
newAccounts.add(unreadAccount);
} else {
@ -990,6 +1014,20 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
}
}
});
case DIALOG_NO_FILE_MANAGER:
return ConfirmationDialog.create(this, id,
R.string.import_dialog_error_title,
getString(R.string.import_dialog_error_message),
R.string.open_market,
R.string.close,
new Runnable() {
@Override
public void run() {
Uri uri = Uri.parse(ANDROID_MARKET_URL);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
});
}
return super.onCreateDialog(id);
}
@ -1011,6 +1049,9 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
alert.setMessage(getString(R.string.account_recreate_dlg_instructions_fmt,
mSelectedContextAccount.getDescription()));
break;
case DIALOG_NO_FILE_MANAGER:
alert.setMessage(getString(R.string.import_dialog_error_message));
break;
}
super.onPrepareDialog(id, d);
@ -1266,7 +1307,16 @@ public class Accounts extends K9ListActivity implements OnItemClickListener, OnC
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType(MimeUtility.K9_SETTINGS_MIME_TYPE);
startActivityForResult(Intent.createChooser(i, null), ACTIVITY_REQUEST_PICK_SETTINGS_FILE);
PackageManager packageManager = getPackageManager();
List<ResolveInfo> infos = packageManager.queryIntentActivities(i, 0);
if (infos.size() > 0) {
startActivityForResult(Intent.createChooser(i, null),
ACTIVITY_REQUEST_PICK_SETTINGS_FILE);
} else {
showDialog(DIALOG_NO_FILE_MANAGER);
}
}
@Override

View File

@ -3,53 +3,42 @@ package com.fsck.k9.activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import com.fsck.k9.Account;
import com.fsck.k9.FontSizes;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.BaseAccount;
import com.fsck.k9.R;
import com.fsck.k9.SearchSpecification;
public class LauncherShortcuts extends K9ListActivity implements OnItemClickListener {
private AccountsAdapter mAdapter;
private FontSizes mFontSizes = K9.getFontSizes();
public class LauncherShortcuts extends AccountList {
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// finish() immediately if we aren't supposed to be here
if (!Intent.ACTION_CREATE_SHORTCUT.equals(getIntent().getAction())) {
finish();
return;
}
setContentView(R.layout.launcher_shortcuts);
ListView listView = getListView();
listView.setOnItemClickListener(this);
listView.setItemsCanFocus(false);
refresh();
super.onCreate(icicle);
}
private void refresh() {
Account[] accounts = Preferences.getPreferences(this).getAccounts();
mAdapter = new AccountsAdapter(accounts);
getListView().setAdapter(mAdapter);
@Override
protected boolean displaySpecialAccounts() {
return true;
}
private void setupShortcut(Account account) {
final Intent shortcutIntent = FolderList.actionHandleAccountIntent(this, account, null, true);
@Override
protected void onAccountSelected(BaseAccount account) {
Intent shortcutIntent = null;
if (account instanceof SearchSpecification) {
shortcutIntent = MessageList.actionHandleAccountIntent(this, account.getDescription(),
(SearchSpecification) account);
} else {
shortcutIntent = FolderList.actionHandleAccountIntent(this, (Account) account, null,
true);
}
shortcutIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
String description = account.getDescription();
@ -63,66 +52,4 @@ public class LauncherShortcuts extends K9ListActivity implements OnItemClickList
setResult(RESULT_OK, intent);
finish();
}
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Account account = (Account) parent.getItemAtPosition(position);
setupShortcut(account);
}
class AccountsAdapter extends ArrayAdapter<Account> {
public AccountsAdapter(Account[] accounts) {
super(LauncherShortcuts.this, 0, accounts);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final Account account = getItem(position);
final View view;
if (convertView != null) {
view = convertView;
} else {
view = getLayoutInflater().inflate(R.layout.accounts_item, parent, false);
view.findViewById(R.id.active_icons).setVisibility(View.GONE);
}
AccountViewHolder holder = (AccountViewHolder) view.getTag();
if (holder == null) {
holder = new AccountViewHolder();
holder.description = (TextView) view.findViewById(R.id.description);
holder.email = (TextView) view.findViewById(R.id.email);
holder.chip = view.findViewById(R.id.chip);
view.setTag(holder);
}
String description = account.getDescription();
if (account.getEmail().equals(description)) {
holder.email.setVisibility(View.GONE);
} else {
holder.email.setVisibility(View.VISIBLE);
holder.email.setText(account.getEmail());
}
if (description == null || description.length() == 0) {
description = account.getEmail();
}
holder.description.setText(description);
holder.chip.setBackgroundColor(account.getChipColor());
holder.chip.getBackground().setAlpha(255);
holder.description.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getAccountName());
holder.email.setTextSize(TypedValue.COMPLEX_UNIT_SP, mFontSizes.getAccountDescription());
return view;
}
class AccountViewHolder {
public TextView description;
public TextView email;
public View chip;
}
}
}

View File

@ -1611,7 +1611,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
final Account account = Preferences.getPreferences(this).getAccount(mMessageReference.accountUuid);
final String folderName = mMessageReference.folderName;
final String sourceMessageUid = mMessageReference.uid;
MessagingController.getInstance(getApplication()).setFlag(account, folderName, new String[] {sourceMessageUid}, mMessageReference.flag, true);
MessagingController.getInstance(getApplication()).setFlag(account, folderName, sourceMessageUid, mMessageReference.flag, true);
}
mDraftNeedsSaving = false;

View File

@ -52,10 +52,12 @@ import android.widget.Toast;
import com.fsck.k9.Account;
import com.fsck.k9.AccountStats;
import com.fsck.k9.BaseAccount;
import com.fsck.k9.FontSizes;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.R;
import com.fsck.k9.SearchAccount;
import com.fsck.k9.SearchSpecification;
import com.fsck.k9.activity.setup.AccountSettings;
import com.fsck.k9.activity.setup.FolderSettings;
@ -70,6 +72,7 @@ import com.fsck.k9.mail.Folder;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.store.LocalStore;
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
import com.fsck.k9.mail.store.StorageManager;
@ -613,7 +616,11 @@ public class MessageList
context.startActivity(intent);
}
public static void actionHandle(Context context, String title, SearchSpecification searchSpecification) {
/**
* Creates and returns an intent that opens Unified Inbox or All Messages screen.
*/
public static Intent actionHandleAccountIntent(Context context, String title,
SearchSpecification searchSpecification) {
Intent intent = new Intent(context, MessageList.class);
intent.putExtra(EXTRA_QUERY, searchSpecification.getQuery());
if (searchSpecification.getRequiredFlags() != null) {
@ -629,6 +636,13 @@ public class MessageList
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
return intent;
}
public static void actionHandle(Context context, String title,
SearchSpecification searchSpecification) {
Intent intent = actionHandleAccountIntent(context, title, searchSpecification);
context.startActivity(intent);
}
@ -1392,13 +1406,21 @@ public class MessageList
}
private void onToggleRead(MessageInfoHolder holder) {
mController.setFlag(holder.message.getFolder().getAccount(), holder.message.getFolder().getName(), new String[] { holder.uid }, Flag.SEEN, !holder.read);
LocalMessage message = holder.message;
Folder folder = message.getFolder();
Account account = folder.getAccount();
String folderName = folder.getName();
mController.setFlag(account, folderName, new Message[] { message }, Flag.SEEN, !holder.read);
holder.read = !holder.read;
mHandler.sortMessages();
}
private void onToggleFlag(MessageInfoHolder holder) {
mController.setFlag(holder.message.getFolder().getAccount(), holder.message.getFolder().getName(), new String[] { holder.uid }, Flag.FLAGGED, !holder.flagged);
LocalMessage message = holder.message;
Folder folder = message.getFolder();
Account account = folder.getAccount();
String folderName = folder.getName();
mController.setFlag(account, folderName, new Message[] { message }, Flag.FLAGGED, !holder.flagged);
holder.flagged = !holder.flagged;
mHandler.sortMessages();
}

View File

@ -20,7 +20,6 @@ import com.fsck.k9.crypto.PgpData;
import com.fsck.k9.helper.FileBrowserHelper;
import com.fsck.k9.helper.FileBrowserHelper.FileBrowserFailOverCallback;
import com.fsck.k9.mail.*;
import com.fsck.k9.mail.store.LocalStore.LocalMessage;
import com.fsck.k9.mail.store.StorageManager;
import com.fsck.k9.view.AttachmentView;
import com.fsck.k9.view.ToggleScrollView;
@ -715,17 +714,8 @@ public class MessageView extends K9Activity implements OnClickListener {
if (mMessage != null) {
boolean newState = !mMessage.isSet(Flag.FLAGGED);
mController.setFlag(mAccount, mMessage.getFolder().getName(),
new String[] {mMessage.getUid()}, Flag.FLAGGED, newState);
try {
// FIXME: This is a hack to change the flagged state of our message object. We
// can't call Message.setFlag() because that would "adjust" the flagged count
// another time (first time by MessagingController.setFlag(...)).
((LocalMessage)mMessage).setFlagInternal(Flag.FLAGGED, newState);
mMessageView.setHeaders(mMessage, mAccount);
} catch (MessagingException me) {
Log.e(K9.LOG_TAG, "Could not set flag on local message", me);
}
new Message[] { mMessage }, Flag.FLAGGED, newState);
mMessageView.setHeaders(mMessage, mAccount);
}
}
@ -858,18 +848,10 @@ public class MessageView extends K9Activity implements OnClickListener {
private void onMarkAsUnread() {
if (mMessage != null) {
mController.setFlag(mAccount, mMessage.getFolder().getName(),
new String[] { mMessage.getUid() }, Flag.SEEN, false);
try {
// FIXME: This is a hack to mark our message object as unread. We can't call
// Message.setFlag() because that would "adjust" the unread count twice.
((LocalMessage)mMessage).setFlagInternal(Flag.SEEN, false);
mMessageView.setHeaders(mMessage, mAccount);
String subject = mMessage.getSubject();
setTitle(subject);
} catch (Exception e) {
Log.e(K9.LOG_TAG, "Unable to unset SEEN flag on message", e);
}
new Message[] { mMessage }, Flag.SEEN, false);
mMessageView.setHeaders(mMessage, mAccount);
String subject = mMessage.getSubject();
setTitle(subject);
}
}

View File

@ -0,0 +1,106 @@
package com.fsck.k9.activity;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import com.fsck.k9.Account;
import com.fsck.k9.BaseAccount;
import com.fsck.k9.R;
import com.fsck.k9.provider.UnreadWidgetProvider;
/**
* Activity to select an account for the unread widget.
*/
public class UnreadWidgetConfiguration extends AccountList {
/**
* Name of the preference file to store the widget configuration.
*/
private static final String PREFS_NAME = "unread_widget_configuration.xml";
/**
* Prefix for the preference keys.
*/
private static final String PREF_PREFIX_KEY = "unread_widget.";
/**
* The ID of the widget we are configuring.
*/
private int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
// Find the widget ID from the intent.
Intent intent = getIntent();
Bundle extras = intent.getExtras();
if (extras != null) {
mAppWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
}
// If they gave us an intent without the widget ID, just bail.
if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
finish();
return;
}
setTitle(R.string.unread_widget_select_account);
}
@Override
protected boolean displaySpecialAccounts() {
return false;
}
@Override
protected void onAccountSelected(BaseAccount baseAccount) {
if (!(baseAccount instanceof Account)) {
finish();
return;
}
Account account = (Account) baseAccount;
// Save widget configuration
String accountUuid = account.getUuid();
saveAccountUuid(this, mAppWidgetId, accountUuid);
// Update widget
Context context = getApplicationContext();
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
UnreadWidgetProvider.updateWidget(context, appWidgetManager, mAppWidgetId, accountUuid);
// Let the caller know that the configuration was successful
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
}
private static void saveAccountUuid(Context context, int appWidgetId, String accountUuid) {
SharedPreferences.Editor editor =
context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit();
editor.putString(PREF_PREFIX_KEY + appWidgetId, accountUuid);
editor.commit();
}
public static String getAccountUuid(Context context, int appWidgetId) {
SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
String accountUuid = prefs.getString(PREF_PREFIX_KEY + appWidgetId, null);
return accountUuid;
}
public static void deleteWidgetConfiguration(Context context, int appWidgetId) {
Editor editor = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit();
editor.remove(PREF_PREFIX_KEY + appWidgetId);
editor.commit();
}
}

View File

@ -2622,51 +2622,67 @@ public class MessagingController implements Runnable {
@Override
public void act(final Account account, final Folder folder,
final List<Message> messages) {
String[] uids = new String[messages.size()];
for (int i = 0; i < messages.size(); i++) {
uids[i] = messages.get(i).getUid();
}
setFlag(account, folder.getName(), uids, flag, newState);
setFlag(account, folder.getName(), messages.toArray(EMPTY_MESSAGE_ARRAY), flag,
newState);
}
});
}
public void setFlag(
final Account account,
final String folderName,
final String[] uids,
final Flag flag,
final boolean newState) {
// TODO: put this into the background, but right now that causes odd behavior
// because the FolderMessageList doesn't have its own cache of the flag states
/**
* Set or remove a flag for a set of messages in a specific folder.
*
* <p>
* The {@link Message} objects passed in are updated to reflect the new flag state.
* </p>
*
* @param account
* The account the folder containing the messages belongs to.
* @param folderName
* The name of the folder.
* @param messages
* The messages to change the flag for.
* @param flag
* The flag to change.
* @param newState
* {@code true}, if the flag should be set. {@code false} if it should be removed.
*/
public void setFlag(Account account, String folderName, Message[] messages, Flag flag,
boolean newState) {
// TODO: Put this into the background, but right now some callers depend on the message
// objects being modified right after this method returns.
LocalFolder localFolder = null;
try {
LocalStore localStore = account.getLocalStore();
localFolder = localStore.getFolder(folderName);
localFolder.open(OpenMode.READ_WRITE);
ArrayList<Message> messages = new ArrayList<Message>();
for (String uid : uids) {
// Allows for re-allowing sending of messages that could not be sent
if (flag == Flag.FLAGGED && !newState
&& uid != null
&& account.getOutboxFolderName().equals(folderName)) {
sendCount.remove(uid);
}
Message msg = localFolder.getMessage(uid);
if (msg != null) {
messages.add(msg);
// Allows for re-allowing sending of messages that could not be sent
if (flag == Flag.FLAGGED && !newState &&
account.getOutboxFolderName().equals(folderName)) {
for (Message message : messages) {
String uid = message.getUid();
if (uid != null) {
sendCount.remove(uid);
}
}
}
localFolder.setFlags(messages.toArray(EMPTY_MESSAGE_ARRAY), new Flag[] {flag}, newState);
// Update the messages in the local store
localFolder.setFlags(messages, new Flag[] {flag}, newState);
for (MessagingListener l : getListeners()) {
l.folderStatusChanged(account, folderName, localFolder.getUnreadMessageCount());
}
/*
* Handle the remote side
*/
// The error folder is always a local folder
// TODO: Skip the remote part for all local-only folders
if (account.getErrorFolderName().equals(folderName)) {
return;
}
@ -2675,16 +2691,54 @@ public class MessagingController implements Runnable {
return;
}
String[] uids = new String[messages.length];
for (int i = 0, end = uids.length; i < end; i++) {
uids[i] = messages[i].getUid();
}
queueSetFlag(account, folderName, Boolean.toString(newState), flag.toString(), uids);
processPendingCommands(account);
} catch (MessagingException me) {
addErrorMessage(account, null, me);
throw new RuntimeException(me);
} finally {
closeFolder(localFolder);
}
}//setMesssageFlag
}
/**
* Set or remove a flag for a message referenced by message UID.
*
* @param account
* The account the folder containing the message belongs to.
* @param folderName
* The name of the folder.
* @param uid
* The UID of the message to change the flag for.
* @param flag
* The flag to change.
* @param newState
* {@code true}, if the flag should be set. {@code false} if it should be removed.
*/
public void setFlag(Account account, String folderName, String uid, Flag flag,
boolean newState) {
Folder localFolder = null;
try {
LocalStore localStore = account.getLocalStore();
localFolder = localStore.getFolder(folderName);
localFolder.open(OpenMode.READ_WRITE);
Message message = localFolder.getMessage(uid);
if (message != null) {
setFlag(account, folderName, new Message[] { message }, flag, newState);
}
} catch (MessagingException me) {
addErrorMessage(account, null, me);
throw new RuntimeException(me);
} finally {
closeFolder(localFolder);
}
}
public void clearAllPending(final Account account) {
try {
@ -3334,6 +3388,8 @@ public class MessagingController implements Runnable {
Store remoteStore = account.getRemoteStore();
LocalFolder localSrcFolder = localStore.getFolder(srcFolder);
LocalFolder localDestFolder = localStore.getFolder(destFolder);
boolean unreadCountAffected = false;
List<String> uids = new LinkedList<String>();
List<String> localUids = new LinkedList<String>();
@ -3350,6 +3406,10 @@ public class MessagingController implements Runnable {
needToLocalizeSourceFolder = true;
}
}
if (!unreadCountAffected && !message.isSet(Flag.SEEN)) {
unreadCountAffected = true;
}
}
// make sure the remote folder actually gets created even though it won't be used yet.
@ -3417,6 +3477,15 @@ public class MessagingController implements Runnable {
fp.add(FetchProfile.Item.BODY);
localSrcFolder.fetch(messages, fp, null);
localSrcFolder.copyMessages(messages, localDestFolder);
if (unreadCountAffected) {
// If this copy operation changes the unread count in the destination
// folder, notify the listeners.
int unreadMessageCount = localDestFolder.getUnreadMessageCount();
for (MessagingListener l : getListeners()) {
l.folderStatusChanged(account, destFolder, unreadMessageCount);
}
}
} else {
localSrcFolder.moveMessages(messages, localDestFolder);
for (Map.Entry<String, Message> entry : origUidMap.entrySet()) {
@ -3427,6 +3496,16 @@ public class MessagingController implements Runnable {
}
unsuppressMessage(account, srcFolder, origUid);
}
if (unreadCountAffected) {
// If this move operation changes the unread count, notify the listeners
// that the unread count changed in both the source and destination folder.
int unreadMessageCountSrc = localSrcFolder.getUnreadMessageCount();
int unreadMessageCountDest = localDestFolder.getUnreadMessageCount();
for (MessagingListener l : getListeners()) {
l.folderStatusChanged(account, srcFolder, unreadMessageCountSrc);
l.folderStatusChanged(account, destFolder, unreadMessageCountDest);
}
}
}
if (!localDestFolder.isLocalOnly() &&
@ -3480,6 +3559,17 @@ public class MessagingController implements Runnable {
}
unsuppressMessage(account, srcFolder, origUid);
}
if (unreadCountAffected) {
// If this move operation changes the unread count, notify the listeners
// that the unread count changed in both the source and destination folder.
int unreadMessageCountSrc = localSrcFolder.getUnreadMessageCount();
int unreadMessageCountDest = localDestFolder.getUnreadMessageCount();
for (MessagingListener l : getListeners()) {
l.folderStatusChanged(account, srcFolder, unreadMessageCountSrc);
l.folderStatusChanged(account, destFolder, unreadMessageCountDest);
}
}
}
FetchProfile fp = new FetchProfile();
@ -3495,6 +3585,16 @@ public class MessagingController implements Runnable {
// local message copy to local folder
localSrcFolder.copyMessages(localMessages, localDestFolder);
}
if (isCopy && unreadCountAffected) {
// ASH this did happen right after removed "localSrcFolder.copyMessages(messages, localDestFolder);"
// If this copy operation changes the unread count in the destination
// folder, notify the listeners.
int unreadMessageCount = localDestFolder.getUnreadMessageCount();
for (MessagingListener l : getListeners()) {
l.folderStatusChanged(account, destFolder, unreadMessageCount);
}
}
}
processPendingCommands(account);
@ -3752,7 +3852,8 @@ public class MessagingController implements Runnable {
localFolder = localStore.getFolder(account.getTrashFolderName());
localFolder.open(OpenMode.READ_WRITE);
localFolder.setFlags(new Flag[] { Flag.DELETED }, true);
localFolder.resetUnreadAndFlaggedCounts();
localFolder.setUnreadMessageCount(0);
localFolder.setFlaggedMessageCount(0);
for (MessagingListener l : getListeners()) {
l.emptyTrashCompleted(account);

View File

@ -12,191 +12,154 @@ import com.fsck.k9.mail.Part;
import java.util.List;
/**
* Defines the interface that MessagingController will use to callback to requesters. This class
* is defined as non-abstract so that someone who wants to receive only a few messages can
* do so without implementing the entire interface. It is highly recommended that users of
* this interface use the @Override annotation in their implementations to avoid being caught by
* Defines the interface that {@link MessagingController} will use to callback to requesters.
*
* <p>
* This class is defined as non-abstract so that someone who wants to receive only a few messages
* can do so without implementing the entire interface. It is highly recommended that users of this
* interface use the {@code @Override} annotation in their implementations to avoid being caught by
* changes in this class.
* </p>
*/
public class MessagingListener {
public void searchStats(AccountStats stats) {}
public void accountStatusChanged(BaseAccount account, AccountStats stats) {
}
public void accountSizeChanged(Account account, long oldSize, long newSize) {
}
public void accountStatusChanged(BaseAccount account, AccountStats stats) {}
public void listFoldersStarted(Account account) {
}
public void accountSizeChanged(Account account, long oldSize, long newSize) {}
public void listFolders(Account account, Folder[] folders) {
}
public void listFoldersFailed(Account account, String message) {
}
public void listFoldersStarted(Account account) {}
public void listFoldersFinished(Account account) {
}
public void listFolders(Account account, Folder[] folders) {}
public void listLocalMessagesStarted(Account account, String folder) {
}
public void listFoldersFinished(Account account) {}
public void listLocalMessages(Account account, String folder, Message[] messages) {
}
public void listFoldersFailed(Account account, String message) {}
public void listLocalMessagesAddMessages(Account account, String folder, List<Message> messages) {
}
public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) {
}
public void listLocalMessagesStarted(Account account, String folder) {}
public void listLocalMessagesRemoveMessage(Account account, String folder, Message message) {
}
public void listLocalMessages(Account account, String folder, Message[] messages) {}
public void listLocalMessagesFailed(Account account, String folder, String message) {
}
public void listLocalMessagesAddMessages(Account account, String folder,
List<Message> messages) {}
public void listLocalMessagesFinished(Account account, String folder) {
}
public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) {}
public void synchronizeMailboxStarted(Account account, String folder) {
}
public void listLocalMessagesRemoveMessage(Account account, String folder, Message message) {}
public void synchronizeMailboxHeadersStarted(Account account, String folder) {
}
public void listLocalMessagesFinished(Account account, String folder) {}
public void synchronizeMailboxHeadersProgress(Account account, String folder, int completed, int total) {
}
public void listLocalMessagesFailed(Account account, String folder, String message) {}
public void synchronizeMailboxStarted(Account account, String folder) {}
public void synchronizeMailboxHeadersStarted(Account account, String folder) {}
public void synchronizeMailboxHeadersProgress(Account account, String folder,
int completed, int total) {}
public void synchronizeMailboxHeadersFinished(Account account, String folder,
int totalMessagesInMailbox, int numNewMessages) {
}
int totalMessagesInMailbox, int numNewMessages) {}
public void synchronizeMailboxProgress(Account account, String folder, int completed,
int total) {}
public void synchronizeMailboxProgress(Account account, String folder, int completed, int total)
{}
public void synchronizeMailboxNewMessage(Account account, String folder, Message message) {}
public void synchronizeMailboxNewMessage(Account account, String folder, Message message) {
}
public void synchronizeMailboxAddOrUpdateMessage(Account account, String folder,
Message message) {}
public void synchronizeMailboxAddOrUpdateMessage(Account account, String folder, Message message) {
}
public void synchronizeMailboxRemovedMessage(Account account, String folder, Message message) {
}
public void synchronizeMailboxRemovedMessage(Account account, String folder,
Message message) {}
public void synchronizeMailboxFinished(Account account, String folder,
int totalMessagesInMailbox, int numNewMessages) {
}
int totalMessagesInMailbox, int numNewMessages) {}
public void synchronizeMailboxFailed(Account account, String folder,
String message) {
}
public void synchronizeMailboxFailed(Account account, String folder, String message) {}
public void loadMessageForViewStarted(Account account, String folder, String uid) {
}
public void loadMessageForViewStarted(Account account, String folder, String uid) {}
public void loadMessageForViewHeadersAvailable(Account account, String folder, String uid,
Message message) {
}
Message message) {}
public void loadMessageForViewBodyAvailable(Account account, String folder, String uid,
Message message) {
}
Message message) {}
public void loadMessageForViewFinished(Account account, String folder, String uid,
Message message) {
}
Message message) {}
public void loadMessageForViewFailed(Account account, String folder, String uid, Throwable t) {
}
public void loadMessageForViewFailed(Account account, String folder, String uid,
Throwable t) {}
/**
* Called when a message for view has been fully displayed on the screen.
*/
public void messageViewFinished() {}
public void checkMailStarted(Context context, Account account) {
}
public void checkMailFinished(Context context, Account account) {
}
public void checkMailStarted(Context context, Account account) {}
public void checkMailFailed(Context context, Account account, String reason) {
}
public void checkMailFinished(Context context, Account account) {}
public void sendPendingMessagesStarted(Account account) {
}
public void checkMailFailed(Context context, Account account, String reason) {}
public void sendPendingMessagesCompleted(Account account) {
}
public void sendPendingMessagesFailed(Account account) {
}
public void sendPendingMessagesStarted(Account account) {}
public void messageDeleted(Account account, String folder, Message message) {
public void sendPendingMessagesCompleted(Account account) {}
}
public void emptyTrashCompleted(Account account) {
}
public void sendPendingMessagesFailed(Account account) {}
public void folderStatusChanged(Account account, String folderName, int unreadMessageCount) {
}
public void folderStatusChanged(Account account, String folderName) {
}
public void emptyTrashCompleted(Account account) {}
public void systemStatusChanged() {
}
public void messageUidChanged(Account account, String folder, String oldUid, String newUid) {
public void folderStatusChanged(Account account, String folderName, int unreadMessageCount) {}
}
public void setPushActive(Account account, String folderName, boolean enabled) {
public void systemStatusChanged() {}
}
public void loadAttachmentStarted(
Account account,
Message message,
Part part,
Object tag,
boolean requiresDownload) {
}
public void messageDeleted(Account account, String folder, Message message) {}
public void loadAttachmentFinished(
Account account,
Message message,
Part part,
Object tag) {
}
public void messageUidChanged(Account account, String folder, String oldUid, String newUid) {}
public void loadAttachmentFailed(
Account account,
Message message,
Part part,
Object tag,
String reason) {
}
public void setPushActive(Account account, String folderName, boolean enabled) {}
public void loadAttachmentStarted(Account account, Message message, Part part, Object tag,
boolean requiresDownload) {}
public void loadAttachmentFinished(Account account, Message message, Part part, Object tag) {}
public void loadAttachmentFailed(Account account, Message message, Part part, Object tag,
String reason) {}
public void pendingCommandStarted(Account account, String commandTitle) {}
public void pendingCommandsProcessing(Account account) {}
public void pendingCommandsFinished(Account account) {}
public void pendingCommandStarted(Account account, String commandTitle)
{}
public void pendingCommandCompleted(Account account, String commandTitle)
{}
public void pendingCommandCompleted(Account account, String commandTitle) {}
public void pendingCommandsFinished(Account account) {}
/**
* General notification messages subclasses can override to be notified that the controller
* has completed a command. This is useful for turning off progress indicators that may have
* been left over from previous commands.
* @param moreCommandsToRun True if the controller will continue on to another command
* immediately.
*
* @param moreCommandsToRun
* {@code true} if the controller will continue on to another command immediately.
* {@code false} otherwise.
*/
public void controllerCommandCompleted(boolean moreCommandsToRun) {
}
public void controllerCommandCompleted(boolean moreCommandsToRun) {}
}

View File

@ -0,0 +1,127 @@
package com.fsck.k9.provider;
import com.fsck.k9.Account;
import com.fsck.k9.AccountStats;
import com.fsck.k9.K9;
import com.fsck.k9.Preferences;
import com.fsck.k9.R;
import com.fsck.k9.activity.UnreadWidgetConfiguration;
import com.fsck.k9.activity.FolderList;
import com.fsck.k9.activity.MessageList;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
public class UnreadWidgetProvider extends AppWidgetProvider {
private static final int MAX_COUNT = 9999;
/**
* Trigger update for all of our unread widgets.
*
* @param context
* The {@code Context} object to use for the broadcast intent.
*/
public static void updateUnreadCount(Context context) {
Context appContext = context.getApplicationContext();
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(appContext);
ComponentName thisWidget = new ComponentName(appContext, UnreadWidgetProvider.class);
int[] widgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
Intent intent = new Intent(context, UnreadWidgetProvider.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIds);
context.sendBroadcast(intent);
}
public static void updateWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId, String accountUuid) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.unread_widget_layout);
int unreadCount = 0;
String accountName = context.getString(R.string.app_name);
Intent clickIntent = null;
try {
Account account = Preferences.getPreferences(context).getAccount(accountUuid);
if (account != null) {
AccountStats stats = new AccountStats();
account.getLocalStore().getMessageCounts(stats);
unreadCount = stats.unreadMessageCount;
accountName = account.getDescription();
if (K9.FOLDER_NONE.equals(account.getAutoExpandFolderName())) {
clickIntent = FolderList.actionHandleAccountIntent(context, account, null);
} else {
clickIntent = MessageList.actionHandleFolderIntent(context, account,
account.getAutoExpandFolderName());
}
clickIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
}
} catch (Exception e) {
if (K9.DEBUG) {
Log.e(K9.LOG_TAG, "Error getting widget configuration", e);
}
}
if (unreadCount <= 0) {
// Hide TextView for unread count if there are no unread messages.
remoteViews.setViewVisibility(R.id.unread_count, View.GONE);
} else {
remoteViews.setViewVisibility(R.id.unread_count, View.VISIBLE);
String displayCount = (unreadCount <= MAX_COUNT) ?
String.valueOf(unreadCount) : String.valueOf(MAX_COUNT) + "+";
remoteViews.setTextViewText(R.id.unread_count, displayCount);
}
remoteViews.setTextViewText(R.id.account_name, accountName);
if (clickIntent == null) {
// If the widget configuration couldn't be loaded we open the configuration
// activity when the user clicks the widget.
clickIntent = new Intent(context, UnreadWidgetConfiguration.class);
clickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
}
clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, appWidgetId,
clickIntent, 0);
remoteViews.setOnClickPendingIntent(R.id.unread_widget_layout, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
/**
* Called when one or more widgets need to be updated.
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for (int widgetId : appWidgetIds) {
String accountUuid = UnreadWidgetConfiguration.getAccountUuid(context, widgetId);
updateWidget(context, appWidgetManager, widgetId, accountUuid);
}
}
/**
* Called when a widget instance is deleted.
*/
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
UnreadWidgetConfiguration.deleteWidgetConfiguration(context, appWidgetId);
}
}
}

View File

@ -102,15 +102,19 @@ public class SingleMessageView extends LinearLayout {
// content://<nameofpackage>.providers.StatusProvider
cursor = cr.query(Uri.parse("content://" + screenReader.serviceInfo.packageName
+ ".providers.StatusProvider"), null, null, null, null);
if (cursor != null) {
cursor.moveToFirst();
// These content providers use a special cursor that only has
// one element,
// an integer that is 1 if the screen reader is running.
status = cursor.getInt(0);
cursor.close();
if (status == 1) {
return true;
try {
if (cursor != null && cursor.moveToFirst()) {
// These content providers use a special cursor that only has
// one element,
// an integer that is 1 if the screen reader is running.
status = cursor.getInt(0);
if (status == 1) {
return true;
}
}
} finally {
if (cursor != null) {
cursor.close();
}
}
}