Fix push notifications in Android 12
This commit is contained in:
parent
d513de70c4
commit
3e140825f5
2 changed files with 131 additions and 108 deletions
|
@ -1,21 +1,28 @@
|
|||
/* Copyright 2018 Jeremiasz Nelz <remi6397(a)gmail.com>
|
||||
* Copyright 2017 Andrew Dawson
|
||||
/*
|
||||
* Husky -- A Pleroma client for Android
|
||||
*
|
||||
* This file is a part of Tusky.
|
||||
* Copyright (C) 2022 The Husky Developers
|
||||
* Copyright (C) 2022 Conny Duck
|
||||
* Copyright (C) 2018 Jeremiasz Nelz <remi6397(a)gmail.com>
|
||||
* Copyright (C) 2017 Andrew Dawson
|
||||
*
|
||||
* This program 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.
|
||||
* This program 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.
|
||||
*
|
||||
* Tusky 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.
|
||||
* This program 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 Tusky; if not,
|
||||
* see <http://www.gnu.org/licenses>. */
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.keylesspalace.tusky.components.notifications;
|
||||
|
||||
import static com.keylesspalace.tusky.viewdata.PollViewDataKt.buildDescription;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationChannelGroup;
|
||||
import android.app.NotificationManager;
|
||||
|
@ -28,8 +35,6 @@ import android.graphics.Color;
|
|||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
@ -42,7 +47,6 @@ import androidx.work.NetworkType;
|
|||
import androidx.work.PeriodicWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
import androidx.work.WorkRequest;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
|
||||
import com.bumptech.glide.request.FutureTarget;
|
||||
|
@ -51,7 +55,6 @@ import com.keylesspalace.tusky.MainActivity;
|
|||
import com.keylesspalace.tusky.R;
|
||||
import com.keylesspalace.tusky.db.AccountEntity;
|
||||
import com.keylesspalace.tusky.db.AccountManager;
|
||||
import com.keylesspalace.tusky.entity.ChatMessage;
|
||||
import com.keylesspalace.tusky.entity.Notification;
|
||||
import com.keylesspalace.tusky.entity.Poll;
|
||||
import com.keylesspalace.tusky.entity.PollOption;
|
||||
|
@ -60,21 +63,17 @@ import com.keylesspalace.tusky.receiver.NotificationClearBroadcastReceiver;
|
|||
import com.keylesspalace.tusky.receiver.SendStatusBroadcastReceiver;
|
||||
import com.keylesspalace.tusky.util.StringUtils;
|
||||
import com.keylesspalace.tusky.viewdata.PollViewDataKt;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
import static com.keylesspalace.tusky.viewdata.PollViewDataKt.buildDescription;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class NotificationHelper {
|
||||
|
||||
|
@ -185,7 +184,7 @@ public class NotificationHelper {
|
|||
break;
|
||||
}
|
||||
} catch(JSONException e) {
|
||||
Log.d(TAG, Log.getStackTraceString(e));
|
||||
Timber.e(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -218,7 +217,7 @@ public class NotificationHelper {
|
|||
|
||||
accountAvatar = target.get();
|
||||
} catch(ExecutionException | InterruptedException e) {
|
||||
Log.d(TAG, "error loading account avatar", e);
|
||||
Timber.e("Error loading account avatar %s", e);
|
||||
accountAvatar = BitmapFactory.decodeResource(context.getResources(), R.drawable.avatar_default);
|
||||
}
|
||||
|
||||
|
@ -287,7 +286,7 @@ public class NotificationHelper {
|
|||
summaryBuilder.setContentTitle(title)
|
||||
.setContentText(text);
|
||||
} catch(JSONException e) {
|
||||
Log.d(TAG, Log.getStackTraceString(e));
|
||||
Timber.e(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -314,8 +313,10 @@ public class NotificationHelper {
|
|||
summaryStackBuilder.addParentStack(MainActivity.class);
|
||||
summaryStackBuilder.addNextIntent(summaryResultIntent);
|
||||
|
||||
PendingIntent summaryResultPendingIntent = summaryStackBuilder.getPendingIntent((int) (notificationId + account.getId() * 10000),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
PendingIntent summaryResultPendingIntent = summaryStackBuilder.getPendingIntent(
|
||||
(int) (notificationId + account.getId() * 10000),
|
||||
pendingIntentFlags(false)
|
||||
);
|
||||
|
||||
// we have to switch account here
|
||||
Intent eventResultIntent = new Intent(context, MainActivity.class);
|
||||
|
@ -324,13 +325,16 @@ public class NotificationHelper {
|
|||
eventStackBuilder.addParentStack(MainActivity.class);
|
||||
eventStackBuilder.addNextIntent(eventResultIntent);
|
||||
|
||||
PendingIntent eventResultPendingIntent = eventStackBuilder.getPendingIntent((int) account.getId(),
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
PendingIntent eventResultPendingIntent = eventStackBuilder.getPendingIntent(
|
||||
(int) account.getId(),
|
||||
pendingIntentFlags(false));
|
||||
|
||||
Intent deleteIntent = new Intent(context, NotificationClearBroadcastReceiver.class);
|
||||
deleteIntent.putExtra(ACCOUNT_ID, account.getId());
|
||||
PendingIntent deletePendingIntent = PendingIntent.getBroadcast(context, summary ? (int) account.getId() : notificationId, deleteIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
PendingIntent deletePendingIntent = PendingIntent.getBroadcast(
|
||||
context, summary ? (int) account.getId() : notificationId,
|
||||
deleteIntent,
|
||||
pendingIntentFlags(false));
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, getChannelId(account, body))
|
||||
.setSmallIcon(R.drawable.ic_notify)
|
||||
|
@ -386,12 +390,11 @@ public class NotificationHelper {
|
|||
return PendingIntent.getBroadcast(context.getApplicationContext(),
|
||||
notificationId,
|
||||
replyIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
pendingIntentFlags(true));
|
||||
}
|
||||
|
||||
public static void createNotificationChannelsForAccount(@NonNull AccountEntity account, @NonNull Context context) {
|
||||
if(NOTIFICATION_USE_CHANNELS) {
|
||||
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
String[] channelIds = new String[]{
|
||||
|
@ -406,6 +409,7 @@ public class NotificationHelper {
|
|||
CHANNEL_SUBSCRIPTIONS + account.getIdentifier(),
|
||||
CHANNEL_MOVE + account.getIdentifier()
|
||||
};
|
||||
|
||||
int[] channelNames = {
|
||||
R.string.notification_mention_name,
|
||||
R.string.notification_follow_name,
|
||||
|
@ -418,6 +422,7 @@ public class NotificationHelper {
|
|||
R.string.notification_subscription_name,
|
||||
R.string.notification_move_name
|
||||
};
|
||||
|
||||
int[] channelDescriptions = {
|
||||
R.string.notification_mention_descriptions,
|
||||
R.string.notification_follow_description,
|
||||
|
@ -455,24 +460,20 @@ public class NotificationHelper {
|
|||
}
|
||||
|
||||
notificationManager.createNotificationChannels(channels);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void deleteNotificationChannelsForAccount(@NonNull AccountEntity account, @NonNull Context context) {
|
||||
if(NOTIFICATION_USE_CHANNELS) {
|
||||
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
//noinspection ConstantConditions
|
||||
notificationManager.deleteNotificationChannelGroup(account.getIdentifier());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static void deleteLegacyNotificationChannels(@NonNull Context context, @NonNull AccountManager accountManager) {
|
||||
if(NOTIFICATION_USE_CHANNELS) {
|
||||
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
// used until Tusky 1.4
|
||||
|
@ -491,7 +492,6 @@ public class NotificationHelper {
|
|||
|
||||
public static boolean areNotificationsEnabled(@NonNull Context context, @NonNull AccountManager accountManager) {
|
||||
if(NOTIFICATION_USE_CHANNELS) {
|
||||
|
||||
// on Android >= O, notifications are enabled, if at least one channel is enabled
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
|
@ -499,20 +499,18 @@ public class NotificationHelper {
|
|||
if(notificationManager.areNotificationsEnabled()) {
|
||||
for(NotificationChannel channel : notificationManager.getNotificationChannels()) {
|
||||
if(channel.getImportance() > NotificationManager.IMPORTANCE_NONE) {
|
||||
Log.d(TAG, "NotificationsEnabled");
|
||||
Timber.d("NotificationsEnabled");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Log.d(TAG, "NotificationsDisabled");
|
||||
Timber.d("NotificationsDisabled");
|
||||
|
||||
return false;
|
||||
|
||||
} else {
|
||||
// on Android < O, notifications are enabled, if at least one account has notification enabled
|
||||
return accountManager.areNotificationsEnabled();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void enablePullNotifications(Context context) {
|
||||
|
@ -530,12 +528,12 @@ public class NotificationHelper {
|
|||
|
||||
workManager.enqueue(workRequest);
|
||||
|
||||
Log.d(TAG, "enabled notification checks with "+ PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS + "ms interval");
|
||||
Timber.d("enabled notification checks with ${PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS} ms interval");
|
||||
}
|
||||
|
||||
public static void disablePullNotifications(Context context) {
|
||||
WorkManager.getInstance(context).cancelAllWorkByTag(NOTIFICATION_PULL_TAG);
|
||||
Log.d(TAG, "disabled notification checks");
|
||||
Timber.d("Disabled notification checks");
|
||||
}
|
||||
|
||||
public static void clearNotificationsForActiveAccount(@NonNull Context context, @NonNull AccountManager accountManager) {
|
||||
|
@ -557,7 +555,6 @@ public class NotificationHelper {
|
|||
|
||||
private static boolean filterNotification(AccountEntity account, Notification notification,
|
||||
Context context) {
|
||||
|
||||
if(NOTIFICATION_USE_CHANNELS) {
|
||||
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
|
||||
|
@ -623,12 +620,11 @@ public class NotificationHelper {
|
|||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void setupPreferences(AccountEntity account,
|
||||
private static void setupPreferences(
|
||||
AccountEntity account,
|
||||
NotificationCompat.Builder builder) {
|
||||
|
||||
if(NOTIFICATION_USE_CHANNELS) {
|
||||
return; //do nothing on Android O or newer, the system uses the channel settings anyway
|
||||
}
|
||||
|
@ -711,6 +707,7 @@ public class NotificationHelper {
|
|||
return String.format(context.getString(R.string.notification_move_format), accountName);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -760,4 +757,10 @@ public class NotificationHelper {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static int pendingIntentFlags(boolean mutable) {
|
||||
return (PendingIntent.FLAG_UPDATE_CURRENT
|
||||
| (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ?
|
||||
(mutable ? PendingIntent.FLAG_MUTABLE : PendingIntent.FLAG_IMMUTABLE) : 0)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,23 @@
|
|||
/*
|
||||
* Husky -- A Pleroma client for Android
|
||||
*
|
||||
* Copyright (C) 2022 The Husky Developers
|
||||
* Copyright (C) 2020 Alibek Omarov
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.keylesspalace.tusky.service
|
||||
|
||||
import android.app.NotificationChannel
|
||||
|
@ -222,15 +242,15 @@ class StreamingService : Service(), Injectable {
|
|||
return object : WebSocketListener() {
|
||||
|
||||
override fun onOpen(webSocket: WebSocket, response: Response) {
|
||||
Timber.d("Stream connected to: $tag")
|
||||
Timber.d("Stream connected to: $tag. Response[$response]")
|
||||
}
|
||||
|
||||
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
|
||||
Timber.d("Stream closed for: $tag")
|
||||
Timber.d("Stream closed for: $tag. Reason[$reason]")
|
||||
}
|
||||
|
||||
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
|
||||
Timber.e("Stream failed for $tag", t)
|
||||
Timber.e("Stream failed for $tag: $t. Response[$response]")
|
||||
}
|
||||
|
||||
override fun onMessage(webSocket: WebSocket, text: String) {
|
||||
|
|
Loading…
Reference in a new issue