Made global (in memory) avatar cache and round avatars in ChatActivity

This commit is contained in:
ChronosX88 2019-05-24 16:05:11 +04:00
parent 6c83e7d9e0
commit cebfb33ff8
5 changed files with 153 additions and 63 deletions

View File

@ -30,6 +30,8 @@ import androidx.room.Room;
import com.instacart.library.truetime.TrueTime; import com.instacart.library.truetime.TrueTime;
import java.io.IOException; import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import io.github.chronosx88.influence.LoginCredentials; import io.github.chronosx88.influence.LoginCredentials;
import io.github.chronosx88.influence.XMPPConnection; import io.github.chronosx88.influence.XMPPConnection;
@ -49,6 +51,7 @@ public class AppHelper extends MultiDexApplication {
private static LoginCredentials currentLoginCredentials; private static LoginCredentials currentLoginCredentials;
private static Handler mainUIThreadHandler; private static Handler mainUIThreadHandler;
private static ServiceConnection serviceConnection; private static ServiceConnection serviceConnection;
public final static Map<String, byte[]> avatarsCache = new ConcurrentHashMap<>();
@Override @Override
public void onCreate() { public void onCreate() {
@ -107,12 +110,14 @@ public class AppHelper extends MultiDexApplication {
private static void initTrueTime() { private static void initTrueTime() {
new Thread(() -> { new Thread(() -> {
boolean isTrueTimeIsOn = false; boolean isTrueTimeIsOn = false;
while(!isTrueTimeIsOn) { int count = 0;
while(!isTrueTimeIsOn && count <= 10) {
try { try {
TrueTime.build().withNtpHost(DEFAULT_NTP_SERVER).initialize(); TrueTime.build().withNtpHost(DEFAULT_NTP_SERVER).initialize();
isTrueTimeIsOn = true; isTrueTimeIsOn = true;
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
count++;
} }
} }
}).start(); }).start();

View File

@ -0,0 +1,73 @@
/*
* 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.
*/
package io.github.chronosx88.influence.helpers;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.widget.ImageView;
import androidx.annotation.Nullable;
import com.amulyakhare.textdrawable.TextDrawable;
import com.amulyakhare.textdrawable.util.ColorGenerator;
import com.stfalcon.chatkit.commons.ImageLoader;
import org.jxmpp.jid.EntityBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
import java9.util.concurrent.CompletableFuture;
public class AvatarImageLoader implements ImageLoader {
@Override
public void loadImage(ImageView imageView, @Nullable String url, @Nullable Object payload) {
if(url.length() != 0) {
if(AppHelper.avatarsCache.containsKey(url)) {
byte[] avatarBytes = AppHelper.avatarsCache.get(url);
Bitmap avatar = BitmapFactory.decodeByteArray(avatarBytes, 0, avatarBytes.length);
imageView.setImageBitmap(avatar);
return;
}
String firstLetter = Character.toString(Character.toUpperCase(url.charAt(0)));
imageView.setImageDrawable(TextDrawable.builder()
.beginConfig()
.width(64)
.height(64)
.endConfig()
.buildRound(firstLetter, ColorGenerator.MATERIAL.getColor(firstLetter)));
CompletableFuture.supplyAsync(() -> {
while (AppHelper.getXmppConnection() == null);
while (AppHelper.getXmppConnection().isConnectionAlive() != true);
EntityBareJid jid = null;
try {
jid = JidCreate.entityBareFrom(url);
} catch (XmppStringprepException e) {
e.printStackTrace();
}
return AppHelper.getXmppConnection().getAvatar(jid);
}).thenAccept((avatarBytes) -> {
AppHelper.getMainUIThread().post(() -> {
if(avatarBytes != null) {
Bitmap avatar = BitmapFactory.decodeByteArray(avatarBytes, 0, avatarBytes.length);
imageView.setImageBitmap(avatar);
AppHelper.avatarsCache.put(url, avatarBytes);
}
});
});
}
}
}

View File

@ -21,10 +21,12 @@ import com.amulyakhare.textdrawable.TextDrawable
import com.amulyakhare.textdrawable.util.ColorGenerator import com.amulyakhare.textdrawable.util.ColorGenerator
import com.google.gson.Gson import com.google.gson.Gson
import com.stfalcon.chatkit.commons.ImageLoader import com.stfalcon.chatkit.commons.ImageLoader
import com.stfalcon.chatkit.messages.MessageHolders
import com.stfalcon.chatkit.messages.MessagesListAdapter import com.stfalcon.chatkit.messages.MessagesListAdapter
import io.github.chronosx88.influence.R import io.github.chronosx88.influence.R
import io.github.chronosx88.influence.contracts.CoreContracts import io.github.chronosx88.influence.contracts.CoreContracts
import io.github.chronosx88.influence.helpers.AppHelper import io.github.chronosx88.influence.helpers.AppHelper
import io.github.chronosx88.influence.helpers.AvatarImageLoader
import io.github.chronosx88.influence.helpers.LocalDBWrapper import io.github.chronosx88.influence.helpers.LocalDBWrapper
import io.github.chronosx88.influence.logic.ChatLogic import io.github.chronosx88.influence.logic.ChatLogic
import io.github.chronosx88.influence.models.GenericMessage import io.github.chronosx88.influence.models.GenericMessage
@ -50,31 +52,9 @@ class ChatPresenter(private val view: CoreContracts.IChatViewContract, private v
this.logic = ChatLogic(LocalDBWrapper.getChatByChatID(chatID)!!) this.logic = ChatLogic(LocalDBWrapper.getChatByChatID(chatID)!!)
this.chatEntity = LocalDBWrapper.getChatByChatID(chatID) this.chatEntity = LocalDBWrapper.getChatByChatID(chatID)
gson = Gson() gson = Gson()
chatAdapter = MessagesListAdapter(AppHelper.getJid(), ImageLoader { imageView, url, _ -> val holdersConfig = MessageHolders()
val firstLetter = Character.toString(Character.toUpperCase(url!!.get(0))) holdersConfig.setIncomingTextLayout(R.layout.item_incoming_text_message_custom)
imageView.setImageDrawable(TextDrawable.builder() chatAdapter = MessagesListAdapter(AppHelper.getJid(), holdersConfig, AvatarImageLoader())
.beginConfig()
.width(64)
.height(64)
.endConfig()
.buildRound(firstLetter, ColorGenerator.MATERIAL.getColor(firstLetter)))
CompletableFuture.supplyAsync { while (AppHelper.getXmppConnection() == null) ;
while (!AppHelper.getXmppConnection().isConnectionAlive) ;
var jid: EntityBareJid? = null
try {
jid = JidCreate.entityBareFrom(url)
} catch (e: XmppStringprepException) {
e.printStackTrace()
}
AppHelper.getXmppConnection().getAvatar(jid) }.thenAccept { avatarBytes -> AppHelper.getMainUIThread().post {
if (avatarBytes != null) {
val avatar = BitmapFactory.decodeByteArray(avatarBytes, 0, avatarBytes.size)
imageView.setImageBitmap(avatar)
}
}
}
})
view.setAdapter(chatAdapter) view.setAdapter(chatAdapter)
EventBus.getDefault().register(this) EventBus.getDefault().register(this)
} }

View File

@ -43,6 +43,7 @@ import java.util.concurrent.ConcurrentHashMap;
import io.github.chronosx88.influence.R; import io.github.chronosx88.influence.R;
import io.github.chronosx88.influence.contracts.CoreContracts; import io.github.chronosx88.influence.contracts.CoreContracts;
import io.github.chronosx88.influence.helpers.AppHelper; import io.github.chronosx88.influence.helpers.AppHelper;
import io.github.chronosx88.influence.helpers.AvatarImageLoader;
import io.github.chronosx88.influence.helpers.LocalDBWrapper; import io.github.chronosx88.influence.helpers.LocalDBWrapper;
import io.github.chronosx88.influence.logic.DialogListLogic; import io.github.chronosx88.influence.logic.DialogListLogic;
import io.github.chronosx88.influence.models.GenericDialog; import io.github.chronosx88.influence.models.GenericDialog;
@ -60,42 +61,7 @@ public class DialogListPresenter implements CoreContracts.IDialogListPresenterCo
private ConcurrentHashMap<String, byte[]> avatarsMap = new ConcurrentHashMap<>(); private ConcurrentHashMap<String, byte[]> avatarsMap = new ConcurrentHashMap<>();
private CoreContracts.IChatListViewContract view; private CoreContracts.IChatListViewContract view;
private CoreContracts.IDialogListLogicContract logic; private CoreContracts.IDialogListLogicContract logic;
private DialogsListAdapter<GenericDialog> dialogListAdapter = new DialogsListAdapter<>(R.layout.item_dialog_custom, (imageView, url, payload) -> { private DialogsListAdapter<GenericDialog> dialogListAdapter = new DialogsListAdapter<>(R.layout.item_dialog_custom, new AvatarImageLoader());
if(url.length() != 0) {
if(avatarsMap.containsKey(url)) {
byte[] avatarBytes = avatarsMap.get(url);
Bitmap avatar = BitmapFactory.decodeByteArray(avatarBytes, 0, avatarBytes.length);
imageView.setImageBitmap(avatar);
return;
}
String firstLetter = Character.toString(Character.toUpperCase(url.charAt(0)));
imageView.setImageDrawable(TextDrawable.builder()
.beginConfig()
.width(64)
.height(64)
.endConfig()
.buildRound(firstLetter, ColorGenerator.MATERIAL.getColor(firstLetter)));
CompletableFuture.supplyAsync(() -> {
while (AppHelper.getXmppConnection() == null);
while (AppHelper.getXmppConnection().isConnectionAlive() != true);
EntityBareJid jid = null;
try {
jid = JidCreate.entityBareFrom(url);
} catch (XmppStringprepException e) {
e.printStackTrace();
}
return AppHelper.getXmppConnection().getAvatar(jid);
}).thenAccept((avatarBytes) -> {
AppHelper.getMainUIThread().post(() -> {
if(avatarBytes != null) {
Bitmap avatar = BitmapFactory.decodeByteArray(avatarBytes, 0, avatarBytes.length);
imageView.setImageBitmap(avatar);
avatarsMap.put(url, avatarBytes);
}
});
});
}
});
private Comparator<GenericDialog> dialogComparator = (dialog1, dialog2) -> Long.compare(dialog2.getLastMessage().getCreatedAt().getTime(), dialog1.getLastMessage().getCreatedAt().getTime()); private Comparator<GenericDialog> dialogComparator = (dialog1, dialog2) -> Long.compare(dialog2.getLastMessage().getCreatedAt().getTime(), dialog1.getLastMessage().getCreatedAt().getTime());
public DialogListPresenter(CoreContracts.IChatListViewContract view) { public DialogListPresenter(CoreContracts.IChatListViewContract view) {
@ -147,7 +113,7 @@ public class DialogListPresenter implements CoreContracts.IDialogListPresenterCo
Intent intent = new Intent(AppHelper.getContext(), ChatActivity.class); Intent intent = new Intent(AppHelper.getContext(), ChatActivity.class);
intent.putExtra("chatID", chatID); intent.putExtra("chatID", chatID);
intent.putExtra("chatName", LocalDBWrapper.getChatByChatID(chatID).chatName); intent.putExtra("chatName", LocalDBWrapper.getChatByChatID(chatID).chatName);
intent.putExtra("chatAvatar", avatarsMap.get(chatID)); intent.putExtra("chatAvatar", AppHelper.avatarsCache.get(chatID));
view.startActivity(intent); view.startActivity(intent);
} }

View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@id/messageUserAvatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"/>
<com.google.android.flexbox.FlexboxLayout
android:id="@id/bubble"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/message_incoming_bubble_margin_right"
android:layout_marginRight="@dimen/message_incoming_bubble_margin_right"
android:layout_toEndOf="@id/messageUserAvatar"
android:layout_toRightOf="@id/messageUserAvatar"
android:orientation="vertical"
app:alignContent="stretch"
app:alignItems="stretch"
app:flexWrap="wrap"
app:justifyContent="flex_end">
<TextView
android:id="@id/messageText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@id/messageTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/messageText"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
app:layout_alignSelf="center"/>
</com.google.android.flexbox.FlexboxLayout>
</RelativeLayout>