+ * For backwards compatibility {@code extras} holds some references to "real" member data such
+ * as {@link getLargeIcon()} which is mirrored by {@link #EXTRA_LARGE_ICON}. This is mostly
+ * fine as long as the object stays in one process.
+ *
+ * However, once the notification goes into a parcel each reference gets marshalled separately,
+ * wasting memory. Especially with large images on Auto and TV, this is worth fixing.
+ */
+ private void fixDuplicateExtras() {
+ if (extras != null) {
+ fixDuplicateExtra(mSmallIcon, EXTRA_SMALL_ICON);
+ fixDuplicateExtra(mLargeIcon, EXTRA_LARGE_ICON);
+ }
+ }
+
+ /**
+ * If we find an extra that's exactly the same as one of the "real" fields but refers to a
+ * separate object, replace it with the field's version to avoid holding duplicate copies.
+ */
+ private void fixDuplicateExtra(@Nullable Parcelable original, @NonNull String extraName) {
+ if (original != null && extras.getParcelable(extraName) != null) {
+ extras.putParcelable(extraName, original);
+ }
+ }
+
/**
* Sets the {@link #contentView} field to be a view with the standard "Latest Event"
* layout.
@@ -5926,9 +5966,10 @@ public class Notification implements Parcelable
public static final int MAXIMUM_RETAINED_MESSAGES = 25;
CharSequence mUserDisplayName;
- CharSequence mConversationTitle;
+ @Nullable CharSequence mConversationTitle;
List mMessages = new ArrayList<>();
List mHistoricMessages = new ArrayList<>();
+ boolean mIsGroupConversation;
MessagingStyle() {
}
@@ -5951,20 +5992,20 @@ public class Notification implements Parcelable
}
/**
- * Sets the title to be displayed on this conversation. This should only be used for
- * group messaging and left unset for one-on-one conversations.
- * @param conversationTitle
+ * Sets the title to be displayed on this conversation. May be set to {@code null}.
+ *
+ * @param conversationTitle A name for the conversation, or {@code null}
* @return this object for method chaining.
*/
- public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
+ public MessagingStyle setConversationTitle(@Nullable CharSequence conversationTitle) {
mConversationTitle = conversationTitle;
return this;
}
/**
- * Return the title to be displayed on this conversation. Can be null
and
- * should be for one-on-one conversations
+ * Return the title to be displayed on this conversation. May return {@code null}.
*/
+ @Nullable
public CharSequence getConversationTitle() {
return mConversationTitle;
}
@@ -6040,6 +6081,24 @@ public class Notification implements Parcelable
return mHistoricMessages;
}
+ /**
+ * Sets whether this conversation notification represents a group.
+ * @param isGroupConversation {@code true} if the conversation represents a group,
+ * {@code false} otherwise.
+ * @return this object for method chaining
+ */
+ public MessagingStyle setGroupConversation(boolean isGroupConversation) {
+ mIsGroupConversation = isGroupConversation;
+ return this;
+ }
+
+ /**
+ * Returns {@code true} if this notification represents a group conversation.
+ */
+ public boolean isGroupConversation() {
+ return mIsGroupConversation;
+ }
+
/**
* @hide
*/
@@ -6060,6 +6119,7 @@ public class Notification implements Parcelable
}
fixTitleAndTextExtras(extras);
+ extras.putBoolean(EXTRA_IS_GROUP_CONVERSATION, mIsGroupConversation);
}
private void fixTitleAndTextExtras(Bundle extras) {
@@ -6102,6 +6162,7 @@ public class Notification implements Parcelable
mMessages = Message.getMessagesFromBundleArray(messages);
Parcelable[] histMessages = extras.getParcelableArray(EXTRA_HISTORIC_MESSAGES);
mHistoricMessages = Message.getMessagesFromBundleArray(histMessages);
+ mIsGroupConversation = extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION);
}
/**
diff --git a/android/app/SharedPreferencesImpl.java b/android/app/SharedPreferencesImpl.java
index 8c47598f..6dca4004 100644
--- a/android/app/SharedPreferencesImpl.java
+++ b/android/app/SharedPreferencesImpl.java
@@ -50,6 +50,11 @@ import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
final class SharedPreferencesImpl implements SharedPreferences {
private static final String TAG = "SharedPreferencesImpl";
@@ -69,15 +74,11 @@ final class SharedPreferencesImpl implements SharedPreferences {
private final Object mLock = new Object();
private final Object mWritingToDiskLock = new Object();
- @GuardedBy("mLock")
- private Map mMap;
+ private Future