mirror of
https://github.com/nuclearfog/Shitter.git
synced 2025-01-30 19:05:02 +01:00
added push notification support, memory leak fix
This commit is contained in:
parent
1294539a62
commit
115029f275
@ -15,7 +15,7 @@
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" tools:ignore="ScopedStorage" />
|
||||
<uses-permission android:name="io.heckel.ntfy.SEND_MESSAGE"/>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<application
|
||||
android:name=".ClientApplication"
|
||||
|
@ -1,10 +1,16 @@
|
||||
package org.nuclearfog.twidda;
|
||||
|
||||
import static android.app.NotificationManager.IMPORTANCE_LOW;
|
||||
|
||||
import android.app.Application;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.os.Build;
|
||||
|
||||
import org.nuclearfog.twidda.backend.image.ImageCache;
|
||||
import org.nuclearfog.twidda.backend.image.PicassoBuilder;
|
||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||
import org.nuclearfog.twidda.notification.PushNotification;
|
||||
import org.nuclearfog.twidda.notification.PushSubscription;
|
||||
|
||||
/**
|
||||
@ -14,13 +20,21 @@ public class ClientApplication extends Application {
|
||||
|
||||
private GlobalSettings settings;
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
// setup push receiver
|
||||
settings = GlobalSettings.getInstance(getApplicationContext());
|
||||
if (settings.pushEnabled()) {
|
||||
PushSubscription.subscripe(getApplicationContext());
|
||||
}
|
||||
// setup notification channel
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationManager manager = getSystemService(NotificationManager.class);
|
||||
NotificationChannel channel = new NotificationChannel(PushNotification.NOTIFICATION_ID_STR, "fdgdfg", IMPORTANCE_LOW);
|
||||
manager.createNotificationChannel(channel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -10,6 +10,7 @@ import org.nuclearfog.twidda.backend.api.Connection;
|
||||
import org.nuclearfog.twidda.backend.api.ConnectionException;
|
||||
import org.nuclearfog.twidda.backend.api.ConnectionManager;
|
||||
import org.nuclearfog.twidda.database.AppDatabase;
|
||||
import org.nuclearfog.twidda.model.Notification;
|
||||
import org.nuclearfog.twidda.model.lists.Notifications;
|
||||
import org.nuclearfog.twidda.ui.fragments.NotificationFragment;
|
||||
|
||||
@ -33,24 +34,38 @@ public class NotificationLoader extends AsyncExecutor<NotificationLoader.Notific
|
||||
|
||||
|
||||
@Override
|
||||
protected NotificationLoaderResult doInBackground(@NonNull NotificationLoaderParam params) {
|
||||
protected NotificationLoaderResult doInBackground(@NonNull NotificationLoaderParam param) {
|
||||
try {
|
||||
Notifications result;
|
||||
if (params.minId == 0L && params.maxId == 0L) {
|
||||
result = db.getNotifications();
|
||||
if (result.isEmpty()) {
|
||||
result = connection.getNotifications(0L, 0L);
|
||||
db.saveNotifications(result);
|
||||
}
|
||||
} else {
|
||||
result = connection.getNotifications(params.minId, params.maxId);
|
||||
if (params.maxId == 0L) {
|
||||
db.saveNotifications(result);
|
||||
}
|
||||
switch (param.mode) {
|
||||
case NotificationLoaderParam.LOAD_ALL:
|
||||
Notifications result;
|
||||
if (param.minId == 0L && param.maxId == 0L) {
|
||||
result = db.getNotifications();
|
||||
if (result.isEmpty()) {
|
||||
result = connection.getNotifications(0L, 0L);
|
||||
db.saveNotifications(result);
|
||||
}
|
||||
} else {
|
||||
result = connection.getNotifications(param.minId, param.maxId);
|
||||
if (param.maxId == 0L) {
|
||||
db.saveNotifications(result);
|
||||
}
|
||||
}
|
||||
return new NotificationLoaderResult(result, param.position, null);
|
||||
|
||||
case NotificationLoaderParam.LOAD_UNREAD:
|
||||
// load (known) notifications from database first
|
||||
Notifications notifications = db.getNotifications();
|
||||
// then load new notifications using the latest known notification
|
||||
long minId = 0L;
|
||||
Notification lastNotification = notifications.peekFirst();
|
||||
if (lastNotification != null)
|
||||
minId = lastNotification.getId();
|
||||
result = connection.getNotifications(minId, 0L);
|
||||
return new NotificationLoaderResult(result, 0, null);
|
||||
}
|
||||
return new NotificationLoaderResult(result, params.position, null);
|
||||
} catch (ConnectionException exception) {
|
||||
return new NotificationLoaderResult(null, params.position, exception);
|
||||
return new NotificationLoaderResult(null, param.position, exception);
|
||||
} catch (Exception exception) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
exception.printStackTrace();
|
||||
@ -64,13 +79,18 @@ public class NotificationLoader extends AsyncExecutor<NotificationLoader.Notific
|
||||
*/
|
||||
public static class NotificationLoaderParam {
|
||||
|
||||
public static final int LOAD_ALL = 1;
|
||||
public static final int LOAD_UNREAD = 2;
|
||||
|
||||
final int position;
|
||||
final long minId, maxId;
|
||||
final int mode;
|
||||
|
||||
public NotificationLoaderParam(int position, long minId, long maxId) {
|
||||
public NotificationLoaderParam(int mode, int position, long minId, long maxId) {
|
||||
this.position = position;
|
||||
this.minId = minId;
|
||||
this.maxId = maxId;
|
||||
this.mode = mode;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,12 @@ public class PushUpdate implements Serializable {
|
||||
* @param host unifiedpush host url
|
||||
*/
|
||||
public PushUpdate(String host) {
|
||||
this.host = host;
|
||||
int idxQuery = host.indexOf("?");
|
||||
if (idxQuery > 0) {
|
||||
this.host = host.substring(0, idxQuery);
|
||||
} else {
|
||||
this.host = host;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,107 @@
|
||||
package org.nuclearfog.twidda.notification;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
|
||||
import org.nuclearfog.twidda.BuildConfig;
|
||||
import org.nuclearfog.twidda.R;
|
||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||
import org.nuclearfog.twidda.model.Notification;
|
||||
import org.nuclearfog.twidda.model.lists.Notifications;
|
||||
|
||||
/**
|
||||
* This class creates app push notification
|
||||
*
|
||||
* @author nuclearfog
|
||||
*/
|
||||
public class PushNotification {
|
||||
|
||||
public static final String NOTIFICATION_ID_STR = BuildConfig.APPLICATION_ID + ".notification";
|
||||
|
||||
private static final int NOTIFICATION_ID = 0x25281;
|
||||
|
||||
private NotificationManagerCompat notificationManager;
|
||||
private NotificationCompat.Builder notificationBuilder;
|
||||
private GlobalSettings settings;
|
||||
private Context context;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public PushNotification(Context context) {
|
||||
notificationManager = NotificationManagerCompat.from(context);
|
||||
notificationBuilder = new NotificationCompat.Builder(context, NOTIFICATION_ID_STR);
|
||||
settings = GlobalSettings.getInstance(context);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* create push-notification from notifications
|
||||
* @param notifications new notifications
|
||||
*/
|
||||
@SuppressLint("MissingPermission")
|
||||
public void createNotification(Notifications notifications) {
|
||||
if (!notifications.isEmpty()) {
|
||||
String title = settings.getLogin().getConfiguration().getName();
|
||||
String content;
|
||||
int icon;
|
||||
if (notifications.size() > 1) {
|
||||
content = context.getString(R.string.notification_new);
|
||||
icon = R.drawable.bell;
|
||||
} else {
|
||||
Notification notification = notifications.getFirst();
|
||||
switch (notification.getType()) {
|
||||
case Notification.TYPE_FAVORITE:
|
||||
icon = R.drawable.favorite;
|
||||
content = context.getString(R.string.notification_favorite, notification.getUser().getScreenname());
|
||||
break;
|
||||
|
||||
case Notification.TYPE_REPOST:
|
||||
icon = R.drawable.repost;
|
||||
content = context.getString(R.string.notification_repost, notification.getUser().getScreenname());
|
||||
break;
|
||||
|
||||
case Notification.TYPE_FOLLOW:
|
||||
icon = R.drawable.follower;
|
||||
content = context.getString(R.string.notification_follow, notification.getUser().getScreenname());
|
||||
break;
|
||||
|
||||
case Notification.TYPE_REQUEST:
|
||||
icon = R.drawable.follower_request;
|
||||
content = context.getString(R.string.notification_request, notification.getUser().getScreenname());
|
||||
break;
|
||||
|
||||
case Notification.TYPE_MENTION:
|
||||
icon = R.drawable.mention;
|
||||
content = context.getString(R.string.notification_mention, notification.getUser().getScreenname());
|
||||
break;
|
||||
|
||||
case Notification.TYPE_STATUS:
|
||||
icon = R.drawable.post;
|
||||
content = context.getString(R.string.notification_status, notification.getUser().getScreenname());
|
||||
break;
|
||||
|
||||
case Notification.TYPE_UPDATE:
|
||||
icon = R.drawable.post;
|
||||
content = context.getString(R.string.notification_edit);
|
||||
break;
|
||||
|
||||
case Notification.TYPE_POLL:
|
||||
icon = R.drawable.poll;
|
||||
content = context.getString(R.string.notification_poll);
|
||||
break;
|
||||
|
||||
default:
|
||||
icon = R.drawable.bell;
|
||||
content = context.getString(R.string.notification_new);
|
||||
break;
|
||||
}
|
||||
}
|
||||
notificationBuilder.setContentTitle(title).setContentText(content).setSmallIcon(icon).setAutoCancel(true);
|
||||
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,10 @@ import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.nuclearfog.twidda.backend.async.AsyncExecutor.AsyncCallback;
|
||||
import org.nuclearfog.twidda.backend.async.NotificationLoader;
|
||||
import org.nuclearfog.twidda.backend.async.NotificationLoader.NotificationLoaderParam;
|
||||
import org.nuclearfog.twidda.backend.async.NotificationLoader.NotificationLoaderResult;
|
||||
import org.nuclearfog.twidda.backend.async.PushUpdater;
|
||||
import org.nuclearfog.twidda.backend.helper.update.PushUpdate;
|
||||
import org.nuclearfog.twidda.config.GlobalSettings;
|
||||
@ -14,14 +18,19 @@ import org.unifiedpush.android.connector.MessagingReceiver;
|
||||
*
|
||||
* @author nuclearfog
|
||||
*/
|
||||
public class PushNotificationReceiver extends MessagingReceiver {
|
||||
public class PushNotificationReceiver extends MessagingReceiver implements AsyncCallback<NotificationLoaderResult> {
|
||||
|
||||
private PushNotification notificationManager;
|
||||
|
||||
|
||||
@Override
|
||||
public void onMessage(@NonNull Context context, @NonNull byte[] message, @NonNull String instance) {
|
||||
GlobalSettings settings = GlobalSettings.getInstance(context);
|
||||
if (settings.pushEnabled()) {
|
||||
// todo add manual synchonization
|
||||
NotificationLoader loader = new NotificationLoader(context);
|
||||
NotificationLoaderParam param = new NotificationLoaderParam(NotificationLoaderParam.LOAD_UNREAD, 0, 0L, 0L);
|
||||
notificationManager = new PushNotification(context);
|
||||
loader.execute(param, this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,7 +38,17 @@ public class PushNotificationReceiver extends MessagingReceiver {
|
||||
@Override
|
||||
public void onNewEndpoint(@NonNull Context context, @NonNull String endpoint, @NonNull String instance) {
|
||||
PushUpdater pushUpdater = new PushUpdater(context);
|
||||
PushUpdate update = new PushUpdate(instance);
|
||||
PushUpdate update = new PushUpdate(endpoint);
|
||||
pushUpdater.execute(update, null);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onResult(@NonNull NotificationLoaderResult result) {
|
||||
if (result.notifications != null && !result.notifications.isEmpty()) {
|
||||
if (notificationManager != null) {
|
||||
notificationManager.createNotification(result.notifications);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -372,6 +372,7 @@ public class StatusActivity extends AppCompatActivity implements OnClickListener
|
||||
notificationLoader.cancel();
|
||||
translationLoader.cancel();
|
||||
emojiLoader.cancel();
|
||||
audioDialog.close();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
|
@ -232,6 +232,7 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr
|
||||
loadingCircle.dismiss();
|
||||
statusUpdater.cancel();
|
||||
instanceLoader.cancel();
|
||||
audioDialog.close();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
|
@ -69,8 +69,10 @@ public class VideoViewer extends AppCompatActivity implements Player.Listener {
|
||||
*/
|
||||
private static final int CACHE_SIZE = 64000000;
|
||||
|
||||
private ExoPlayer player;
|
||||
private Toolbar toolbar;
|
||||
private StyledPlayerView playerView;
|
||||
|
||||
private ExoPlayer player;
|
||||
|
||||
private Uri data;
|
||||
|
||||
@ -85,7 +87,7 @@ public class VideoViewer extends AppCompatActivity implements Player.Listener {
|
||||
protected void onCreate(@Nullable Bundle b) {
|
||||
super.onCreate(b);
|
||||
setContentView(R.layout.page_video);
|
||||
StyledPlayerView playerView = findViewById(R.id.page_video_player);
|
||||
playerView = findViewById(R.id.page_video_player);
|
||||
toolbar = findViewById(R.id.page_video_toolbar);
|
||||
playerView.setShowNextButton(false);
|
||||
playerView.setShowPreviousButton(false);
|
||||
@ -157,6 +159,14 @@ public class VideoViewer extends AppCompatActivity implements Player.Listener {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
// remove player reference to prevent memory leak
|
||||
playerView.setPlayer(null);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
|
@ -32,6 +32,8 @@ import org.nuclearfog.twidda.R;
|
||||
import org.nuclearfog.twidda.backend.utils.ConnectionBuilder;
|
||||
import org.nuclearfog.twidda.backend.utils.LinkUtils;
|
||||
|
||||
import java.io.Closeable;
|
||||
|
||||
import okhttp3.Call;
|
||||
|
||||
/**
|
||||
@ -39,9 +41,11 @@ import okhttp3.Call;
|
||||
*
|
||||
* @author nuclearfog
|
||||
*/
|
||||
public class AudioPlayerDialog extends Dialog implements OnClickListener {
|
||||
public class AudioPlayerDialog extends Dialog implements OnClickListener, Closeable {
|
||||
|
||||
private PlayerControlView controls;
|
||||
private TextView mediaLink;
|
||||
|
||||
private ExoPlayer player;
|
||||
|
||||
private Uri data;
|
||||
@ -52,7 +56,7 @@ public class AudioPlayerDialog extends Dialog implements OnClickListener {
|
||||
public AudioPlayerDialog(@NonNull Context context) {
|
||||
super(context, R.style.AudioDialog);
|
||||
setContentView(R.layout.dialog_audio_player);
|
||||
PlayerControlView controls = findViewById(R.id.dialog_audio_player_controls);
|
||||
controls = findViewById(R.id.dialog_audio_player_controls);
|
||||
mediaLink = findViewById(R.id.dialog_audio_player_share);
|
||||
|
||||
controls.setShowNextButton(false);
|
||||
@ -87,6 +91,12 @@ public class AudioPlayerDialog extends Dialog implements OnClickListener {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
// remove player to prevent memory leak
|
||||
controls.setPlayer(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* show dialog and play audio
|
||||
*
|
||||
|
@ -12,6 +12,7 @@ import android.widget.Button;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.CompoundButton.OnCheckedChangeListener;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
@ -34,6 +35,7 @@ import org.nuclearfog.twidda.model.WebPush;
|
||||
public class WebPushDialog extends Dialog implements OnCheckedChangeListener, OnClickListener, OnItemSelectedListener, AsyncCallback<PushUpdateResult> {
|
||||
|
||||
private PushUpdater updater;
|
||||
private GlobalSettings settings;
|
||||
|
||||
private PushUpdate update;
|
||||
|
||||
@ -55,7 +57,7 @@ public class WebPushDialog extends Dialog implements OnCheckedChangeListener, On
|
||||
Button apply_changes = findViewById(R.id.dialog_push_apply);
|
||||
Spinner policySelector = findViewById(R.id.dialog_push_policy);
|
||||
|
||||
GlobalSettings settings = GlobalSettings.getInstance(context);
|
||||
settings = GlobalSettings.getInstance(context);
|
||||
updater = new PushUpdater(context);
|
||||
update = new PushUpdate(settings.getWebPush());
|
||||
mention.setCheckedImmediately(update.mentionsEnabled());
|
||||
@ -164,6 +166,8 @@ public class WebPushDialog extends Dialog implements OnCheckedChangeListener, On
|
||||
@Override
|
||||
public void onResult(@NonNull PushUpdateResult result) {
|
||||
if (result.push != null) {
|
||||
Toast.makeText(getContext(), R.string.info_webpush_update, Toast.LENGTH_SHORT).show();
|
||||
settings.setWebPush(result.push);
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
|
@ -215,7 +215,7 @@ public class NotificationFragment extends ListFragment implements OnNotification
|
||||
* @param pos index to insert the new items
|
||||
*/
|
||||
private void load(long minId, long maxId, int pos) {
|
||||
NotificationLoaderParam param = new NotificationLoaderParam(pos, minId, maxId);
|
||||
NotificationLoaderParam param = new NotificationLoaderParam(NotificationLoaderParam.LOAD_ALL, pos, minId, maxId);
|
||||
notificationLoader.execute(param, notificationLoaderCallback);
|
||||
}
|
||||
}
|
@ -315,4 +315,9 @@
|
||||
<string name="dialog_push_title">Pushbenachrichtigungen</string>
|
||||
<string name="dialog_push_apply">Änderungen anwenden</string>
|
||||
<string name="settings_enable_push_label">aktiviere Push-Benachrichtigung</string>
|
||||
<string name="notification_favorite">%1$s hat einen Status favorisiert</string>
|
||||
<string name="notification_repost">%1$s hat einen Status geteilt</string>
|
||||
<string name="notification_follow">%1$s folgt dir jetzt</string>
|
||||
<string name="notification_poll">Eine Umfrage wurde beendet</string>
|
||||
<string name="notification_mention">%1$s hat dich erwähnt</string>
|
||||
</resources>
|
@ -69,6 +69,7 @@
|
||||
<string name="info_domain_removed">domain removed from the list</string>
|
||||
<string name="info_hashtag_followed">hashtag followed</string>
|
||||
<string name="info_domain_blocked">domain blocked!</string>
|
||||
<string name="info_webpush_update">Push configuration updated!</string>
|
||||
<string name="info_error">Error</string>
|
||||
|
||||
<!-- toast messages for error information -->
|
||||
@ -342,4 +343,13 @@
|
||||
<string name="dialog_push_title">Push notifications</string>
|
||||
<string name="dialog_push_apply">apply changes</string>
|
||||
<string name="settings_enable_push_label">enable push notification</string>
|
||||
<string name="notification_favorite">%1$s favorited your status</string>
|
||||
<string name="notification_repost">%1$s reposted your status</string>
|
||||
<string name="notification_follow">%1$s followed you</string>
|
||||
<string name="notification_request">%1$s requested to follow you</string>
|
||||
<string name="notification_mention">%1$s mentioned you in a status</string>
|
||||
<string name="notification_status">%1$s has posted a status</string>
|
||||
<string name="notification_edit">A status you boosted with has been edited</string>
|
||||
<string name="notification_poll">A poll you have voted in or created has ended</string>
|
||||
<string name="notification_new">New notifications</string>
|
||||
</resources>
|
Loading…
x
Reference in New Issue
Block a user