aboutsummaryrefslogtreecommitdiff
path: root/wearable/wear/WearMessagingApp
diff options
context:
space:
mode:
authorBenjamin Baxter <benbaxter@google.com>2017-03-22 15:56:48 -0700
committerBenjamin Baxter <benbaxter@google.com>2017-04-14 15:50:11 -0700
commit2264fb2a775cb1a5ba84d1550dfe5f1ac388cbce (patch)
tree86b214f784050d86baefb410228130fc32802c0a /wearable/wear/WearMessagingApp
parent06feed786f96cb11cb374645efadcec5b8a900d3 (diff)
downloadandroid-2264fb2a775cb1a5ba84d1550dfe5f1ac388cbce.tar.gz
Adding model, repository and shared preference helper
Bug: 34841755 Change-Id: Ibe7b2908b98aa24b32a6f6d5f44f0533fe545bf3
Diffstat (limited to 'wearable/wear/WearMessagingApp')
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/mock/MockDatabase.java321
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/mock/MockObjectGenerator.java67
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/model/Chat.java235
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/model/Message.java182
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/model/Profile.java230
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/Constants.java50
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/DividerItemDecoration.java4
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/MenuTinter.java4
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/PrescrollToBottom.java6
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/SharedPreferencesHelper.java198
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/android_logo.pngbin0 -> 3095 bytes
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/ben.pngbin0 -> 49312 bytes
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/default_profile.pngbin0 -> 4389 bytes
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/jane.pngbin0 -> 57180 bytes
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/jennifer.pngbin0 -> 45387 bytes
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/jeremy.pngbin0 -> 34251 bytes
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/lisa.pngbin0 -> 56121 bytes
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/paul.pngbin0 -> 48810 bytes
18 files changed, 1287 insertions, 10 deletions
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/mock/MockDatabase.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/mock/MockDatabase.java
new file mode 100644
index 00000000..1b0d91a8
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/mock/MockDatabase.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 2017 Google Inc.
+ *
+ * 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 com.example.android.wearable.wear.messaging.mock;
+
+import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.Log;
+import com.example.android.wearable.wear.messaging.model.Chat;
+import com.example.android.wearable.wear.messaging.model.Message;
+import com.example.android.wearable.wear.messaging.model.Profile;
+import com.example.android.wearable.wear.messaging.util.SharedPreferencesHelper;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+/** Mock database stores data in {@link android.content.SharedPreferences} */
+public class MockDatabase {
+
+ private static final String TAG = "MockDatabase";
+
+ private static Context mContext;
+
+ /** A callback for events when retrieving a user asynchronously */
+ interface RetrieveUserCallback {
+ void retrieved(Profile user);
+
+ void error(Exception e);
+ }
+
+ /** A callback for getting events during the creation of a user */
+ interface CreateUserCallback {
+ void onSuccess();
+
+ void onError(Exception e);
+ }
+
+ /**
+ * Initializes the mock database with a context for use with {@link
+ * android.content.SharedPreferences}
+ */
+ public static void init(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Creates a chat and stores it in the mock database
+ *
+ * @param participants of the chat
+ * @param user that has started the chat
+ * @return a chat with information attached to it
+ */
+ public static Chat createChat(Collection<Profile> participants, Profile user) {
+ int size = participants.size();
+ Log.d(TAG, String.format("Creating a new chat with %d participant(s)", size));
+
+ Chat chat = new Chat();
+
+ // Initializes chat's last message to a blank String.
+ Message message = new Message.Builder().senderId(user.getId()).text("").build();
+
+ chat.setLastMessage(message);
+
+ Map<String, Profile> participantMap = new HashMap<>();
+ for (Profile profile : participants) {
+ participantMap.put(profile.getId(), profile);
+ }
+ chat.setParticipantsAndAlias(participantMap);
+
+ // Create an id for the chat based on the aggregate of the participants' ids
+ chat.setId(concat(participantMap.keySet()));
+
+ // If you start a new chat with someone you already have a chat with, reuse that chat
+ Collection<Chat> allChats = getAllChats();
+ Chat exists = findChat(allChats, chat.getId());
+ if (exists != null) {
+ chat = exists;
+ } else {
+ allChats.add(chat);
+ }
+
+ persistsChats(allChats);
+
+ return chat;
+ }
+
+ private static void persistsChats(Collection<Chat> chats) {
+ try {
+ SharedPreferencesHelper.writeChatsToJsonPref(mContext, new ArrayList<>(chats));
+ } catch (JsonProcessingException e) {
+ Log.e(TAG, "Could not write chats to json", e);
+ }
+ }
+
+ @Nullable
+ private static Chat findChat(Collection<Chat> chats, String chatId) {
+ for (Chat c : chats) {
+ if (c.getId().equals(chatId)) {
+ return c;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns all of the chats stored in {@link android.content.SharedPreferences}. An empty {@link
+ * Collection<Chat>} will be returned if preferences cannot be read from or cannot parse the
+ * json object.
+ *
+ * @return a collection of chats
+ */
+ public static Collection<Chat> getAllChats() {
+
+ try {
+ return SharedPreferencesHelper.readChatsFromJsonPref(mContext);
+ } catch (IOException e) {
+ Log.e(TAG, "Could not read/unmarshall the list of chats from shared preferences", e);
+ return Collections.emptyList();
+ }
+ }
+
+ /**
+ * Returns a {@link Chat} object with a given id.
+ *
+ * @param id of the stored chat
+ * @return chat with id or null if no chat has that id
+ */
+ @Nullable
+ public static Chat findChatById(String id) {
+ return findChat(getAllChats(), id);
+ }
+
+ /**
+ * Updates the {@link Chat#lastMessage} field in the stored json object.
+ *
+ * @param chat to be updated.
+ * @param lastMessage to be updated on the chat.
+ */
+ public static void updateLastMessage(Chat chat, Message lastMessage) {
+ Collection<Chat> chats = getAllChats();
+ // Update reference of chat to what it is the mock database.
+ chat = findChat(chats, chat.getId());
+ if (chat != null) {
+ chat.setLastMessage(lastMessage);
+ }
+
+ // Save all chats since share prefs are managing them as one entity instead of individually.
+ persistsChats(chats);
+ }
+
+ /**
+ * Flattens the collection of strings into a single string. For example,
+ *
+ * <p>Input: ["a", "b", "c"]
+ *
+ * <p>Output: "abc"
+ *
+ * @param collection to be flattened into a string
+ * @return a concatenated string
+ */
+ @NonNull
+ private static String concat(Collection<String> collection) {
+ Set<String> participantIds = new TreeSet<>(collection);
+ StringBuilder sb = new StringBuilder();
+ for (String id : participantIds) {
+ sb.append(id);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Saves the message to the thread of messages for the given chat. The message's sent time will
+ * also be updated to preserve order.
+ *
+ * @param chat that the message should be added to.
+ * @param message that was sent in the chat.
+ * @return message with {@link Message#sentTime} updated
+ */
+ public static Message saveMessage(Chat chat, Message message) {
+
+ message.setSentTime(System.currentTimeMillis());
+
+ updateLastMessage(chat, message);
+
+ Collection<Message> messages = getAllMessagesForChat(chat.getId());
+ messages.add(message);
+
+ try {
+ SharedPreferencesHelper.writeMessagesForChatToJsonPref(
+ mContext, chat, new ArrayList<>(messages));
+ } catch (JsonProcessingException e) {
+ Log.e(TAG, "Could not write the list of messages to shared preferences", e);
+ }
+
+ return message;
+ }
+
+ /**
+ * Returns all messages related to a given chat.
+ *
+ * @param chatId of the conversation
+ * @return messages in the conversation
+ */
+ public static Collection<Message> getAllMessagesForChat(String chatId) {
+ try {
+ return SharedPreferencesHelper.readMessagesForChat(mContext, chatId);
+ } catch (IOException e) {
+ Log.e(TAG, "Could not read/unmarshall the list of messages from shared preferences", e);
+ return Collections.emptyList();
+ }
+ }
+
+ /**
+ * Returns message details for a message in a particular chat.
+ *
+ * @param chatId that the message is in
+ * @param messageId of the message to be found in the chat
+ * @return message from a chat
+ */
+ @Nullable
+ public static Message findMessageById(String chatId, String messageId) {
+ for (Message message : getAllMessagesForChat(chatId)) {
+ if (message.getId().equals(messageId)) {
+ return message;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Generates a set of predefined dummy contacts. You may need to add in extra logic for
+ * timestamp changes between server and local app.
+ *
+ * @return a list of profiles to be used as contacts
+ */
+ public static List<Profile> getUserContacts() {
+
+ try {
+ List<Profile> contacts = SharedPreferencesHelper.readContactsFromJsonPref(mContext);
+ if (!contacts.isEmpty()) {
+ return contacts;
+ }
+ } catch (IOException e) {
+ String logMessage =
+ "Could not read/unmarshall the list of contacts from shared preferences. "
+ + "Returning mock contacts.";
+ Log.e(TAG, logMessage, e);
+ }
+
+ // Cannot find contacts so we will persist and return a default set of contacts.
+ List<Profile> defaultContacts = MockObjectGenerator.generateDefaultContacts();
+ try {
+ Log.d(TAG, "saving default contacts");
+ SharedPreferencesHelper.writeContactsToJsonPref(mContext, defaultContacts);
+ } catch (JsonProcessingException e) {
+ Log.e(TAG, "Could not write the list of contacts to shared preferences", e);
+ }
+ return defaultContacts;
+ }
+
+ /**
+ * Returns the user asynchronously to the client via a callback.
+ *
+ * @param id for a user
+ * @param callback used for handling asynchronous responses
+ */
+ public static void getUser(String id, RetrieveUserCallback callback) {
+ Profile user = null;
+ try {
+ user = SharedPreferencesHelper.readUserFromJsonPref(mContext);
+ } catch (IOException e) {
+ Log.e(TAG, "Could not read/unmarshall the user from shared preferences.", e);
+ callback.error(e);
+ }
+ if (user != null && user.getId().equals(id)) {
+ callback.retrieved(user);
+ } else {
+ // Could not find user with that id.
+ callback.retrieved(null);
+ }
+ }
+
+ /**
+ * Creates a user asynchronously and notifies the client if a user has been created successfully
+ * or if there were any errors.
+ *
+ * @param user that needs to be created
+ * @param callback used for handling asynchronous responses
+ */
+ public static void createUser(Profile user, CreateUserCallback callback) {
+ try {
+ SharedPreferencesHelper.writeUserToJsonPref(mContext, user);
+ callback.onSuccess();
+ } catch (JsonProcessingException e) {
+ Log.e(TAG, "Could not write the user to shared preferences", e);
+ //Let the client know that the user cannot be saved
+ callback.onError(e);
+ }
+ }
+}
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/mock/MockObjectGenerator.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/mock/MockObjectGenerator.java
new file mode 100644
index 00000000..b99871df
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/mock/MockObjectGenerator.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * 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 com.example.android.wearable.wear.messaging.mock;
+
+import android.support.annotation.NonNull;
+import com.example.android.wearable.wear.messaging.R;
+import com.example.android.wearable.wear.messaging.model.Profile;
+import java.util.Arrays;
+import java.util.List;
+
+/** Helper methods to generate mock objects. */
+public class MockObjectGenerator {
+
+ /**
+ * Returns a list of mocked contacts.
+ *
+ * @return a {@link List<Profile>} that can be used to mock out a user's contact list.
+ */
+ public static List<Profile> generateDefaultContacts() {
+
+ Profile paul = buildProfile("1234", "Paul Saxman", "PaulSaxman@email.com", R.drawable.paul);
+
+ Profile ben =
+ buildProfile("2345", "Benjamin Baxter", "benjaminbaxter@email.com", R.drawable.ben);
+
+ Profile jeremy =
+ buildProfile("3456", "Jeremy Walker", "jeremywalker@email.com", R.drawable.jeremy);
+
+ Profile jennifer =
+ buildProfile(
+ "4567", "Jennifer Smith", "jennifersmith@email.com", R.drawable.jennifer);
+
+ Profile android =
+ buildProfile("5678", "Android", "android@email.com", R.drawable.android_logo);
+
+ Profile lisa =
+ buildProfile("6789", "Lisa Williams", "lisawilliams@email.com", R.drawable.lisa);
+
+ Profile jane = buildProfile("7890", "Jane Doe", "janedoe@email.com", R.drawable.jane);
+
+ return Arrays.asList(paul, jennifer, ben, lisa, jane, jeremy, android);
+ }
+
+ @NonNull
+ private static Profile buildProfile(String id, String name, String email, int profileResource) {
+ return new Profile.Builder()
+ .id(id)
+ .name(name)
+ .email(email)
+ .profileImageResource(profileResource)
+ .lastUpdatedTime(System.currentTimeMillis())
+ .build();
+ }
+}
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/model/Chat.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/model/Chat.java
new file mode 100644
index 00000000..7138e638
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/model/Chat.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * 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 com.example.android.wearable.wear.messaging.model;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/** Data representation of a chat. */
+public class Chat implements Parcelable {
+ private String id;
+ private String alias;
+
+ // Map using User's id as a hash for profile details for everyone in the conversation.
+ private Map<String, Profile> participants;
+ //TODO: remove me if not being used
+ private Map<String, String> lastReadMessages;
+
+ private Message lastMessage;
+
+ public Chat() {
+ id = UUID.randomUUID().toString();
+ }
+
+ public Chat(String id) {
+ this.id = id;
+ }
+
+ public String getAlias() {
+ return alias;
+ }
+
+ public void setAlias(String alias) {
+ this.alias = alias;
+ }
+
+ public Map<String, Profile> getParticipants() {
+ return participants;
+ }
+
+ public void setParticipants(Map<String, Profile> participants) {
+ this.participants = participants;
+ }
+
+ private void generateAliasFromParticipants() {
+ StringBuilder participantNames = new StringBuilder();
+
+ // Iterates through each participants then generates a comma separated string as the alias.
+ for (Profile profile : participants.values()) {
+ participantNames.append(profile.getName()).append(",");
+ }
+ participantNames.setLength(participantNames.length() - 1); // Remove last comma (,)
+
+ this.alias = participantNames.toString();
+ }
+
+ /**
+ * Sets the participants and adds an alias. The alias is displayed in the chat list.
+ *
+ * @param participants chat participants
+ */
+ public void setParticipantsAndAlias(Map<String, Profile> participants) {
+ setParticipants(participants);
+ generateAliasFromParticipants();
+ }
+
+ /**
+ * Sets the participants and specified alias
+ *
+ * @param participants chat participants
+ * @param alias chat alias
+ */
+ public void setParticipantsAndAlias(Map<String, Profile> participants, String alias) {
+ setParticipants(participants);
+ this.alias = alias;
+ }
+
+ public Map<String, String> getLastReadMessages() {
+ return lastReadMessages;
+ }
+
+ public void setLastReadMessages(Map<String, String> lastReadMessages) {
+ this.lastReadMessages = lastReadMessages;
+ }
+
+ public void addParticipant(Profile participant) {
+ participants.put(participant.getId(), participant);
+ }
+
+ public Message getLastMessage() {
+ return lastMessage;
+ }
+
+ public void setLastMessage(Message lastMessage) {
+ this.lastMessage = lastMessage;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(this.id);
+ dest.writeString(this.alias);
+ dest.writeParcelable(this.lastMessage, 0);
+ dest.writeInt(this.participants != null ? this.participants.size() : -1);
+ for (Map.Entry<String, Profile> entry : this.participants.entrySet()) {
+ dest.writeString(entry.getKey());
+ dest.writeParcelable(entry.getValue(), flags);
+ }
+
+ dest.writeInt(this.lastReadMessages != null ? this.lastReadMessages.size() : -1);
+ if (this.lastReadMessages != null) {
+ for (Map.Entry<String, String> entry : this.lastReadMessages.entrySet()) {
+ dest.writeString(entry.getKey());
+ dest.writeString(entry.getValue());
+ }
+ }
+ }
+
+ protected Chat(Parcel in) {
+ this.id = in.readString();
+ this.alias = in.readString();
+ this.lastMessage = in.readParcelable(Message.class.getClassLoader());
+
+ int participantsSize = in.readInt();
+ if (participantsSize != -1) {
+ this.participants = new HashMap<>(participantsSize);
+ for (int i = 0; i < participantsSize; i++) {
+ String key = in.readString();
+ Profile value = in.readParcelable(Profile.class.getClassLoader());
+ this.participants.put(key, value);
+ }
+ }
+
+ int lastReadMessagesSize = in.readInt();
+ if (lastReadMessagesSize != -1) {
+ this.lastReadMessages = new HashMap<>(lastReadMessagesSize);
+ for (int i = 0; i < lastReadMessagesSize; i++) {
+ String key = in.readString();
+ String value = in.readString();
+ this.lastReadMessages.put(key, value);
+ }
+ }
+ }
+
+ public static final Parcelable.Creator<Chat> CREATOR =
+ new Parcelable.Creator<Chat>() {
+ @Override
+ public Chat createFromParcel(Parcel source) {
+ return new Chat(source);
+ }
+
+ @Override
+ public Chat[] newArray(int size) {
+ return new Chat[size];
+ }
+ };
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ *
+ * @param other - the reference object with which to compare.
+ * @return true/false based on all fields being equal or equally null
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+
+ Chat chat = (Chat) other;
+
+ if (!id.equals(chat.id)) {
+ return false;
+ }
+ if (alias != null ? !alias.equals(chat.alias) : chat.alias != null) {
+ return false;
+ }
+ if (!participants.equals(chat.participants)) {
+ return false;
+ }
+ if (lastReadMessages != null
+ ? !lastReadMessages.equals(chat.lastReadMessages)
+ : chat.lastReadMessages != null) {
+ return false;
+ }
+ return lastMessage != null
+ ? lastMessage.equals(chat.lastMessage)
+ : chat.lastMessage == null;
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ *
+ * @return a hash code value for this object.
+ */
+ @Override
+ public int hashCode() {
+ int result = id.hashCode();
+ result = 31 * result + (alias != null ? alias.hashCode() : 0);
+ result = 31 * result + participants.hashCode();
+ result = 31 * result + (lastReadMessages != null ? lastReadMessages.hashCode() : 0);
+ result = 31 * result + (lastMessage != null ? lastMessage.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/model/Message.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/model/Message.java
new file mode 100644
index 00000000..95f7b834
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/model/Message.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * 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 com.example.android.wearable.wear.messaging.model;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** Data representation of each message. Stores the sender, text content, and the sent time. */
+public class Message implements Parcelable {
+ private String id;
+ private String senderId;
+ private String name;
+ private String text;
+ private long sentTime;
+
+ public Message() {}
+
+ public Message(Message message, String name) {
+ senderId = message.getSenderId();
+ this.name = name;
+ text = message.getText();
+ }
+
+ private Message(Builder builder) {
+ id = builder.id;
+ senderId = builder.senderId;
+ text = builder.text;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getSenderId() {
+ return senderId;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public long getSentTime() {
+ return sentTime;
+ }
+
+ public void setSentTime(long sentTime) {
+ this.sentTime = sentTime;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /** Message Builder */
+ public static final class Builder {
+ private String id;
+ private String senderId;
+ private String text;
+
+ public Builder() {}
+
+ public Builder id(String val) {
+ id = val;
+ return this;
+ }
+
+ public Builder senderId(String val) {
+ senderId = val;
+ return this;
+ }
+
+ public Builder text(String val) {
+ text = val;
+ return this;
+ }
+
+ public Message build() {
+ return new Message(this);
+ }
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ *
+ * @param other - the reference object with which to compare.
+ * @return true/false based on all fields being equal or equally null
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+
+ Message message = (Message) other;
+
+ if (sentTime != message.sentTime) {
+ return false;
+ }
+ if (id != null ? !id.equals(message.id) : message.id != null) {
+ return false;
+ }
+ if (!senderId.equals(message.senderId)) {
+ return false;
+ }
+ if (name != null ? !name.equals(message.name) : message.name != null) {
+ return false;
+ }
+ return text != null ? text.equals(message.text) : message.text == null;
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ *
+ * @return a hash code value for this object.
+ */
+ @Override
+ public int hashCode() {
+ int result = id != null ? id.hashCode() : 0;
+ result = 31 * result + senderId.hashCode();
+ result = 31 * result + (name != null ? name.hashCode() : 0);
+ result = 31 * result + (text != null ? text.hashCode() : 0);
+ result = 31 * result + (int) (sentTime ^ (sentTime >>> 32));
+ return result;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(this.senderId);
+ dest.writeString(this.name);
+ dest.writeString(this.text);
+ dest.writeLong(this.sentTime);
+ }
+
+ protected Message(Parcel in) {
+ this.senderId = in.readString();
+ this.name = in.readString();
+ this.text = in.readString();
+ this.sentTime = in.readLong();
+ }
+
+ public static final Creator<Message> CREATOR =
+ new Creator<Message>() {
+ @Override
+ public Message createFromParcel(Parcel source) {
+ return new Message(source);
+ }
+
+ @Override
+ public Message[] newArray(int size) {
+ return new Message[size];
+ }
+ };
+}
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/model/Profile.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/model/Profile.java
new file mode 100644
index 00000000..7a687496
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/model/Profile.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * 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 com.example.android.wearable.wear.messaging.model;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
+import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
+
+/** Represents a user profile. Parcelable to pass between activities. */
+public class Profile implements Parcelable {
+
+ private String id;
+ private String email;
+ private String name;
+ private String profileImageUri;
+ private int profileImageResource;
+ private Long lastUpdatedTime;
+
+ public Profile() {}
+
+ public Profile(@NonNull GoogleSignInAccount account) {
+ this.id = account.getId();
+ this.email = account.getEmail();
+ this.name = account.getDisplayName();
+ this.profileImageUri =
+ (account.getPhotoUrl() != null) ? account.getPhotoUrl().toString() : null;
+ this.lastUpdatedTime = System.currentTimeMillis();
+ }
+
+ public Profile(String id, String name, String imageUri) {
+ this.id = id;
+ this.name = name;
+ this.profileImageUri = imageUri;
+ this.lastUpdatedTime = System.currentTimeMillis();
+ }
+
+ private Profile(Builder builder) {
+ setId(builder.id);
+ setEmail(builder.email);
+ setName(builder.name);
+ setProfileImageResource(builder.profileImageResource);
+ setLastUpdatedTime(builder.lastUpdatedTime);
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getProfileImageResource() {
+ return profileImageResource;
+ }
+
+ public void setProfileImageResource(int profileImageResource) {
+ this.profileImageResource = profileImageResource;
+ }
+
+ public void setProfileImageUri(String profileImageUri) {
+ this.profileImageUri = profileImageUri;
+ }
+
+ public String getProfileImageUri() {
+ return profileImageUri;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public void setEmail(String email) {
+ this.email = email;
+ }
+
+ public Long getLastUpdatedTime() {
+ return lastUpdatedTime;
+ }
+
+ public void setLastUpdatedTime(Long lastUpdatedTime) {
+ this.lastUpdatedTime = lastUpdatedTime;
+ }
+
+ /**
+ * Indicates whether some other object is "equal to" this one.
+ *
+ * @param other - the reference object with which to compare.
+ * @return true/false based on all fields being equal or equally null
+ */
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (other == null || getClass() != other.getClass()) {
+ return false;
+ }
+
+ Profile profile = (Profile) other;
+
+ if (!id.equals(profile.id)) {
+ return false;
+ }
+ if (email != null ? !email.equals(profile.email) : profile.email != null) {
+ return false;
+ }
+ if (!name.equals(profile.name)) {
+ return false;
+ }
+ if (!profileImageUri.equals(profile.profileImageUri)) {
+ return false;
+ }
+ return lastUpdatedTime != null
+ ? lastUpdatedTime.equals(profile.lastUpdatedTime)
+ : profile.lastUpdatedTime == null;
+ }
+
+ /**
+ * Returns a hash code value for the object.
+ *
+ * @return a hash code value for this object.
+ */
+ @Override
+ public int hashCode() {
+ int result = id.hashCode();
+ result = 31 * result + (email != null ? email.hashCode() : 0);
+ result = 31 * result + name.hashCode();
+ result = 31 * result + profileImageUri.hashCode();
+ result = 31 * result + (lastUpdatedTime != null ? lastUpdatedTime.hashCode() : 0);
+ return result;
+ }
+
+ /** Builder crates profiles. */
+ public static final class Builder {
+ private String id;
+ private String email;
+ private String name;
+ private int profileImageResource;
+ private Long lastUpdatedTime;
+
+ public Builder() {}
+
+ public Builder id(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public Builder email(String email) {
+ this.email = email;
+ return this;
+ }
+
+ public Builder name(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Builder profileImageResource(int profileImageResource) {
+ this.profileImageResource = profileImageResource;
+ return this;
+ }
+
+ public Builder lastUpdatedTime(Long lastUpdatedTime) {
+ this.lastUpdatedTime = lastUpdatedTime;
+ return this;
+ }
+
+ public Profile build() {
+ return new Profile(this);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(this.id);
+ dest.writeString(this.email);
+ dest.writeString(this.name);
+ dest.writeString(this.profileImageUri);
+ dest.writeValue(this.lastUpdatedTime);
+ }
+
+ protected Profile(Parcel in) {
+ this.id = in.readString();
+ this.email = in.readString();
+ this.name = in.readString();
+ this.profileImageUri = in.readString();
+ this.lastUpdatedTime = (Long) in.readValue(Long.class.getClassLoader());
+ }
+
+ public static final Creator<Profile> CREATOR =
+ new Creator<Profile>() {
+ @Override
+ public Profile createFromParcel(Parcel source) {
+ return new Profile(source);
+ }
+
+ @Override
+ public Profile[] newArray(int size) {
+ return new Profile[size];
+ }
+ };
+}
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/Constants.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/Constants.java
new file mode 100644
index 00000000..e99414a4
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/Constants.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * 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 com.example.android.wearable.wear.messaging.util;
+
+/**
+ * All Constants for interaction with the Mock Database, extras between activities, and actions for
+ * intents.
+ */
+public class Constants {
+
+ /* Constants for Extras to be passed along through bundles */
+ public static final String EXTRA_CHAT =
+ "com.example.android.wearable.wear.messaging.extra.CHAT";
+ public static final String EXTRA_MESSAGE =
+ "com.example.android.wearable.wear.messaging.extra.MESSAGE";
+ public static final String EXTRA_REPLY =
+ "com.example.android.wearable.wear.messaging.extra.REPLY";
+
+ /* Shared Preference keys for SharedPreferencesHelper */
+ static final String PREFS_NAME = "com.example.android.wearable.wear.messaging";
+ static final String PREFS_CHAT_ID_KEY =
+ "com.example.android.wearable.wear.messaging.prefs.CHAT_ID";
+ static final String PREFS_USER_KEY = "com.example.android.wearable.wear.messaging.prefs.USER";
+ static final String PREFS_CONTACTS_KEY =
+ "com.example.android.wearable.wear.messaging.prefs.CONTACTS";
+ static final String PREFS_CHATS_KEY = "com.example.android.wearable.wear.messaging.prefs.CHATS";
+ static final String PREFS_MESSAGE_PREFIX = "chat_message_";
+
+ public static final String RESULT_CONTACTS_KEY =
+ "com.example.android.wear.samples.sup.result.CONTACTS_RESULT";
+
+ /* Action constants for services/broadcast receiver intents */
+ static final String ACTION_RECEIVE_MESSAGE =
+ "com.example.android.wearable.wear.messaging.action.RECEIVE_MESSAGE";
+ public static final String ACTION_REPLY =
+ "com.example.android.wearable.wear.messaging.action.REPLY_MESSAGE";
+}
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/DividerItemDecoration.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/DividerItemDecoration.java
index 7ea10de0..6927216c 100644
--- a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/DividerItemDecoration.java
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/DividerItemDecoration.java
@@ -22,9 +22,7 @@ import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.view.View;
-/**
- * Recycler view divider.
- */
+/** Recycler view divider. */
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private Drawable mDrawable;
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/MenuTinter.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/MenuTinter.java
index bad1df3a..08f23cdf 100644
--- a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/MenuTinter.java
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/MenuTinter.java
@@ -22,9 +22,7 @@ import android.support.v4.graphics.drawable.DrawableCompat;
import android.view.Menu;
import android.view.MenuItem;
-/**
- * Helper method to tint menu item icons.
- */
+/** Helper method to tint menu item icons. */
public class MenuTinter {
private MenuTinter() {}
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/PrescrollToBottom.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/PrescrollToBottom.java
index 3b98c5bc..91b7693f 100644
--- a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/PrescrollToBottom.java
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/PrescrollToBottom.java
@@ -19,9 +19,7 @@ import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.ViewTreeObserver;
-/**
- * Prescrolls to the last element in the adapter before draw and removes itself.
- */
+/** Prescrolls to the last element in the adapter before draw and removes itself. */
public class PrescrollToBottom implements ViewTreeObserver.OnPreDrawListener {
private static final String TAG = "PrescrollToBottom";
@@ -46,4 +44,4 @@ public class PrescrollToBottom implements ViewTreeObserver.OnPreDrawListener {
mRecyclerView.getViewTreeObserver().removeOnPreDrawListener(this);
return !shouldScroll;
}
-} \ No newline at end of file
+}
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/SharedPreferencesHelper.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/SharedPreferencesHelper.java
new file mode 100644
index 00000000..3caa5edb
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/util/SharedPreferencesHelper.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2017 Google Inc.
+ *
+ * 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 com.example.android.wearable.wear.messaging.util;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.Log;
+import com.example.android.wearable.wear.messaging.model.Chat;
+import com.example.android.wearable.wear.messaging.model.Message;
+import com.example.android.wearable.wear.messaging.model.Profile;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * SharedPreferencesHelper provides static methods to set, get, delete these objects.
+ *
+ * <p>The user's profile details and chat details are persisted in SharedPreferences to access
+ * across the app.
+ */
+public class SharedPreferencesHelper {
+
+ private static final String TAG = "SharedPreferencesHelper";
+
+ private static ObjectMapper mMapper = new ObjectMapper();
+
+ /**
+ * Returns logged in user or null if no user is logged in.
+ *
+ * @param context shared preferences context
+ * @return user profile
+ * @throws IOException
+ */
+ public static Profile readUserFromJsonPref(Context context) throws IOException {
+ SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.PREFS_NAME, 0);
+ String profileString = sharedPreferences.getString(Constants.PREFS_USER_KEY, null);
+ return profileString != null ? mMapper.readValue(profileString, Profile.class) : null;
+ }
+
+ /**
+ * Writes a {@link Profile} to json and stores it in preferences.
+ *
+ * @param context used to access {@link SharedPreferences}
+ * @param user to be stored in preferences
+ * @throws JsonProcessingException if object cannot be written to preferences
+ */
+ public static void writeUserToJsonPref(Context context, Profile user)
+ throws JsonProcessingException {
+ SharedPreferences sharedPreferences = context.getSharedPreferences(Constants.PREFS_NAME, 0);
+ SharedPreferences.Editor editor = sharedPreferences.edit();
+ editor.putString(Constants.PREFS_USER_KEY, mMapper.writeValueAsString(user));
+ editor.apply();
+ }
+
+ /**
+ * Reads contacts from preferences.
+ *
+ * @param context used to access {@link SharedPreferences}
+ * @return contacts from preferences
+ * @throws IOException if there is an error parsing the json from preferences
+ */
+ public static List<Profile> readContactsFromJsonPref(Context context) throws IOException {
+ return getList(context, Profile.class, Constants.PREFS_CONTACTS_KEY);
+ }
+
+ /**
+ * Writes a {@link List<Profile>} to json and stores it in preferences.
+ *
+ * @param context used to access {@link SharedPreferences}
+ * @param contacts to be stored in preferences
+ * @throws JsonProcessingException if objects cannot be marshalled to json
+ */
+ public static void writeContactsToJsonPref(Context context, List<Profile> contacts)
+ throws JsonProcessingException {
+ setList(context, contacts, Constants.PREFS_CONTACTS_KEY);
+ }
+
+ /**
+ * Reads chats from preferences
+ *
+ * @param context used to access {@link SharedPreferences}
+ * @return chats from preferences
+ * @throws IOException if there is an error parsing the json from preferences
+ */
+ public static List<Chat> readChatsFromJsonPref(Context context) throws IOException {
+ return getList(context, Chat.class, Constants.PREFS_CHATS_KEY);
+ }
+
+ /**
+ * Writes a {@link List<Chat>} to json and stores it in preferences.
+ *
+ * @param context used to access {@link SharedPreferences}
+ * @param chats to be stores in preferences
+ * @throws JsonProcessingException if objects cannot be marshalled to json
+ */
+ public static void writeChatsToJsonPref(Context context, List<Chat> chats)
+ throws JsonProcessingException {
+ Log.d(TAG, String.format("Saving %d chat(s)", chats.size()));
+ setList(context, chats, Constants.PREFS_CHATS_KEY);
+ }
+
+ /**
+ * Reads messages for a chat from preferences.
+ *
+ * @param context used to access {@link SharedPreferences}
+ * @param chatId for the chat the messages are from
+ * @return messages from preferences
+ * @throws IOException if there is an error parsing the json from preferences
+ */
+ public static List<Message> readMessagesForChat(Context context, String chatId)
+ throws IOException {
+ return getList(context, Message.class, Constants.PREFS_MESSAGE_PREFIX + chatId);
+ }
+
+ /**
+ * Writes a {@link List<Message>} to json and stores it in preferences.
+ *
+ * @param context used to access {@link SharedPreferences}
+ * @param chat that the messages are from
+ * @param messages to be stored into preferences
+ * @throws JsonProcessingException if objects cannot be marshalled to json
+ */
+ public static void writeMessagesForChatToJsonPref(
+ Context context, Chat chat, List<Message> messages) throws JsonProcessingException {
+ setList(context, messages, Constants.PREFS_MESSAGE_PREFIX + chat.getId());
+ }
+
+ /**
+ * Returns List of specified class from SharedPreferences (converts from string in
+ * SharedPreferences to class)
+ *
+ * @param context used for getting an instance of shared preferences
+ * @param clazz the class that the strings will be unmarshalled into
+ * @param key the key in shared preferences to access the string set
+ * @param <T> the type of object that will be in the returned list, should be the same as the
+ * clazz that was supplied
+ * @return a list of <T> objects that were stored in shared preferences. Returns an empty list
+ * if no data is available.
+ * @throws IOException if the string cannot unmarshall into the object <T>
+ */
+ private static <T> List<T> getList(Context context, Class<T> clazz, String key)
+ throws IOException {
+ SharedPreferences sharedPreferences =
+ context.getSharedPreferences(Constants.PREFS_NAME, Context.MODE_PRIVATE);
+ Set<String> contactsSet = sharedPreferences.getStringSet(key, new HashSet<String>());
+ if (contactsSet.isEmpty()) {
+ // Favoring mutability of the list over Collections.emptyList().
+ return new ArrayList<>();
+ }
+ List<T> list = new ArrayList<>(contactsSet.size());
+ for (String contactString : contactsSet) {
+ list.add(mMapper.readValue(contactString, clazz));
+ }
+ return list;
+ }
+
+ /**
+ * Sets a List of specified class in SharedPreferences (converts from List of class to string
+ * for SharedPreferences)
+ *
+ * @param context used for getting an instance of shared preferences
+ * @param list of <T> object that need to be persisted
+ * @param key the key in shared preferences which the string set will be stored
+ * @param <T> type the of object we will be marshalling and persisting
+ * @throws JsonProcessingException if the object cannot be written to a string
+ */
+ private static <T> void setList(Context context, List<T> list, String key)
+ throws JsonProcessingException {
+ SharedPreferences sharedPreferences =
+ context.getSharedPreferences(Constants.PREFS_NAME, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sharedPreferences.edit();
+
+ Set<String> strings = new LinkedHashSet<>(list.size());
+ for (T t : list) {
+ strings.add(mMapper.writeValueAsString(t));
+ }
+ editor.putStringSet(key, strings);
+ editor.apply();
+ }
+}
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/android_logo.png b/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/android_logo.png
new file mode 100644
index 00000000..53f59c69
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/android_logo.png
Binary files differ
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/ben.png b/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/ben.png
new file mode 100644
index 00000000..676eb94f
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/ben.png
Binary files differ
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/default_profile.png b/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/default_profile.png
new file mode 100644
index 00000000..a57b044c
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/default_profile.png
Binary files differ
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/jane.png b/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/jane.png
new file mode 100644
index 00000000..cbf4a056
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/jane.png
Binary files differ
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/jennifer.png b/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/jennifer.png
new file mode 100644
index 00000000..3097ce12
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/jennifer.png
Binary files differ
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/jeremy.png b/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/jeremy.png
new file mode 100644
index 00000000..92204dbf
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/jeremy.png
Binary files differ
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/lisa.png b/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/lisa.png
new file mode 100644
index 00000000..efbdb5d3
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/lisa.png
Binary files differ
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/paul.png b/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/paul.png
new file mode 100644
index 00000000..31554da2
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/res/drawable/paul.png
Binary files differ