diff options
author | Christian Schneppe <christian@pix-art.de> | 2018-09-12 22:32:47 +0200 |
---|---|---|
committer | Christian Schneppe <christian@pix-art.de> | 2018-09-13 17:29:36 +0200 |
commit | 46b77bf34219aa47e916f0ab980d3bd5bb05ba34 (patch) | |
tree | 0a73271b9cdea5577b7d69dbdbf525f24bcc31b7 /src/main/java | |
parent | e50ba72e8ae024e64b1e2b2838f73faf64330adb (diff) |
initial work toward api 26+
* introduce notification channels
* always use foreground service on 26+
Diffstat (limited to 'src/main/java')
12 files changed, 332 insertions, 171 deletions
diff --git a/src/main/java/de/pixart/messenger/services/BarcodeProvider.java b/src/main/java/de/pixart/messenger/services/BarcodeProvider.java index 67dc74cc6..5ac2ee016 100644 --- a/src/main/java/de/pixart/messenger/services/BarcodeProvider.java +++ b/src/main/java/de/pixart/messenger/services/BarcodeProvider.java @@ -162,7 +162,6 @@ public class BarcodeProvider extends ContentProvider implements ServiceConnectio synchronized (this) { if (mXmppConnectionService == null && !mBindingInProcess) { Log.d(Config.LOGTAG, "calling to bind service"); - context.startService(intent); context.bindService(intent, this, Context.BIND_AUTO_CREATE); this.mBindingInProcess = true; } diff --git a/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java b/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java index 5d6469bae..d20f9c1f3 100644 --- a/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java +++ b/src/main/java/de/pixart/messenger/services/ContactChooserTargetService.java @@ -32,7 +32,6 @@ public class ContactChooserTargetService extends ChooserTargetService implements public List<ChooserTarget> onGetChooserTargets(ComponentName targetActivityName, IntentFilter matchedFilter) { Intent intent = new Intent(this, XmppConnectionService.class); intent.setAction("contact_chooser"); - startService(intent); bindService(intent, this, Context.BIND_AUTO_CREATE); ArrayList<ChooserTarget> chooserTargets = new ArrayList<>(); try { diff --git a/src/main/java/de/pixart/messenger/services/EventReceiver.java b/src/main/java/de/pixart/messenger/services/EventReceiver.java index 6200a40ce..529741ead 100644 --- a/src/main/java/de/pixart/messenger/services/EventReceiver.java +++ b/src/main/java/de/pixart/messenger/services/EventReceiver.java @@ -4,6 +4,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.preference.PreferenceManager; +import android.support.v4.content.ContextCompat; import android.util.Log; import de.pixart.messenger.Config; @@ -13,26 +14,26 @@ public class EventReceiver extends BroadcastReceiver { public static final String SETTING_ENABLED_ACCOUNTS = "enabled_accounts"; @Override - public void onReceive(Context context, Intent intent) { - Intent mIntentForService = new Intent(context, XmppConnectionService.class); - if (intent.getAction() != null) { - mIntentForService.setAction(intent.getAction()); + public void onReceive(final Context context, final Intent originalIntent) { + final Intent intentForService = new Intent(context, XmppConnectionService.class); + if (originalIntent.getAction() != null) { + intentForService.setAction(originalIntent.getAction()); } else { - mIntentForService.setAction("other"); + intentForService.setAction("other"); } - final String action = intent.getAction(); + final String action = originalIntent.getAction(); if (action.equals("ui") || hasEnabledAccounts(context)) { try { - context.startService(mIntentForService); + ContextCompat.startForegroundService(context, intentForService); } catch (RuntimeException e) { Log.d(Config.LOGTAG, "EventReceiver was unable to start service"); } } else { - Log.d(Config.LOGTAG, "EventReceiver ignored action " + mIntentForService.getAction()); + Log.d(Config.LOGTAG, "EventReceiver ignored action " + intentForService.getAction()); } } - public static boolean hasEnabledAccounts(Context context) { + public static boolean hasEnabledAccounts(final Context context) { return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SETTING_ENABLED_ACCOUNTS, true); } diff --git a/src/main/java/de/pixart/messenger/services/ExportLogsService.java b/src/main/java/de/pixart/messenger/services/ExportLogsService.java index b485fb91d..c2704490d 100644 --- a/src/main/java/de/pixart/messenger/services/ExportLogsService.java +++ b/src/main/java/de/pixart/messenger/services/ExportLogsService.java @@ -1,7 +1,5 @@ package de.pixart.messenger.services; -import android.app.NotificationManager; -import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -10,7 +8,6 @@ import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.preference.PreferenceManager; import android.support.annotation.BoolRes; -import android.support.v4.app.NotificationCompat; import android.util.Log; import java.io.BufferedWriter; @@ -41,19 +38,17 @@ import rocks.xmpp.addr.Jid; import static de.pixart.messenger.ui.SettingsActivity.USE_MULTI_ACCOUNTS; -public class ExportLogsService extends Service { +public class ExportLogsService extends XmppConnectionService { private static final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); private static final String DIRECTORY_STRING_FORMAT = FileBackend.getConversationsDirectory("Chats", false) + "%s"; private static final String MESSAGE_STRING_FORMAT = "(%s) %s: %s\n"; - private static final int NOTIFICATION_ID = 1; private static AtomicBoolean running = new AtomicBoolean(false); + boolean ReadableLogsEnabled = false; private DatabaseBackend mDatabaseBackend; private List<Account> mAccounts; - boolean ReadableLogsEnabled = false; private WakeLock wakeLock; private PowerManager pm; - XmppConnectionService mXmppConnectionService; @Override public void onCreate() { @@ -63,20 +58,19 @@ public class ExportLogsService extends Service { ReadableLogsEnabled = ReadableLogs.getBoolean("export_plain_text_logs", getResources().getBoolean(R.bool.plain_text_logs)); pm = (PowerManager) getSystemService(Context.POWER_SERVICE); wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ExportLogsService"); + this.startForeground(NotificationService.FOREGROUND_NOTIFICATION_ID, getNotificationService().exportLogsNotification()); } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (running.compareAndSet(false, true)) { - new Thread(new Runnable() { - @Override - public void run() { - export(); - stopForeground(true); - WakeLockHelper.release(wakeLock); - running.set(false); - stopSelf(); - } + new Thread(() -> { + startForcingForegroundNotification(); + export(); + stopForcingForegroundNotification(); + WakeLockHelper.release(wakeLock); + running.set(false); + stopSelf(); }).start(); } return START_NOT_STICKY; @@ -84,16 +78,9 @@ public class ExportLogsService extends Service { private void export() { wakeLock.acquire(); + getNotificationService().exportLogsServiceNotification(getNotificationService().exportLogsNotification()); List<Conversation> conversations = mDatabaseBackend.getConversations(Conversation.STATUS_AVAILABLE); conversations.addAll(mDatabaseBackend.getConversations(Conversation.STATUS_ARCHIVED)); - NotificationManager mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getBaseContext()); - mBuilder.setContentTitle(getString(R.string.app_name)) - .setContentText(getString(R.string.notification_export_logs_title)) - .setSmallIcon(R.drawable.ic_import_export_white_24dp) - .setProgress(0, 0, true); - startForeground(NOTIFICATION_ID, mBuilder.build()); - mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build()); if (ReadableLogsEnabled) { for (Conversation conversation : conversations) { writeToFile(conversation); diff --git a/src/main/java/de/pixart/messenger/services/NotificationService.java b/src/main/java/de/pixart/messenger/services/NotificationService.java index 7fa091478..4cdde8390 100644 --- a/src/main/java/de/pixart/messenger/services/NotificationService.java +++ b/src/main/java/de/pixart/messenger/services/NotificationService.java @@ -1,16 +1,23 @@ package de.pixart.messenger.services; import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationChannelGroup; +import android.app.NotificationManager; import android.app.PendingIntent; +import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Typeface; +import android.media.AudioAttributes; +import android.media.RingtoneManager; import android.net.Uri; import android.os.Build; import android.os.SystemClock; import android.preference.PreferenceManager; +import android.support.annotation.RequiresApi; import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat.BigPictureStyle; import android.support.v4.app.NotificationCompat.Builder; @@ -46,9 +53,9 @@ import de.pixart.messenger.entities.Conversational; import de.pixart.messenger.entities.Message; import de.pixart.messenger.persistance.FileBackend; import de.pixart.messenger.ui.ConversationsActivity; -import de.pixart.messenger.ui.EditAccountActivity; import de.pixart.messenger.ui.ManageAccountActivity; import de.pixart.messenger.ui.TimePreference; +import de.pixart.messenger.utils.Compatibility; import de.pixart.messenger.utils.GeoHelper; import de.pixart.messenger.utils.UIHelper; import de.pixart.messenger.xmpp.XmppConnection; @@ -56,39 +63,108 @@ import de.pixart.messenger.xmpp.XmppConnection; public class NotificationService { public static final Object CATCHUP_LOCK = new Object(); + private static final String CONVERSATIONS_GROUP = "de.pixart.messenger"; - private final XmppConnectionService mXmppConnectionService; - private final LinkedHashMap<String, ArrayList<Message>> notifications = new LinkedHashMap<>(); private static final int NOTIFICATION_ID_MULTIPLIER = 1024 * 1024; public static final int NOTIFICATION_ID = 2 * NOTIFICATION_ID_MULTIPLIER; public static final int FOREGROUND_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 4; public static final int ERROR_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 6; - public static final int UPDATE_NOTIFICATION_ID = NOTIFICATION_ID_MULTIPLIER * 8; + public static final String MESSAGES_CHANNEL_ID = "messages"; + public static final String FOREGROUND_CHANNEL_ID = "foreground"; + public static final String ERROR_CHANNEL_ID = "error"; + + private final XmppConnectionService mXmppConnectionService; + private final LinkedHashMap<String, ArrayList<Message>> notifications = new LinkedHashMap<>(); + private final HashMap<Conversation, AtomicInteger> mBacklogMessageCounter = new HashMap<>(); private Conversation mOpenConversation; private boolean mIsInForeground; private long mLastNotification; - private final HashMap<Conversation, AtomicInteger> mBacklogMessageCounter = new HashMap<>(); - - public NotificationService(final XmppConnectionService service) { + NotificationService(final XmppConnectionService service) { this.mXmppConnectionService = service; } + private static boolean displaySnoozeAction(List<Message> messages) { + int numberOfMessagesWithoutReply = 0; + for (Message message : messages) { + if (message.getStatus() == Message.STATUS_RECEIVED) { + ++numberOfMessagesWithoutReply; + } else { + return false; + } + } + return numberOfMessagesWithoutReply >= 3; + } + public static Pattern generateNickHighlightPattern(final String nick) { return Pattern.compile("(?<=(^|\\s))" + Pattern.quote(nick) + "\\b"); } + @RequiresApi(api = Build.VERSION_CODES.O) + public void initializeChannels() { + final Context c = mXmppConnectionService; + NotificationManager notificationManager = c.getSystemService(NotificationManager.class); + if (notificationManager == null) { + return; + } + notificationManager.createNotificationChannelGroup(new NotificationChannelGroup("status", c.getString(R.string.notification_group_status_information))); + notificationManager.createNotificationChannelGroup(new NotificationChannelGroup("chats", c.getString(R.string.notification_group_messages))); + + final NotificationChannel foregroundServiceChannel = new NotificationChannel(FOREGROUND_CHANNEL_ID, + c.getString(R.string.foreground_service_channel_name), + NotificationManager.IMPORTANCE_MIN); + foregroundServiceChannel.setDescription(c.getString(R.string.foreground_service_channel_description)); + foregroundServiceChannel.setShowBadge(false); + foregroundServiceChannel.setGroup("status"); + notificationManager.createNotificationChannel(foregroundServiceChannel); + + final NotificationChannel errorChannel = new NotificationChannel(ERROR_CHANNEL_ID, + c.getString(R.string.error_channel_name), + NotificationManager.IMPORTANCE_LOW); + errorChannel.setDescription(c.getString(R.string.error_channel_description)); + errorChannel.setShowBadge(false); + errorChannel.setGroup("status"); + notificationManager.createNotificationChannel(errorChannel); + + final NotificationChannel messagesChannel = new NotificationChannel(MESSAGES_CHANNEL_ID, + c.getString(R.string.messages_channel_name), + NotificationManager.IMPORTANCE_HIGH); + messagesChannel.setShowBadge(true); + messagesChannel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) + .build()); + messagesChannel.setLightColor(0xff00ff00); + final int dat = 70; + final long[] pattern = {0, 3 * dat, dat, dat}; + messagesChannel.setVibrationPattern(pattern); + messagesChannel.enableVibration(true); + messagesChannel.enableLights(true); + messagesChannel.setGroup("chats"); + notificationManager.createNotificationChannel(messagesChannel); + + final NotificationChannel silentMessagesChannel = new NotificationChannel("silent_messages", + c.getString(R.string.silent_messages_channel_name), + NotificationManager.IMPORTANCE_LOW); + silentMessagesChannel.setDescription(c.getString(R.string.silent_messages_channel_description)); + silentMessagesChannel.setShowBadge(true); + silentMessagesChannel.setLightColor(0xff00ff00); + silentMessagesChannel.enableLights(true); + silentMessagesChannel.setGroup("chats"); + notificationManager.createNotificationChannel(silentMessagesChannel); + } + public boolean notify(final Message message) { final Conversation conversation = (Conversation) message.getConversation(); return message.getStatus() == Message.STATUS_RECEIVED && notificationsEnabled() && !conversation.isMuted() && (conversation.alwaysNotify() || wasHighlightedOrPrivate(message)) - && (!conversation.isWithStranger() || notificationsFromStrangers() - ); + && (!conversation.isWithStranger() || notificationsFromStrangers()) + ; } - public boolean notificationsEnabled() { + private boolean notificationsEnabled() { return mXmppConnectionService.getBooleanPreference("show_notification", R.bool.show_notification); } @@ -96,7 +172,7 @@ public class NotificationService { return mXmppConnectionService.getBooleanPreference("notifications_from_strangers", R.bool.notifications_from_strangers); } - public boolean isQuietHours() { + private boolean isQuietHours() { if (!mXmppConnectionService.getBooleanPreference("enable_quiet_hours", R.bool.enable_quiet_hours)) { return false; } @@ -104,7 +180,6 @@ public class NotificationService { final long startTime = preferences.getLong("quiet_hours_start", TimePreference.DEFAULT_VALUE) % Config.MILLISECONDS_IN_DAY; final long endTime = preferences.getLong("quiet_hours_end", TimePreference.DEFAULT_VALUE) % Config.MILLISECONDS_IN_DAY; final long nowTime = Calendar.getInstance().getTimeInMillis() % Config.MILLISECONDS_IN_DAY; - if (endTime < startTime) { return nowTime > startTime || nowTime < endTime; } else { @@ -159,7 +234,7 @@ public class NotificationService { } } } - Log.d(Config.LOGTAG,account.getJid().asBareJid()+": backlog message count="+count); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": backlog message count=" + count); return count; } @@ -260,9 +335,8 @@ public class NotificationService { updateNotification(notify, false); } - public void updateNotification(final boolean notify, boolean summaryOnly) { + private void updateNotification(final boolean notify, boolean summaryOnly) { final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mXmppConnectionService); - if (notifications.size() == 0) { cancel(NOTIFICATION_ID); } else { @@ -271,15 +345,16 @@ public class NotificationService { } final Builder mBuilder; if (notifications.size() == 1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { - mBuilder = buildSingleConversations(notifications.values().iterator().next()); + mBuilder = buildSingleConversations(notifications.values().iterator().next(), notify); modifyForSoundVibrationAndLight(mBuilder, notify, preferences); notify(NOTIFICATION_ID, mBuilder.build()); } else { - mBuilder = buildMultipleConversation(); + mBuilder = buildMultipleConversation(notify); + mBuilder.setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_CHILDREN); modifyForSoundVibrationAndLight(mBuilder, notify, preferences); if (!summaryOnly) { for (Map.Entry<String, ArrayList<Message>> entry : notifications.entrySet()) { - Builder singleBuilder = buildSingleConversations(entry.getValue()); + Builder singleBuilder = buildSingleConversations(entry.getValue(), notify); singleBuilder.setGroup(CONVERSATIONS_GROUP); setNotificationColor(singleBuilder); notify(entry.getKey(), NOTIFICATION_ID, singleBuilder.build()); @@ -330,9 +405,8 @@ public class NotificationService { } } - private Builder buildMultipleConversation() { - final Builder mBuilder = new NotificationCompat.Builder( - mXmppConnectionService); + private Builder buildMultipleConversation(final boolean notify) { + final Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService, notify ? "messages" : "silent_messages"); final NotificationCompat.InboxStyle style = new NotificationCompat.InboxStyle(); style.setBigContentTitle(notifications.size() + " " @@ -378,8 +452,8 @@ public class NotificationService { return mBuilder; } - private Builder buildSingleConversations(final ArrayList<Message> messages) { - final Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService); + private Builder buildSingleConversations(final ArrayList<Message> messages, final boolean notify) { + final Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService, notify ? "messages" : "silent_messages"); if (messages.size() >= 1) { final Conversation conversation = (Conversation) messages.get(0).getConversation(); final UnreadConversation.Builder mUnreadBuilder = new UnreadConversation.Builder(conversation.getName().toString()); @@ -407,8 +481,7 @@ public class NotificationService { R.drawable.ic_reply_white_24dp, replyLabel, createReplyIntent(conversation, false)).addRemoteInput(remoteInput).build(); - NotificationCompat.Action wearReplyAction = new NotificationCompat.Action.Builder( - R.drawable.ic_wear_reply, + NotificationCompat.Action wearReplyAction = new NotificationCompat.Action.Builder(R.drawable.ic_wear_reply, replyLabel, createReplyIntent(conversation, true)).addRemoteInput(remoteInput).build(); mBuilder.extend(new NotificationCompat.WearableExtender().addAction(wearReplyAction)); @@ -473,18 +546,6 @@ public class NotificationService { return mBuilder; } - private static boolean displaySnoozeAction(List<Message> messages) { - int numberOfMessagesWithoutReply = 0; - for (Message message : messages) { - if (message.getStatus() == Message.STATUS_RECEIVED) { - ++numberOfMessagesWithoutReply; - } else { - return false; - } - } - return numberOfMessagesWithoutReply >= 3; - } - private void modifyForImage(final Builder builder, final UnreadConversation.Builder uBuilder, final Message message, final ArrayList<Message> messages) { try { @@ -545,7 +606,7 @@ public class NotificationService { styledString.setSpan(new StyleSpan(Typeface.BOLD), 0, name.length(), 0); builder.setContentText(styledString); } else { - builder.setContentText(mXmppConnectionService.getResources().getQuantityString(R.plurals.x_messages,count,count)); + builder.setContentText(mXmppConnectionService.getResources().getQuantityString(R.plurals.x_messages, count, count)); } } } @@ -596,7 +657,7 @@ public class NotificationService { private CharSequence getMergedBodies(final ArrayList<Message> messages) { final StringBuilder text = new StringBuilder(); - for(Message message : messages) { + for (Message message : messages) { if (text.length() != 0) { text.append("\n"); } @@ -606,7 +667,7 @@ public class NotificationService { } private PendingIntent createShowLocationIntent(final Message message) { - Iterable<Intent> intents = GeoHelper.createGeoIntentsFromMessage(message, this.mXmppConnectionService.getApplicationContext()); + Iterable<Intent> intents = GeoHelper.createGeoIntentsFromMessage(message, mXmppConnectionService); for (Intent intent : intents) { if (intent.resolveActivity(mXmppConnectionService.getPackageManager()) != null) { return PendingIntent.getActivity(mXmppConnectionService, generateRequestCode(message.getConversation(), 18), intent, PendingIntent.FLAG_UPDATE_CURRENT); @@ -676,7 +737,7 @@ public class NotificationService { return PendingIntent.getService(mXmppConnectionService, generateRequestCode(conversation, 16), intent, PendingIntent.FLAG_UPDATE_CURRENT); } - public PendingIntent createSnoozeIntent(Conversation conversation) { + private PendingIntent createSnoozeIntent(Conversation conversation) { final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class); intent.setAction(XmppConnectionService.ACTION_SNOOZE); intent.putExtra("uuid", conversation.getUuid()); @@ -736,52 +797,61 @@ public class NotificationService { } public Notification createForegroundNotification() { - final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService); - List<Account> accounts = mXmppConnectionService.getAccounts(); - String status; - Account mAccount = null; - Log.d(Config.LOGTAG, "Accounts size " + accounts.size()); - if (accounts.size() == 1) { - mAccount = accounts.get(0); - if (mAccount.getStatus() == Account.State.ONLINE) { - status = "(" + mXmppConnectionService.getString(R.string.account_status_online) + ")"; - } else if (mAccount.getStatus() == Account.State.CONNECTING) { - status = "(" + mXmppConnectionService.getString(R.string.account_status_connecting) + ")"; - } else { - status = "(" + mXmppConnectionService.getString(R.string.account_status_offline) + ")"; - } - } else if (accounts.size() > 1) { - status = ""; // todo: status for multiple accounts??? - } else { - status = "(" + mXmppConnectionService.getString(R.string.account_status_offline) + ")"; - } - status = " " + status; - Log.d(Config.LOGTAG, "Status: " + status); - mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service) + status); - if (Config.SHOW_CONNECTED_ACCOUNTS) { + final Notification.Builder mBuilder = new Notification.Builder(mXmppConnectionService); + mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service)); + if (Compatibility.twentySix() || Config.SHOW_CONNECTED_ACCOUNTS) { + List<Account> accounts = mXmppConnectionService.getAccounts(); int enabled = 0; int connected = 0; - for (Account account : accounts) { - if (account.isOnlineAndConnected()) { - connected++; - enabled++; - } else if (account.isEnabled()) { - enabled++; + String status; + Account mAccount = null; + if (accounts != null) { + for (Account account : accounts) { + if (account.isOnlineAndConnected()) { + connected++; + enabled++; + } else if (account.isEnabled()) { + enabled++; + } + } + if (accounts.size() == 1) { + mAccount = accounts.get(0); + if (mAccount.getStatus() == Account.State.ONLINE) { + status = "(" + mXmppConnectionService.getString(R.string.account_status_online) + ")"; + status = " " + status; + Log.d(Config.LOGTAG, "Status: " + status); + mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service) + status); + } else if (mAccount.getStatus() == Account.State.CONNECTING) { + status = "(" + mXmppConnectionService.getString(R.string.account_status_connecting) + ")"; + status = " " + status; + Log.d(Config.LOGTAG, "Status: " + status); + mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service) + status); + } else { + status = "(" + mXmppConnectionService.getString(R.string.account_status_offline) + ")"; + status = " " + status; + Log.d(Config.LOGTAG, "Status: " + status); + mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service) + status); + } + } else if (accounts.size() > 1) { + mBuilder.setContentText(mXmppConnectionService.getString(R.string.connected_accounts, connected, enabled)); + } else { + status = "(" + mXmppConnectionService.getString(R.string.account_status_offline) + ")"; + status = " " + status; + Log.d(Config.LOGTAG, "Status: " + status); + mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.conversations_foreground_service) + status); } + } else { + mBuilder.setContentText(mXmppConnectionService.getString(R.string.touch_to_open_conversations)); } - mBuilder.setContentText(mXmppConnectionService.getString(R.string.connected_accounts, connected, enabled)); } else { mBuilder.setContentText(mXmppConnectionService.getString(R.string.touch_to_open_conversations)); } mBuilder.setContentIntent(createOpenConversationsIntent()); mBuilder.setWhen(0); - mBuilder.setPriority(Config.SHOW_CONNECTED_ACCOUNTS ? NotificationCompat.PRIORITY_DEFAULT : NotificationCompat.PRIORITY_MIN); - if (accounts.size() == 1 && accounts.get(0).getStatus() == Account.State.ONLINE) { - mBuilder.setSmallIcon(R.drawable.ic_link_white_24dp); - } else if (accounts.size() > 1) { - mBuilder.setSmallIcon(R.drawable.ic_link_white_24dp); // todo: status for multiple accounts??? - } else { - mBuilder.setSmallIcon(R.drawable.ic_unlink_white_24dp); + mBuilder.setPriority(Notification.PRIORITY_LOW); + mBuilder.setSmallIcon(R.drawable.ic_link_white_24dp); + if (Compatibility.twentySix()) { + mBuilder.setChannelId(FOREGROUND_CHANNEL_ID); } return mBuilder.build(); } @@ -801,10 +871,10 @@ public class NotificationService { errors.add(account); } } - if (mXmppConnectionService.showForegroundService()) { + if (Compatibility.keepForegroundService(mXmppConnectionService)) { notify(FOREGROUND_NOTIFICATION_ID, createForegroundNotification()); } - final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService); + final Notification.Builder mBuilder = new Notification.Builder(mXmppConnectionService); if (errors.size() == 0) { cancel(ERROR_NOTIFICATION_ID); return; @@ -819,38 +889,70 @@ public class NotificationService { mXmppConnectionService.getString(R.string.try_again), createTryAgainIntent()); mBuilder.setDeleteIntent(createDismissErrorIntent()); - mBuilder.setVisibility(NotificationCompat.VISIBILITY_PRIVATE); - mBuilder.setSmallIcon(R.drawable.ic_warning_white_24dp); - mBuilder.setLocalOnly(true); - mBuilder.setPriority(NotificationCompat.PRIORITY_LOW); - if (errors.size() == 1) { - Intent intent = new Intent(mXmppConnectionService, EditAccountActivity.class); - Account mAccount = mXmppConnectionService.getAccounts().get(0); - intent.putExtra("jid", mAccount.getJid().asBareJid().toString()); - intent.putExtra("init", false); - mBuilder.setContentIntent(PendingIntent.getActivity(mXmppConnectionService, - 145, - intent, - PendingIntent.FLAG_UPDATE_CURRENT)); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + mBuilder.setVisibility(NotificationCompat.VISIBILITY_PRIVATE); + mBuilder.setSmallIcon(R.drawable.ic_warning_white_24dp); } else { - mBuilder.setContentIntent(PendingIntent.getActivity(mXmppConnectionService, - 145, - new Intent(mXmppConnectionService, ManageAccountActivity.class), - PendingIntent.FLAG_UPDATE_CURRENT)); + mBuilder.setSmallIcon(R.drawable.ic_warning_white_24dp); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { + mBuilder.setLocalOnly(true); + } + mBuilder.setPriority(Notification.PRIORITY_LOW); + mBuilder.setContentIntent(PendingIntent.getActivity(mXmppConnectionService, + 145, + new Intent(mXmppConnectionService, ManageAccountActivity.class), + PendingIntent.FLAG_UPDATE_CURRENT)); + if (Compatibility.twentySix()) { + mBuilder.setChannelId(ERROR_CHANNEL_ID); } notify(ERROR_NOTIFICATION_ID, mBuilder.build()); } - public Notification updateFileAddingNotification(int current, Message message) { - final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(mXmppConnectionService); - NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mXmppConnectionService); + public void updateFileAddingNotification(int current, Message message) { + Notification.Builder mBuilder = new Notification.Builder(mXmppConnectionService); mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.transcoding_video)); mBuilder.setProgress(100, current, false); mBuilder.setSmallIcon(R.drawable.ic_hourglass_empty_white_24dp); mBuilder.setContentIntent(createContentIntent(message.getConversation())); + if (Compatibility.twentySix()) { + mBuilder.setChannelId(FOREGROUND_CHANNEL_ID); + } Notification notification = mBuilder.build(); notify(FOREGROUND_NOTIFICATION_ID, notification); - return notification; + } + + public Notification exportLogsNotification() { + Notification.Builder mBuilder = new Notification.Builder(mXmppConnectionService); + mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.app_name)); + mBuilder.setContentText(mXmppConnectionService.getString(R.string.notification_export_logs_title)); + mBuilder.setProgress(0, 0, true); + mBuilder.setSmallIcon(R.drawable.ic_import_export_white_24dp); + if (Compatibility.twentySix()) { + mBuilder.setChannelId(FOREGROUND_CHANNEL_ID); + } + return mBuilder.build(); + } + + public void exportLogsServiceNotification(Notification notification) { + notify(FOREGROUND_NOTIFICATION_ID, notification); + } + + public Notification AppUpdateNotification(PendingIntent intent, String version, String filesize) { + Notification.Builder mBuilder = new Notification.Builder(mXmppConnectionService); + mBuilder.setContentTitle(mXmppConnectionService.getString(R.string.app_name)); + mBuilder.setContentText(mXmppConnectionService.getString(R.string.notification_export_logs_title)); + mBuilder.setContentText(String.format(mXmppConnectionService.getString(R.string.update_available), version, filesize)); + mBuilder.setSmallIcon(R.drawable.ic_update_notification); + mBuilder.setContentIntent(intent); + if (Compatibility.twentySix()) { + mBuilder.setChannelId(FOREGROUND_CHANNEL_ID); + } + return mBuilder.build(); + } + + public void AppUpdateServiceNotification(Notification notification) { + notify(FOREGROUND_NOTIFICATION_ID, notification); } private void notify(String tag, int id, Notification notification) { diff --git a/src/main/java/de/pixart/messenger/services/UpdateService.java b/src/main/java/de/pixart/messenger/services/UpdateService.java index 3b3829290..bf5242939 100644 --- a/src/main/java/de/pixart/messenger/services/UpdateService.java +++ b/src/main/java/de/pixart/messenger/services/UpdateService.java @@ -1,14 +1,11 @@ package de.pixart.messenger.services; -import android.app.Notification; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.AsyncTask; import android.os.Handler; import android.os.Looper; -import android.support.v4.app.NotificationCompat; -import android.support.v4.app.NotificationManagerCompat; import android.util.Log; import android.widget.Toast; @@ -27,12 +24,12 @@ import de.pixart.messenger.R; import de.pixart.messenger.ui.UpdaterActivity; import static de.pixart.messenger.http.HttpConnectionManager.getProxy; -import static de.pixart.messenger.services.NotificationService.UPDATE_NOTIFICATION_ID; public class UpdateService extends AsyncTask<String, Object, UpdateService.Wrapper> { private boolean mUseTor; private Context context; private String store; + private NotificationService getNotificationService; public UpdateService() { } @@ -40,6 +37,7 @@ public class UpdateService extends AsyncTask<String, Object, UpdateService.Wrapp this.context = context; this.store = Store; this.mUseTor = mXmppConnectionService.useTorToConnect(); + this.getNotificationService = mXmppConnectionService.getNotificationService(); } @Override @@ -145,14 +143,7 @@ public class UpdateService extends AsyncTask<String, Object, UpdateService.Wrapp intent.putExtra("changelog", changelog); intent.putExtra("store", store); PendingIntent pi = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); - final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context); - NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context); - mBuilder.setContentTitle(context.getString(R.string.update_service)); - mBuilder.setContentText(String.format(context.getString(R.string.update_available), version, filesize)); - mBuilder.setSmallIcon(R.drawable.ic_update_notification); - mBuilder.setContentIntent(pi); - Notification notification = mBuilder.build(); - notificationManager.notify(UPDATE_NOTIFICATION_ID, notification); + getNotificationService.AppUpdateServiceNotification(getNotificationService.AppUpdateNotification(pi, version, filesize)); } private int checkVersion(String remoteVersion, String installedVersion) { diff --git a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java index 896061457..af94d017b 100644 --- a/src/main/java/de/pixart/messenger/services/XmppConnectionService.java +++ b/src/main/java/de/pixart/messenger/services/XmppConnectionService.java @@ -1,5 +1,6 @@ package de.pixart.messenger.services; +import android.Manifest; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.AlarmManager; @@ -114,6 +115,7 @@ import de.pixart.messenger.ui.SettingsActivity; import de.pixart.messenger.ui.UiCallback; import de.pixart.messenger.ui.interfaces.OnAvatarPublication; import de.pixart.messenger.ui.interfaces.OnSearchResultsAvailable; +import de.pixart.messenger.utils.Compatibility; import de.pixart.messenger.utils.ConversationsFileObserver; import de.pixart.messenger.utils.CryptoHelper; import de.pixart.messenger.utils.ExceptionHelper; @@ -170,8 +172,6 @@ public class XmppConnectionService extends Service { public static final String ACTION_FCM_MESSAGE_RECEIVED = "fcm_message_received"; public static final String FDroid = "org.fdroid.fdroid"; public static final String PlayStore = "com.android.vending"; - public static final String Yalp = "com.github.yeriomin.yalpstore"; - private static final String ACTION_MERGE_PHONE_CONTACTS = "merge_phone_contacts"; private static final String SETTING_LAST_ACTIVITY_TS = "last_activity_timestamp"; static { @@ -234,6 +234,7 @@ public class XmppConnectionService extends Service { ) { @Override public void onEvent(int event, String path) { + Log.d(Config.LOGTAG, "event " + event + " path=" + path); markFileDeleted(path); } }; @@ -244,17 +245,16 @@ public class XmppConnectionService extends Service { @Override public void onChange(boolean selfChange) { super.onChange(selfChange); - Intent intent = new Intent(getApplicationContext(), - XmppConnectionService.class); - intent.setAction(ACTION_MERGE_PHONE_CONTACTS); - startService(intent); + if (restoredFromDatabaseLatch.getCount() == 0) { + loadPhoneContacts(); + } } }; private MemorizingTrustManager mMemorizingTrustManager; private NotificationService mNotificationService = new NotificationService(this); private ShortcutService mShortcutService = new ShortcutService(this); private AtomicBoolean mInitialAddressbookSyncCompleted = new AtomicBoolean(false); - private AtomicBoolean mForceForegroundService = new AtomicBoolean(false); + protected AtomicBoolean mForceForegroundService = new AtomicBoolean(false); private OnMessagePacketReceived mMessageParser = new MessageParser(this); private OnPresencePacketReceived mPresenceParser = new PresenceParser(this); private IqParser mIqParser = new IqParser(this); @@ -590,11 +590,6 @@ public class XmppConnectionService extends Service { resetAllAttemptCounts(true, false); } break; - case ACTION_MERGE_PHONE_CONTACTS: - if (restoredFromDatabaseLatch.getCount() == 0) { - loadPhoneContacts(); - } - return START_STICKY; case Intent.ACTION_SHUTDOWN: logoutAndSave(true); return START_NOT_STICKY; @@ -1094,6 +1089,9 @@ public class XmppConnectionService extends Service { Resolver.init(this); this.mRandom = new SecureRandom(); updateMemorizingTrustmanager(); + if (Compatibility.twentySix()) { + mNotificationService.initializeChannels(); + } final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); final int cacheSize = maxMemory / 8; this.mBitmapCache = new LruCache<String, Bitmap>(cacheSize) { @@ -1120,7 +1118,10 @@ public class XmppConnectionService extends Service { editor.apply(); restoreFromDatabase(); - getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, contactObserver); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) { + //TODO get this restarted if users gives permission + getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, contactObserver); + } if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || ContextCompat.checkSelfPermission(this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { Log.d(Config.LOGTAG, "starting file observer"); new Thread(fileObserver::startWatching).start(); @@ -1203,7 +1204,7 @@ public class XmppConnectionService extends Service { } public void toggleForegroundService() { - if (mForceForegroundService.get() || (showForegroundService())) { + if (mForceForegroundService.get() || (Compatibility.keepForegroundService(this) && hasEnabledAccounts())) { startForeground(NotificationService.FOREGROUND_NOTIFICATION_ID, this.mNotificationService.createForegroundNotification()); Log.d(Config.LOGTAG, "started foreground service"); } else { @@ -1215,7 +1216,8 @@ public class XmppConnectionService extends Service { @Override public void onTaskRemoved(final Intent rootIntent) { super.onTaskRemoved(rootIntent); - if (showForegroundService() || mForceForegroundService.get()) { + //TODO check for accounts enabled + if ((Compatibility.keepForegroundService(this) && hasEnabledAccounts()) || mForceForegroundService.get()) { Log.d(Config.LOGTAG, "ignoring onTaskRemoved because foreground service is activated"); } else { this.logoutAndSave(false); @@ -2195,6 +2197,7 @@ public class XmppConnectionService extends Service { updateAccountUi(); getNotificationService().updateErrorNotification(); syncEnabledAccountSetting(); + toggleForegroundService(); } } @@ -2637,6 +2640,9 @@ public class XmppConnectionService extends Service { } private boolean hasEnabledAccounts() { + if (this.accounts == null) { + return true; // set to true if accounts could not be fetched - used for notifications + } for (Account account : this.accounts) { if (account.isEnabled()) { return true; @@ -4330,10 +4336,6 @@ public class XmppConnectionService extends Service { return getBooleanPreference(SettingsActivity.BLIND_TRUST_BEFORE_VERIFICATION, R.bool.btbv); } - public boolean showForegroundService() { - return getBooleanPreference(SettingsActivity.SHOW_FOREGROUND_SERVICE, R.bool.show_foreground_service); - } - public void ScheduleAutomaticExport() { //start export log service every day at given time if (Config.ExportLogs) { diff --git a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java index 1ace03e5e..4bf9d26cc 100644 --- a/src/main/java/de/pixart/messenger/ui/SettingsActivity.java +++ b/src/main/java/de/pixart/messenger/ui/SettingsActivity.java @@ -17,6 +17,7 @@ import android.preference.PreferenceCategory; import android.preference.PreferenceManager; import android.preference.PreferenceScreen; import android.support.annotation.NonNull; +import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.util.Log; import android.view.LayoutInflater; @@ -541,7 +542,7 @@ public class SettingsActivity extends XmppActivity implements } private void startExport() { - startService(new Intent(getApplicationContext(), ExportLogsService.class)); + ContextCompat.startForegroundService(this, new Intent(this, ExportLogsService.class)); } private void displayToast(final String msg) { diff --git a/src/main/java/de/pixart/messenger/ui/SettingsFragment.java b/src/main/java/de/pixart/messenger/ui/SettingsFragment.java index b40875f7c..ec4898f89 100644 --- a/src/main/java/de/pixart/messenger/ui/SettingsFragment.java +++ b/src/main/java/de/pixart/messenger/ui/SettingsFragment.java @@ -10,6 +10,7 @@ import android.text.TextUtils; import de.pixart.messenger.Config; import de.pixart.messenger.R; +import de.pixart.messenger.utils.Compatibility; public class SettingsFragment extends PreferenceFragment { @@ -30,6 +31,7 @@ public class SettingsFragment extends PreferenceFragment { mCategory.removePreference(cleanPrivateStorage); } } + Compatibility.removeUnusedPreferences(this); if (!TextUtils.isEmpty(page)) { openPreferenceScreen(page); diff --git a/src/main/java/de/pixart/messenger/ui/XmppActivity.java b/src/main/java/de/pixart/messenger/ui/XmppActivity.java index 3ab6bfc6a..acb970e82 100644 --- a/src/main/java/de/pixart/messenger/ui/XmppActivity.java +++ b/src/main/java/de/pixart/messenger/ui/XmppActivity.java @@ -536,7 +536,11 @@ public abstract class XmppActivity extends ActionBarActivity { intent.setAction(Intent.ACTION_SEND); intent.setData(uri); intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - startService(intent); + try { + startService(intent); + } catch (Exception e) { + Log.e(Config.LOGTAG, "unable to delegate uri permission", e); + } } protected void inviteToConversation(Conversation conversation) { diff --git a/src/main/java/de/pixart/messenger/utils/Compatibility.java b/src/main/java/de/pixart/messenger/utils/Compatibility.java new file mode 100644 index 000000000..95d7b4ef2 --- /dev/null +++ b/src/main/java/de/pixart/messenger/utils/Compatibility.java @@ -0,0 +1,66 @@ +package de.pixart.messenger.utils; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Build; +import android.preference.Preference; +import android.preference.PreferenceCategory; +import android.preference.PreferenceManager; +import android.preference.PreferenceScreen; +import android.support.annotation.BoolRes; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import de.pixart.messenger.R; +import de.pixart.messenger.ui.SettingsActivity; +import de.pixart.messenger.ui.SettingsFragment; + +public class Compatibility { + private static final List<String> UNUSED_SETTINGS_POST_TWENTYSIX = Arrays.asList( + SettingsActivity.SHOW_FOREGROUND_SERVICE, + "led", + "notification_ringtone", + "notification_headsup", + "vibrate_on_notification"); + private static final List<String> UNUESD_SETTINGS_PRE_TWENTYSIX = Collections.singletonList("more_notification_settings"); + + public static boolean twentySix() { + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O; + } + + private static boolean getBooleanPreference(Context context, String name, @BoolRes int res) { + return getPreferences(context).getBoolean(name, context.getResources().getBoolean(res)); + } + + private static SharedPreferences getPreferences(final Context context) { + return PreferenceManager.getDefaultSharedPreferences(context); + } + + public static boolean keepForegroundService(Context context) { + return twentySix() || getBooleanPreference(context, SettingsActivity.SHOW_FOREGROUND_SERVICE, R.bool.show_foreground_service); + } + + public static void removeUnusedPreferences(SettingsFragment settingsFragment) { + List<PreferenceScreen> screens = Arrays.asList( + (PreferenceScreen) settingsFragment.findPreference("notifications")); + List<PreferenceCategory> categories = Arrays.asList( + (PreferenceCategory) settingsFragment.findPreference("general")); + for (String key : (twentySix() ? UNUSED_SETTINGS_POST_TWENTYSIX : UNUESD_SETTINGS_PRE_TWENTYSIX)) { + Preference preference = settingsFragment.findPreference(key); + if (preference != null) { + for (PreferenceScreen screen : screens) { + if (screen != null) { + screen.removePreference(preference); + } + } + for (PreferenceCategory category : categories) { + if (category != null) { + category.removePreference(preference); + } + } + } + } + } +}
\ No newline at end of file diff --git a/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java b/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java index 4e275df3f..73d5589e4 100644 --- a/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java +++ b/src/main/java/de/pixart/messenger/utils/ConversationsFileObserver.java @@ -2,12 +2,15 @@ package de.pixart.messenger.utils; import android.os.FileObserver; +import android.util.Log; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Stack; +import de.pixart.messenger.Config; + /** * Copyright (C) 2012 Bartek Przybylski * Copyright (C) 2015 ownCloud Inc. @@ -19,7 +22,7 @@ public abstract class ConversationsFileObserver { private final String path; private final List<SingleFileObserver> mObservers = new ArrayList<>(); - public ConversationsFileObserver(String path) { + protected ConversationsFileObserver(String path) { this.path = path; } @@ -88,13 +91,17 @@ public abstract class ConversationsFileObserver { private class SingleFileObserver extends FileObserver { private final String path; - public SingleFileObserver(String path, int mask) { + SingleFileObserver(String path, int mask) { super(path, mask); this.path = path; } @Override public void onEvent(int event, String filename) { + if (filename == null) { + Log.d(Config.LOGTAG, "ignored file event with NULL filename (event=" + event + ")"); + return; + } ConversationsFileObserver.this.onEvent(event, path + '/' + filename); } |