Added notifications for new messages

This commit is contained in:
ChronosX88 2019-05-26 14:34:43 +04:00
parent 18bce5605f
commit 6936c0127e
7 changed files with 162 additions and 0 deletions

View File

@ -57,6 +57,7 @@ android {
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs') implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.1.0-alpha02' implementation 'androidx.appcompat:appcompat:1.1.0-alpha02'
implementation "com.android.support:support-compat:28.0.0"
implementation "androidx.room:room-runtime:2.1.0-alpha04" implementation "androidx.room:room-runtime:2.1.0-alpha04"
implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
annotationProcessor "androidx.room:room-compiler:2.1.0-alpha04" annotationProcessor "androidx.room:room-compiler:2.1.0-alpha04"

View File

@ -52,6 +52,7 @@ public class AppHelper extends MultiDexApplication {
private static Handler mainUIThreadHandler; private static Handler mainUIThreadHandler;
private static ServiceConnection serviceConnection; private static ServiceConnection serviceConnection;
private static boolean isMainActivityDestroyed = true; private static boolean isMainActivityDestroyed = true;
private static String currentChatActivity = "";
public final static Map<String, byte[]> avatarsCache = new ConcurrentHashMap<>(); public final static Map<String, byte[]> avatarsCache = new ConcurrentHashMap<>();
@Override @Override
@ -150,4 +151,12 @@ public class AppHelper extends MultiDexApplication {
public static void setIsMainActivityDestroyed(boolean isMainActivityDestroyed) { public static void setIsMainActivityDestroyed(boolean isMainActivityDestroyed) {
AppHelper.isMainActivityDestroyed = isMainActivityDestroyed; AppHelper.isMainActivityDestroyed = isMainActivityDestroyed;
} }
public static String getCurrentChatActivity() {
return currentChatActivity;
}
public static void setCurrentChatActivity(String currentChatActivity) {
AppHelper.currentChatActivity = currentChatActivity;
}
} }

View File

@ -16,6 +16,20 @@
package io.github.chronosx88.influence.helpers; package io.github.chronosx88.influence.helpers;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import com.amulyakhare.textdrawable.TextDrawable;
import com.amulyakhare.textdrawable.util.ColorGenerator;
import com.instacart.library.truetime.TrueTime; import com.instacart.library.truetime.TrueTime;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
@ -28,14 +42,28 @@ import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.FullJid; import org.jxmpp.jid.FullJid;
import org.jxmpp.jid.Jid; import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import io.github.chronosx88.influence.R;
import io.github.chronosx88.influence.models.GenericMessage; import io.github.chronosx88.influence.models.GenericMessage;
import io.github.chronosx88.influence.models.appEvents.LastMessageEvent; import io.github.chronosx88.influence.models.appEvents.LastMessageEvent;
import io.github.chronosx88.influence.models.appEvents.NewMessageEvent; import io.github.chronosx88.influence.models.appEvents.NewMessageEvent;
import io.github.chronosx88.influence.models.appEvents.UserPresenceChangedEvent; import io.github.chronosx88.influence.models.appEvents.UserPresenceChangedEvent;
import java9.util.concurrent.CompletableFuture;
public class NetworkHandler implements IncomingChatMessageListener, PresenceEventListener { public class NetworkHandler implements IncomingChatMessageListener, PresenceEventListener {
private final static String LOG_TAG = "NetworkHandler"; private final static String LOG_TAG = "NetworkHandler";
private final static String NOTIFICATION_CHANNEL_ID = "InfluenceNotificationsChannel";
private NotificationManagerCompat notificationManager = NotificationManagerCompat.from(AppHelper.getContext());
public NetworkHandler() {
createNotificationChannel();
}
@Override @Override
public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) { public void newIncomingMessage(EntityBareJid from, Message message, Chat chat) {
@ -49,8 +77,68 @@ public class NetworkHandler implements IncomingChatMessageListener, PresenceEven
EventBus.getDefault().post(new NewMessageEvent(chatID, messageID)); EventBus.getDefault().post(new NewMessageEvent(chatID, messageID));
EventBus.getDefault().post(new LastMessageEvent(chatID, new GenericMessage(LocalDBWrapper.getMessageByID(messageID)))); EventBus.getDefault().post(new LastMessageEvent(chatID, new GenericMessage(LocalDBWrapper.getMessageByID(messageID))));
if(!AppHelper.getCurrentChatActivity().equals(chatID)) {
byte[] avatarBytes = new byte[0];
try {
CompletableFuture<byte[]> future = loadAvatar(chatID);
if(future != null) {
avatarBytes = future.get();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
Bitmap avatar = null;
if(avatarBytes != null) {
avatar = BitmapFactory.decodeByteArray(avatarBytes, 0, avatarBytes.length);
}
NotificationCompat.Builder notification = new NotificationCompat.Builder(AppHelper.getContext(), NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_message_white_24dp)
.setContentTitle(chatID)
.setContentText(message.getBody())
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
if(avatar != null) {
notification.setLargeIcon(avatar);
} else {
String firstLetter = Character.toString(Character.toUpperCase(chatID.charAt(0)));
Drawable avatarText = TextDrawable.builder()
.beginConfig()
.width(64)
.height(64)
.endConfig()
.buildRound(firstLetter, ColorGenerator.MATERIAL.getColor(firstLetter));
notification.setLargeIcon(drawableToBitmap(avatarText));
}
notificationManager.notify(new Random().nextInt(), notification.build());
}
} }
public static Bitmap drawableToBitmap(Drawable drawable) {
Bitmap bitmap;
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
if(bitmapDrawable.getBitmap() != null) {
return bitmapDrawable.getBitmap();
}
}
if(drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
} else {
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}
@Override @Override
public void presenceAvailable(FullJid address, Presence availablePresence) { public void presenceAvailable(FullJid address, Presence availablePresence) {
EventBus.getDefault().post(new UserPresenceChangedEvent(address.asBareJid().asUnescapedString(), availablePresence.isAvailable())); EventBus.getDefault().post(new UserPresenceChangedEvent(address.asBareJid().asUnescapedString(), availablePresence.isAvailable()));
@ -75,4 +163,41 @@ public class NetworkHandler implements IncomingChatMessageListener, PresenceEven
public void presenceUnsubscribed(BareJid address, Presence unsubscribedPresence) { public void presenceUnsubscribed(BareJid address, Presence unsubscribedPresence) {
} }
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String name = AppHelper.getContext().getString(R.string.notification_channel_name);
String description = AppHelper.getContext().getString(R.string.notification_channel_desc);
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, name, NotificationManager.IMPORTANCE_DEFAULT);
channel.setDescription(description);
NotificationManager notificationManager = AppHelper.getContext().getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
private CompletableFuture<byte[]> loadAvatar(String senderID) {
if(senderID.length() != 0) {
if(AppHelper.avatarsCache.containsKey(senderID)) {
return CompletableFuture.completedFuture(AppHelper.avatarsCache.get(senderID));
}
CompletableFuture<byte[]> completableFuture = CompletableFuture.supplyAsync(() -> {
while (AppHelper.getXmppConnection() == null);
while (AppHelper.getXmppConnection().isConnectionAlive() != true);
EntityBareJid jid = null;
try {
jid = JidCreate.entityBareFrom(senderID);
} catch (XmppStringprepException e) {
e.printStackTrace();
}
return AppHelper.getXmppConnection().getAvatar(jid);
}).thenApply((avatarBytes) -> {
if(avatarBytes != null) {
AppHelper.avatarsCache.put(senderID, avatarBytes);
}
return avatarBytes;
});
return completableFuture;
}
return null;
}
} }

View File

@ -60,6 +60,7 @@ class ChatPresenter(private val view: CoreContracts.IChatViewContract, private v
view.setAdapter(chatAdapter) view.setAdapter(chatAdapter)
getUserStatus() getUserStatus()
EventBus.getDefault().register(this) EventBus.getDefault().register(this)
AppHelper.setCurrentChatActivity(chatID)
} }
override fun sendMessage(text: String): Boolean { override fun sendMessage(text: String): Boolean {
@ -87,6 +88,7 @@ class ChatPresenter(private val view: CoreContracts.IChatViewContract, private v
override fun onDestroy() { override fun onDestroy() {
EventBus.getDefault().unregister(this) EventBus.getDefault().unregister(this)
AppHelper.setCurrentChatActivity("")
} }
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)

View File

@ -0,0 +1,21 @@
<!--
~ Copyright 2019 ChronosX88
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM18,14L6,14v-2h12v2zM18,11L6,11L6,9h12v2zM18,8L6,8L6,6h12v2z"/>
</vector>

View File

@ -27,4 +27,6 @@
<string name="offline">Не в сети</string> <string name="offline">Не в сети</string>
<string name="online">В сети</string> <string name="online">В сети</string>
<string name="clear_chat">Очистить чат</string> <string name="clear_chat">Очистить чат</string>
<string name="notification_channel_name">Уведомления Influence</string>
<string name="notification_channel_desc">Уведомления чатов Influence</string>
</resources> </resources>

View File

@ -26,4 +26,6 @@
<string name="offline">Offline</string> <string name="offline">Offline</string>
<string name="online">Online</string> <string name="online">Online</string>
<string name="clear_chat">Clear chat</string> <string name="clear_chat">Clear chat</string>
<string name="notification_channel_name">Influence Notifications</string>
<string name="notification_channel_desc">Influence chat notifications</string>
</resources> </resources>