-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Persist notification cache to internal storage #23825
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0d201fe
f0d1811
5891d85
9e3452d
43893e2
05cb476
56a1dc1
3bf3bf5
e643107
5ef1f63
43dfbc6
949fb1c
6fa2aac
907fb2e
5170132
17b882d
46687de
e03a51c
dd8f47c
1e06cf0
3704944
6189be7
38f55c0
93fa41e
32f2027
887b1bc
e7188b8
6df512d
f119998
d772757
b396b5e
6b3ff25
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
package com.expensify.chat.customairshipextender; | ||
|
||
import android.content.Context; | ||
import android.graphics.Bitmap; | ||
import android.graphics.BitmapFactory; | ||
import android.util.Base64; | ||
|
||
import androidx.core.app.Person; | ||
import androidx.core.graphics.drawable.IconCompat; | ||
|
||
import com.expensify.chat.MainApplication; | ||
import com.urbanairship.UAirship; | ||
|
||
import java.io.ByteArrayOutputStream; | ||
import java.io.File; | ||
import java.io.FileInputStream; | ||
import java.io.FileOutputStream; | ||
import java.io.IOException; | ||
import java.io.ObjectInputStream; | ||
import java.io.ObjectOutputStream; | ||
import java.io.Serializable; | ||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
|
||
public class NotificationCache { | ||
|
||
private static final String CACHE_FILE_NAME = "notification-cache"; | ||
private static HashMap<String, NotificationData> cache = null; | ||
|
||
/* | ||
* Get NotificationData for an existing notification or create a new instance | ||
* if it doesn't exist | ||
*/ | ||
public static NotificationData getNotificationData(long reportID) { | ||
if (cache == null) { | ||
cache = readFromInternalStorage(); | ||
} | ||
|
||
NotificationData notificationData = cache.get(Long.toString(reportID)); | ||
|
||
if (notificationData == null) { | ||
notificationData = new NotificationData(); | ||
setNotificationData(reportID, notificationData); | ||
} | ||
|
||
return notificationData; | ||
} | ||
|
||
/* | ||
* Set and persist NotificationData in the cache | ||
*/ | ||
public static void setNotificationData(long reportID, NotificationData data) { | ||
arosiclair marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (cache == null) { | ||
cache = readFromInternalStorage(); | ||
} | ||
|
||
cache.put(Long.toString(reportID), data); | ||
writeToInternalStorage(); | ||
arosiclair marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
private static void writeToInternalStorage() { | ||
arosiclair marked this conversation as resolved.
Show resolved
Hide resolved
|
||
Context context = UAirship.getApplicationContext(); | ||
|
||
FileOutputStream fos = null; | ||
ObjectOutputStream oos = null; | ||
try { | ||
File outputFile = new File(context.getFilesDir(), CACHE_FILE_NAME); | ||
fos = new FileOutputStream(outputFile); | ||
oos = new ObjectOutputStream(fos); | ||
oos.writeObject(cache); | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
} finally { | ||
try { | ||
if (oos != null) { | ||
oos.close(); | ||
} | ||
if (fos != null) { | ||
fos.close(); | ||
} | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
} | ||
|
||
private static HashMap<String, NotificationData> readFromInternalStorage() { | ||
HashMap<String, NotificationData> result; | ||
Context context = UAirship.getApplicationContext(); | ||
|
||
FileInputStream fis = null; | ||
ObjectInputStream ois = null; | ||
try { | ||
File fileCache = new File(context.getFilesDir(), CACHE_FILE_NAME); | ||
fis = new FileInputStream(fileCache); | ||
ois = new ObjectInputStream(fis); | ||
result = (HashMap<String, NotificationData>) ois.readObject(); | ||
} catch (IOException | ClassNotFoundException e) { | ||
e.printStackTrace(); | ||
result = new HashMap<>(); | ||
Comment on lines
+99
to
+100
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the case where casting the object to result fails, can we assume it is malformed and wipe it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reading the file could fail in a few ways, but in any case, the file will get overwritten once we're done processing the notification and update the cache |
||
} finally { | ||
try { | ||
if (ois != null) { | ||
ois.close(); | ||
} | ||
if (fis != null) { | ||
fis.close(); | ||
} | ||
} catch (IOException e) { | ||
e.printStackTrace(); | ||
} | ||
} | ||
|
||
return result; | ||
} | ||
|
||
/** | ||
* A class for caching data for notifications. We use this to track active notifications so we | ||
* can thread related notifications together | ||
*/ | ||
public static class NotificationData implements Serializable { | ||
arosiclair marked this conversation as resolved.
Show resolved
Hide resolved
|
||
private final HashMap<String, String> names = new HashMap<>(); | ||
|
||
// A map of accountID => base64 encoded Bitmap | ||
// In order to make Bitmaps serializable, we encode them as base64 strings | ||
private final HashMap<String, String> icons = new HashMap<>(); | ||
public ArrayList<NotificationMessage> messages = new ArrayList<>(); | ||
|
||
public int prevNotificationID = -1; | ||
|
||
public NotificationData() {} | ||
|
||
public Bitmap getIcon(String accountID) { | ||
return decodeToBitmap(icons.get(accountID)); | ||
} | ||
|
||
public void putIcon(String accountID, Bitmap bitmap) { | ||
icons.put(accountID, encodeToBase64(bitmap)); | ||
} | ||
|
||
public Person getPerson(String accountID) { | ||
if (!names.containsKey(accountID) || !icons.containsKey(accountID)) { | ||
return null; | ||
} | ||
|
||
String name = names.get(accountID); | ||
Bitmap icon = getIcon(accountID); | ||
|
||
return new Person.Builder() | ||
.setIcon(IconCompat.createWithBitmap(icon)) | ||
.setKey(accountID) | ||
.setName(name) | ||
.build(); | ||
} | ||
|
||
public void putPerson(String accountID, String name, Bitmap icon) { | ||
names.put(accountID, name); | ||
putIcon(accountID, icon); | ||
} | ||
|
||
public static String encodeToBase64(Bitmap bitmap) { | ||
if (bitmap == null) { | ||
return ""; | ||
} | ||
|
||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); | ||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream); | ||
byte[] byteArray = byteArrayOutputStream.toByteArray(); | ||
return Base64.encodeToString(byteArray, Base64.DEFAULT); | ||
} | ||
|
||
public static Bitmap decodeToBitmap(String base64String) { | ||
if (base64String == null) { | ||
return null; | ||
} | ||
|
||
byte[] decodedBytes = Base64.decode(base64String, Base64.DEFAULT); | ||
return BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length); | ||
} | ||
} | ||
|
||
public static class NotificationMessage implements Serializable { | ||
public String accountID; | ||
public String text; | ||
public long time; | ||
|
||
NotificationMessage(String accountID, String text, long time) { | ||
this.accountID = accountID; | ||
this.text = text; | ||
this.time = time; | ||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.