From b99fcb48f7347be4b96e97e135b4fe158a446d70 Mon Sep 17 00:00:00 2001 From: M66B Date: Wed, 1 Jan 2020 20:18:54 +0100 Subject: [PATCH] Added widget new message count per account --- app/src/main/AndroidManifest.xml | 9 ++ .../eu/faircode/email/ActivityWidget.java | 130 ++++++++++++++++++ .../java/eu/faircode/email/DaoMessage.java | 24 ++-- .../eu/faircode/email/ServiceSynchronize.java | 63 +++++---- .../eu/faircode/email/TupleMessageStats.java | 4 +- .../main/java/eu/faircode/email/Widget.java | 74 +++++----- app/src/main/res/layout/activity_widget.xml | 50 +++++++ app/src/main/res/layout/widget.xml | 16 ++- app/src/main/res/xml/widget.xml | 1 + 9 files changed, 298 insertions(+), 73 deletions(-) create mode 100644 app/src/main/java/eu/faircode/email/ActivityWidget.java create mode 100644 app/src/main/res/layout/activity_widget.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 783605dd37..629f8e300d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -93,6 +93,15 @@ + + + + + + + diff --git a/app/src/main/java/eu/faircode/email/ActivityWidget.java b/app/src/main/java/eu/faircode/email/ActivityWidget.java new file mode 100644 index 0000000000..a5bce79a8a --- /dev/null +++ b/app/src/main/java/eu/faircode/email/ActivityWidget.java @@ -0,0 +1,130 @@ +package eu.faircode.email; + +/* + This file is part of FairEmail. + + FairEmail is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + FairEmail is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with FairEmail. If not, see . + + Copyright 2018-2019 by Marcel Bokhorst (M66B) +*/ + +import android.appwidget.AppWidgetManager; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.Spinner; + +import androidx.constraintlayout.widget.Group; +import androidx.preference.PreferenceManager; + +import java.util.ArrayList; +import java.util.List; + +public class ActivityWidget extends ActivityBase { + private int appWidgetId; + + private Spinner spAccount; + private Button btnSave; + private ContentLoadingProgressBar pbWait; + private Group grpReady; + + private ArrayAdapter adapterAccount; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Bundle extras = getIntent().getExtras(); + if (extras == null) { + finish(); + return; + } + + appWidgetId = extras.getInt( + AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); + + getSupportActionBar().setSubtitle(R.string.title_widget_title_count); + setContentView(R.layout.activity_widget); + + spAccount = findViewById(R.id.spAccount); + btnSave = findViewById(R.id.btnSave); + pbWait = findViewById(R.id.pbWait); + grpReady = findViewById(R.id.grpReady); + + final Intent resultValue = new Intent(); + resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); + + btnSave.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + EntityAccount account = (EntityAccount) spAccount.getSelectedItem(); + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ActivityWidget.this); + SharedPreferences.Editor editor = prefs.edit(); + if (account != null && account.id > 0) + editor.putString("widget." + appWidgetId + ".name", account.name); + else + editor.remove("widget." + appWidgetId + ".name"); + editor.putLong("widget." + appWidgetId + ".account", account == null ? -1L : account.id); + editor.apply(); + + Widget.init(ActivityWidget.this, appWidgetId); + + setResult(RESULT_OK, resultValue); + finish(); + } + }); + + adapterAccount = new ArrayAdapter<>(this, R.layout.spinner_item1, android.R.id.text1, new ArrayList()); + adapterAccount.setDropDownViewResource(R.layout.spinner_item1_dropdown); + spAccount.setAdapter(adapterAccount); + + grpReady.setVisibility(View.GONE); + pbWait.setVisibility(View.VISIBLE); + + setResult(RESULT_CANCELED, resultValue); + + new SimpleTask>() { + @Override + protected List onExecute(Context context, Bundle args) { + DB db = DB.getInstance(context); + + return db.account().getSynchronizingAccounts(); + } + + @Override + protected void onExecuted(Bundle args, List accounts) { + EntityAccount all = new EntityAccount(); + all.id = -1L; + all.name = getString(R.string.title_widget_account_all); + all.primary = false; + accounts.add(0, all); + + adapterAccount.addAll(accounts); + + grpReady.setVisibility(View.VISIBLE); + pbWait.setVisibility(View.GONE); + } + + @Override + protected void onException(Bundle args, Throwable ex) { + Log.unexpectedError(getSupportFragmentManager(), ex); + } + }.execute(this, new Bundle(), "widget:accounts"); + } +} diff --git a/app/src/main/java/eu/faircode/email/DaoMessage.java b/app/src/main/java/eu/faircode/email/DaoMessage.java index c8819f7766..051db0e362 100644 --- a/app/src/main/java/eu/faircode/email/DaoMessage.java +++ b/app/src/main/java/eu/faircode/email/DaoMessage.java @@ -300,19 +300,27 @@ public interface DaoMessage { " WHERE message.id = :id") LiveData liveMessage(long id); - String widget = "SELECT COUNT(message.id) AS unseen, SUM(ABS(notifying)) AS notifying" + + @Query("SELECT account.id, COUNT(message.id) AS unseen, SUM(ABS(notifying)) AS notifying" + " FROM message" + " JOIN account ON account.id = message.account" + " JOIN folder ON folder.id = message.folder" + - " WHERE account.`synchronize`" + + " WHERE (:account IS NULL OR account.id = :account)" + + " AND account.`synchronize`" + " AND folder.notify" + - " AND NOT (message.ui_seen OR message.ui_hide)"; + " AND NOT (message.ui_seen OR message.ui_hide)" + + " GROUP BY account.id" + + " ORDER BY account.id") + LiveData> liveUnseenWidget(Long account); - @Query(widget) - LiveData liveUnseenWidget(); - - @Query(widget) - TupleMessageStats getUnseenWidget(); + @Query("SELECT :account AS account, COUNT(message.id) AS unseen, SUM(ABS(notifying)) AS notifying" + + " FROM message" + + " JOIN account ON account.id = message.account" + + " JOIN folder ON folder.id = message.folder" + + " WHERE (:account IS NULL OR account.id = :account)" + + " AND account.`synchronize`" + + " AND folder.notify" + + " AND NOT (message.ui_seen OR message.ui_hide)") + TupleMessageStats getUnseenWidget(Long account); @Query("SELECT message.*" + ", account.pop AS accountProtocol, account.name AS accountName, COALESCE(identity.color, folder.color, account.color) AS accountColor" + diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java index 1c987d3a3e..9b6f18b8fc 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java +++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java @@ -362,23 +362,46 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - db.message().liveUnseenWidget().observe(this, new Observer() { - private Integer lastUnseen = null; + db.message().liveUnseenWidget(null).observe(this, new Observer>() { + private List last = null; @Override - public void onChanged(TupleMessageStats stats) { + public void onChanged(List stats) { if (stats == null) - stats = new TupleMessageStats(); + stats = new ArrayList<>(); + boolean changed = false; + if (last == null || last.size() != stats.size()) + changed = true; + else + for (int i = 0; i < stats.size(); i++) + if (!last.get(i).equals(stats.get(i))) { + changed = true; + break; + } + + if (!changed) + return; + + Widget.update(ServiceSynchronize.this); + + boolean badge = prefs.getBoolean("badge", true); boolean unseen_ignored = prefs.getBoolean("unseen_ignored", false); - Integer unseen = (unseen_ignored ? stats.notifying : stats.unseen); - if (unseen == null) - unseen = 0; - if (lastUnseen == null || !lastUnseen.equals(unseen)) { - Log.i("Stats " + stats); - lastUnseen = unseen; - setUnseen(unseen); + int count = 0; + for (TupleMessageStats stat : stats) { + Integer unseen = (unseen_ignored ? stat.notifying : stat.unseen); + if (unseen != null) + count += unseen; + } + + try { + if (count == 0 || !badge) + ShortcutBadger.removeCount(ServiceSynchronize.this); + else + ShortcutBadger.applyCount(ServiceSynchronize.this, count); + } catch (Throwable ex) { + Log.e(ex); } } }); @@ -510,8 +533,6 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences liveAccountNetworkState.postDestroy(); - setUnseen(null); - try { stopForeground(true); } catch (Throwable ex) { @@ -687,22 +708,6 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences return builder; } - private void setUnseen(Integer unseen) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - boolean badge = prefs.getBoolean("badge", true); - - Widget.update(this, unseen); - - try { - if (unseen == null || !badge) - ShortcutBadger.removeCount(this); - else - ShortcutBadger.applyCount(this, unseen); - } catch (Throwable ex) { - Log.e(ex); - } - } - private void monitorAccount(final EntityAccount account, final Core.State state, final boolean sync) throws NoSuchProviderException { final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); final PowerManager.WakeLock wlAccount = pm.newWakeLock( diff --git a/app/src/main/java/eu/faircode/email/TupleMessageStats.java b/app/src/main/java/eu/faircode/email/TupleMessageStats.java index 7726a71672..1911791ddb 100644 --- a/app/src/main/java/eu/faircode/email/TupleMessageStats.java +++ b/app/src/main/java/eu/faircode/email/TupleMessageStats.java @@ -25,6 +25,7 @@ import androidx.annotation.Nullable; import java.util.Objects; public class TupleMessageStats { + public Long account; public Integer unseen; public Integer notifying; @@ -32,7 +33,8 @@ public class TupleMessageStats { public boolean equals(@Nullable Object obj) { if (obj instanceof TupleMessageStats) { TupleMessageStats other = (TupleMessageStats) obj; - return (Objects.equals(this.unseen, other.unseen) && + return (Objects.equals(this.account, other.account) && + Objects.equals(this.unseen, other.unseen) && Objects.equals(this.notifying, other.notifying)); } else return false; diff --git a/app/src/main/java/eu/faircode/email/Widget.java b/app/src/main/java/eu/faircode/email/Widget.java index 49b91fc679..d284e7b456 100644 --- a/app/src/main/java/eu/faircode/email/Widget.java +++ b/app/src/main/java/eu/faircode/email/Widget.java @@ -26,6 +26,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.text.TextUtils; import android.widget.RemoteViews; import androidx.preference.PreferenceManager; @@ -42,21 +43,49 @@ public class Widget extends AppWidgetProvider { executor.submit(new Runnable() { @Override public void run() { - DB db = DB.getInstance(context); - TupleMessageStats stats = db.message().getUnseenWidget(); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); boolean unseen_ignored = prefs.getBoolean("unseen_ignored", false); - Integer unseen = (unseen_ignored ? stats.notifying : stats.unseen); - if (unseen == null) - unseen = 0; - update(context, appWidgetManager, appWidgetIds, unseen); + DB db = DB.getInstance(context); + NumberFormat nf = NumberFormat.getIntegerInstance(); + + Intent view = new Intent(context, ActivityView.class); + view.setAction("unified"); + view.putExtra("refresh", true); + view.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + PendingIntent pi = PendingIntent.getActivity(context, ActivityView.REQUEST_UNIFIED, view, PendingIntent.FLAG_UPDATE_CURRENT); + + for (int appWidgetId : appWidgetIds) { + long account = prefs.getLong("widget." + appWidgetId + ".account", -1L); + String name = prefs.getString("widget." + appWidgetId + ".name", null); + + TupleMessageStats stats = db.message().getUnseenWidget(account < 0 ? null : account); + Integer unseen = (unseen_ignored ? stats.notifying : stats.unseen); + if (unseen == null) + unseen = 0; + + RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget); + + views.setOnClickPendingIntent(R.id.widget, pi); + + views.setTextViewText(R.id.tvCount, unseen < 100 ? nf.format(unseen) : "∞"); + + if (!TextUtils.isEmpty(name)) { + views.setTextViewText(R.id.tvAccount, name); + views.setViewVisibility(R.id.tvAccount, ViewStripe.VISIBLE); + } + + appWidgetManager.updateAppWidget(appWidgetId, views); + } } }); } - static void update(Context context, Integer count) { + static void init(Context context, int appWidgetId) { + update(context); + } + + static void update(Context context) { AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); if (appWidgetManager == null) { Log.w("No app widget manager"); // Fairphone FP2 @@ -64,31 +93,10 @@ public class Widget extends AppWidgetProvider { } int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, Widget.class)); - update(context, appWidgetManager, appWidgetIds, count); - } - private static void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds, Integer count) { - NumberFormat nf = NumberFormat.getIntegerInstance(); - - Intent view = new Intent(context, ActivityView.class); - view.setAction("unified"); - view.putExtra("refresh", true); - view.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - PendingIntent pi = PendingIntent.getActivity(context, ActivityView.REQUEST_UNIFIED, view, PendingIntent.FLAG_UPDATE_CURRENT); - - for (int appWidgetId : appWidgetIds) { - RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget); - - views.setOnClickPendingIntent(R.id.widget, pi); - - if (count == null) - views.setTextViewText(R.id.tvCount, "?"); - else if (count > 99) - views.setTextViewText(R.id.tvCount, "∞"); - else - views.setTextViewText(R.id.tvCount, nf.format(count)); - - appWidgetManager.updateAppWidget(appWidgetId, views); - } + Intent intent = new Intent(context, Widget.class); + intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); + context.sendBroadcast(intent); } } diff --git a/app/src/main/res/layout/activity_widget.xml b/app/src/main/res/layout/activity_widget.xml new file mode 100644 index 0000000000..fec0c83ab0 --- /dev/null +++ b/app/src/main/res/layout/activity_widget.xml @@ -0,0 +1,50 @@ + + + + + + + +