Merge pull request #2255 from k9mail/message_list_widget

Message list widget
This commit is contained in:
cketti 2017-02-19 23:18:39 +01:00 committed by GitHub
commit b1b2d0a3f1
13 changed files with 495 additions and 2 deletions

View file

@ -356,6 +356,23 @@
android:resource="@xml/unread_widget_info"/>
</receiver>
<receiver
android:name=".widget.list.MessageListWidgetProvider"
android:icon="@drawable/message_list_widget_preview"
android:label="@string/mail_list_widget_text">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/message_list_widget_info" />
</receiver>
<service
android:name=".widget.list.MessageListWidgetService"
android:enabled="true"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<service
android:name=".service.MailService"
android:enabled="true"/>

View file

@ -44,6 +44,7 @@ import com.fsck.k9.service.BootReceiver;
import com.fsck.k9.service.MailService;
import com.fsck.k9.service.ShutdownReceiver;
import com.fsck.k9.service.StorageGoneReceiver;
import com.fsck.k9.widget.list.MessageListWidgetProvider;
public class K9 extends Application {
/**
@ -584,22 +585,37 @@ public class K9 extends Application {
}
}
private void updateMailListWidget() {
try {
MessageListWidgetProvider.triggerMessageListWidgetUpdate(K9.this);
} catch (RuntimeException e) {
if (BuildConfig.DEBUG) {
throw e;
} else if (K9.DEBUG) {
Log.e(LOG_TAG, "Error while updating message list widget", e);
}
}
}
@Override
public void synchronizeMailboxRemovedMessage(Account account, String folder, Message message) {
broadcastIntent(K9.Intents.EmailReceived.ACTION_EMAIL_DELETED, account, folder, message);
updateUnreadWidget();
updateMailListWidget();
}
@Override
public void messageDeleted(Account account, String folder, Message message) {
broadcastIntent(K9.Intents.EmailReceived.ACTION_EMAIL_DELETED, account, folder, message);
updateUnreadWidget();
updateMailListWidget();
}
@Override
public void synchronizeMailboxNewMessage(Account account, String folder, Message message) {
broadcastIntent(K9.Intents.EmailReceived.ACTION_EMAIL_RECEIVED, account, folder, message);
updateUnreadWidget();
updateMailListWidget();
}
@Override
@ -607,6 +623,7 @@ public class K9 extends Application {
int unreadMessageCount) {
updateUnreadWidget();
updateMailListWidget();
// let observers know a change occurred
Intent intent = new Intent(K9.Intents.EmailReceived.ACTION_REFRESH_OBSERVER, null);

View file

@ -0,0 +1,209 @@
package com.fsck.k9.widget.list;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Locale;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Binder;
import android.support.v4.content.ContextCompat;
import android.text.SpannableString;
import android.text.style.StyleSpan;
import android.view.View;
import android.widget.RemoteViews;
import android.widget.RemoteViewsService;
import com.fsck.k9.K9;
import com.fsck.k9.R;
import com.fsck.k9.provider.MessageProvider;
public class MessageListRemoteViewFactory implements RemoteViewsService.RemoteViewsFactory {
private static String[] MAIL_LIST_PROJECTIONS = {
MessageProvider.MessageColumns.SENDER,
MessageProvider.MessageColumns.SEND_DATE,
MessageProvider.MessageColumns.SUBJECT,
MessageProvider.MessageColumns.PREVIEW,
MessageProvider.MessageColumns.UNREAD,
MessageProvider.MessageColumns.HAS_ATTACHMENTS,
MessageProvider.MessageColumns.URI
};
private final Context context;
private final Calendar calendar;
private final ArrayList<MailItem> mailItems = new ArrayList<>(25);
private boolean senderAboveSubject;
private int readTextColor;
private int unreadTextColor;
public MessageListRemoteViewFactory(Context context) {
this.context = context;
calendar = Calendar.getInstance();
}
@Override
public void onCreate() {
senderAboveSubject = K9.messageListSenderAboveSubject();
readTextColor = ContextCompat.getColor(context, R.color.message_list_widget_text_read);
unreadTextColor = ContextCompat.getColor(context, R.color.message_list_widget_text_unread);
}
@Override
public void onDataSetChanged() {
long identityToken = Binder.clearCallingIdentity();
try {
loadMessageList();
} finally {
Binder.restoreCallingIdentity(identityToken);
}
}
private void loadMessageList() {
mailItems.clear();
Uri unifiedInboxUri = MessageProvider.CONTENT_URI.buildUpon().appendPath("inbox_messages").build();
Cursor cursor = context.getContentResolver().query(unifiedInboxUri, MAIL_LIST_PROJECTIONS, null, null, null);
if (cursor == null) {
return;
}
try {
while (cursor.moveToNext()) {
String sender = cursor.getString(0);
long date = cursor.isNull(1) ? 0L : cursor.getLong(1);
String subject = cursor.getString(2);
String preview = cursor.getString(3);
boolean unread = toBoolean(cursor.getString(4));
boolean hasAttachment = toBoolean(cursor.getString(5));
Uri viewUri = Uri.parse(cursor.getString(6));
mailItems.add(new MailItem(sender, date, subject, preview, unread, hasAttachment, viewUri));
}
} finally {
cursor.close();
}
}
@Override
public void onDestroy() {
// Implement interface
}
@Override
public int getCount() {
return mailItems.size();
}
@Override
public RemoteViews getViewAt(int position) {
RemoteViews remoteView = new RemoteViews(context.getPackageName(), R.layout.message_list_widget_list_item);
MailItem item = mailItems.get(position);
CharSequence sender = item.unread ? bold(item.sender) : item.sender;
CharSequence subject = item.unread ? bold(item.subject) : item.subject;
if (senderAboveSubject) {
remoteView.setTextViewText(R.id.sender, sender);
remoteView.setTextViewText(R.id.mail_subject, subject);
} else {
remoteView.setTextViewText(R.id.sender, subject);
remoteView.setTextViewText(R.id.mail_subject, sender);
}
remoteView.setTextViewText(R.id.mail_date, item.getDateFormatted("%d %s"));
remoteView.setTextViewText(R.id.mail_preview, item.preview);
int textColor = item.getTextColor();
remoteView.setTextColor(R.id.sender, textColor);
remoteView.setTextColor(R.id.mail_subject, textColor);
remoteView.setTextColor(R.id.mail_date, textColor);
remoteView.setTextColor(R.id.mail_preview, textColor);
if (item.hasAttachment) {
remoteView.setInt(R.id.attachment, "setVisibility", View.VISIBLE);
} else {
remoteView.setInt(R.id.attachment, "setVisibility", View.GONE);
}
Intent intent = new Intent();
intent.setData(item.uri);
remoteView.setOnClickFillInIntent(R.id.mail_list_item, intent);
return remoteView;
}
@Override
public RemoteViews getLoadingView() {
RemoteViews loadingView = new RemoteViews(context.getPackageName(), R.layout.message_list_widget_loading);
loadingView.setTextViewText(R.id.loadingText, context.getString(R.string.mail_list_widget_loading));
loadingView.setViewVisibility(R.id.loadingText, View.VISIBLE);
return loadingView;
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public boolean hasStableIds() {
return true;
}
private CharSequence bold(String text) {
SpannableString spannableString = new SpannableString(text);
spannableString.setSpan(new StyleSpan(Typeface.BOLD), 0, text.length(), 0);
return spannableString;
}
private boolean toBoolean(String value) {
return Boolean.valueOf(value);
}
private class MailItem {
final long date;
final String sender;
final String preview;
final String subject;
final boolean unread;
final boolean hasAttachment;
final Uri uri;
MailItem(String sender, long date, String subject, String preview, boolean unread, boolean hasAttachment,
Uri viewUri) {
this.sender = sender;
this.date = date;
this.preview = preview;
this.subject = subject;
this.unread = unread;
this.uri = viewUri;
this.hasAttachment = hasAttachment;
}
int getTextColor() {
return unread ? unreadTextColor : readTextColor;
}
String getDateFormatted(String format) {
calendar.setTimeInMillis(date);
return String.format(format,
calendar.get(Calendar.DAY_OF_MONTH),
calendar.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.getDefault()));
}
}
}

View file

@ -0,0 +1,85 @@
package com.fsck.k9.widget.list;
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.net.Uri;
import android.widget.RemoteViews;
import com.fsck.k9.R;
import com.fsck.k9.activity.MessageCompose;
import com.fsck.k9.activity.MessageList;
public class MessageListWidgetProvider extends AppWidgetProvider {
private static String ACTION_UPDATE_MESSAGE_LIST = "UPDATE_MESSAGE_LIST";
public static void triggerMessageListWidgetUpdate(Context context) {
Context appContext = context.getApplicationContext();
AppWidgetManager widgetManager = AppWidgetManager.getInstance(appContext);
ComponentName widget = new ComponentName(appContext, MessageListWidgetProvider.class);
int[] widgetIds = widgetManager.getAppWidgetIds(widget);
Intent intent = new Intent(context, MessageListWidgetProvider.class);
intent.setAction(ACTION_UPDATE_MESSAGE_LIST);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIds);
context.sendBroadcast(intent);
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
private void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.message_list_widget_layout);
views.setTextViewText(R.id.folder, context.getString(R.string.integrated_inbox_title));
Intent intent = new Intent(context, MessageListWidgetService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
views.setRemoteAdapter(R.id.listView, intent);
PendingIntent viewAction = viewActionTemplatePendingIntent(context);
views.setPendingIntentTemplate(R.id.listView, viewAction);
PendingIntent composeAction = composeActionPendingIntent(context);
views.setOnClickPendingIntent(R.id.new_message, composeAction);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
@Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
String action = intent.getAction();
if (action.equals(ACTION_UPDATE_MESSAGE_LIST)) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetIds, R.id.listView);
}
}
private PendingIntent viewActionTemplatePendingIntent(Context context) {
Intent intent = new Intent(context, MessageList.class);
intent.setAction(Intent.ACTION_VIEW);
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
private PendingIntent composeActionPendingIntent(Context context) {
Intent intent = new Intent(context, MessageCompose.class);
intent.setAction(MessageCompose.ACTION_COMPOSE);
return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
}

View file

@ -0,0 +1,13 @@
package com.fsck.k9.widget.list;
import android.content.Intent;
import android.widget.RemoteViewsService;
public class MessageListWidgetService extends RemoteViewsService {
@Override
public RemoteViewsFactory onGetViewFactory(Intent intent) {
return new MessageListRemoteViewFactory(getApplicationContext());
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

View file

@ -0,0 +1,46 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/top_controls"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/message_list_widget_header_background"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/folder"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingBottom="12dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="12dp"
android:textSize="20sp"
android:textColor="@color/message_list_widget_header_text"
tools:text="Unified Inbox" />
<ImageButton
android:id="@+id/new_message"
android:layout_width="56dp"
android:layout_height="match_parent"
android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/compose_action"
android:scaleType="center"
android:src="@drawable/ic_action_compose_dark" />
</LinearLayout>
<ListView android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:divider="@color/message_list_widget_divider"
android:dividerHeight="0.5dp" />
</LinearLayout>

View file

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mail_list_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/widget_padding">
<TextView
android:id="@+id/mail_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
tools:text="25 May" />
<ImageView
android:id="@+id/attachment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/mail_date"
android:layout_centerInParent="true"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:layout_toLeftOf="@+id/mail_date"
android:layout_toStartOf="@+id/mail_date"
android:src="@drawable/ic_email_attachment_small"
android:visibility="gone"
tools:visibility="visible" />
<TextView
android:id="@+id/sender"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_gravity="start"
android:layout_toLeftOf="@id/attachment"
android:layout_toStartOf="@id/attachment"
android:maxLines="1"
android:ellipsize="end"
android:textSize="16sp"
tools:text="Kinda long subject that should be long enough to exceed the available display space" />
<TextView
android:id="@+id/mail_subject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/sender"
android:maxLines="1"
android:ellipsize="end"
android:paddingBottom="2dp"
android:textSize="15sp"
tools:text="Wikipedia" />
<TextView
android:id="@+id/mail_preview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/mail_subject"
android:maxLines="1"
android:textSize="13sp"
tools:text="Towel Day is celebrated every year on 25 May as a tribute to the author Douglas Adams by his fans." />
</RelativeLayout>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/loadingText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="@android:color/black"
android:textSize="18sp" />
</LinearLayout>

View file

@ -1,6 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="message_list_item_footer_background">#eeeeee</color>
<color name="light_black">#444</color>
<color name="message_list_widget_header_background">#737373</color>
<color name="message_list_widget_header_text">#e4e4e4</color>
<color name="message_list_widget_divider">#e5e5e5</color>
<color name="message_list_widget_text_read">#444444</color>
<color name="message_list_widget_text_unread">#000000</color>
<color name="light_black">#444444</color>
</resources>

View file

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="button_minWidth">100sp</dimen>
<dimen name="widget_padding">8dp</dimen>
</resources>

View file

@ -1218,4 +1218,6 @@ Please submit bug reports, contribute new features and ask questions at
<string name="apg">APG</string>
<string name="no_crypto_provider_see_global">No OpenPGP app configured, see global settings!</string>
<string name="mail_list_widget_text">K-9 Message List</string>
<string name="mail_list_widget_loading">Loading messages…</string>
</resources>

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialKeyguardLayout="@layout/message_list_widget_layout"
android:initialLayout="@layout/message_list_widget_layout"
android:minHeight="180dp"
android:minWidth="250dp"
android:minResizeWidth="180dp"
android:minResizeHeight="110dp"
android:previewImage="@drawable/message_list_widget_preview"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="86400000"
android:widgetCategory="home_screen|keyguard">
</appwidget-provider>