aboutsummaryrefslogtreecommitdiff
path: root/wearable/wear/WearMessagingApp
diff options
context:
space:
mode:
authorBenjamin Baxter <benbaxter@google.com>2017-03-22 16:03:01 -0700
committerBenjamin Baxter <benbaxter@google.com>2017-04-20 19:50:46 -0700
commitbf82f6671608e5abc123c501cbe2709ceea9b739 (patch)
treeef957504bb65bb54ed4121dfb3a4245794682a14 /wearable/wear/WearMessagingApp
parent2635b32fcaaaf2eccb4c177dd45bcfb48bccfa2f (diff)
downloadandroid-bf82f6671608e5abc123c501cbe2709ceea9b739.tar.gz
Adding chat list module
Bug: 34841755 Change-Id: I9a4472519fa959c814396dbe65e1bfdd7c773e03
Diffstat (limited to 'wearable/wear/WearMessagingApp')
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/GoogleSignedInActivity.java2
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/SignInActivity.java5
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/chat/ChatAdapter.java2
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/chatlist/ChatListActivity.java135
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/chatlist/ChatListAdapter.java249
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/res/layout/activity_chat_list.xml35
-rw-r--r--wearable/wear/WearMessagingApp/Wearable/src/main/res/layout/chat_list_item.xml75
7 files changed, 500 insertions, 3 deletions
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/GoogleSignedInActivity.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/GoogleSignedInActivity.java
index 494c9c7b..8bad9f8e 100644
--- a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/GoogleSignedInActivity.java
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/GoogleSignedInActivity.java
@@ -21,6 +21,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.wearable.activity.WearableActivity;
import android.util.Log;
+import com.example.android.wearable.wear.messaging.mock.MockDatabase;
import com.example.android.wearable.wear.messaging.model.Profile;
import com.example.android.wearable.wear.messaging.util.SharedPreferencesHelper;
import com.google.android.gms.auth.api.Auth;
@@ -57,6 +58,7 @@ public abstract class GoogleSignedInActivity extends WearableActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setAmbientEnabled();
+ MockDatabase.init(this);
// Try to get the user if they don't exist, return to sign in.
try {
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/SignInActivity.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/SignInActivity.java
index af3d74be..0189930c 100644
--- a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/SignInActivity.java
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/SignInActivity.java
@@ -23,6 +23,7 @@ import android.support.wearable.activity.WearableActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
+import com.example.android.wearable.wear.messaging.chatlist.ChatListActivity;
import com.example.android.wearable.wear.messaging.mock.MockDatabase;
import com.example.android.wearable.wear.messaging.model.Profile;
import com.google.android.gms.auth.api.Auth;
@@ -185,8 +186,8 @@ public class SignInActivity extends WearableActivity
mSignInButton.setEnabled(false);
}
-// Intent chatActivityIntent = new Intent(this, ChatListActivity.class);
-// startActivity(chatActivityIntent);
+ Intent chatActivityIntent = new Intent(this, ChatListActivity.class);
+ startActivity(chatActivityIntent);
finish();
}
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/chat/ChatAdapter.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/chat/ChatAdapter.java
index 6e2831b8..5cca0872 100644
--- a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/chat/ChatAdapter.java
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/chat/ChatAdapter.java
@@ -143,7 +143,7 @@ class ChatAdapter extends WearableRecyclerView.Adapter<ChatAdapter.MessageViewHo
}
/**
- * Converts time since epoch to Month Date Time
+ * Converts time since epoch to Month Date Time.
*
* @param time since epoch
* @return String formatted in Month Date HH:MM
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/chatlist/ChatListActivity.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/chatlist/ChatListActivity.java
new file mode 100644
index 00000000..56727d8d
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/chatlist/ChatListActivity.java
@@ -0,0 +1,135 @@
+/*
+ * 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.chatlist;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.wearable.activity.WearableActivity;
+import android.support.wearable.view.WearableRecyclerView;
+import android.util.Log;
+import com.example.android.wearable.wear.messaging.GoogleSignedInActivity;
+import com.example.android.wearable.wear.messaging.R;
+import com.example.android.wearable.wear.messaging.chat.ChatActivity;
+import com.example.android.wearable.wear.messaging.contacts.ContactsListActivity;
+import com.example.android.wearable.wear.messaging.mock.MockDatabase;
+import com.example.android.wearable.wear.messaging.model.Chat;
+import com.example.android.wearable.wear.messaging.model.Profile;
+import com.example.android.wearable.wear.messaging.util.Constants;
+import com.example.android.wearable.wear.messaging.util.DividerItemDecoration;
+import com.example.android.wearable.wear.messaging.util.SharedPreferencesHelper;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * Displays list of active chats of user.
+ *
+ * <p>Uses a simple mocked backend solution with shared preferences.
+ *
+ * TODO: Processes database activities on the UI thread, move to async.
+ */
+public class ChatListActivity extends GoogleSignedInActivity {
+
+ private static final String TAG = "ChatListActivity";
+
+ // Triggered by contact selection in ContactsListActivity.
+ private static final int CONTACTS_SELECTED_REQUEST_CODE = 9004;
+
+ private WearableRecyclerView mRecyclerView;
+ private ChatListAdapter mRecyclerAdapter;
+ private Profile mUser;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setTheme(R.style.BlueTheme);
+ setContentView(R.layout.activity_chat_list);
+
+ mRecyclerView = (WearableRecyclerView) findViewById(R.id.recycler_view);
+
+ mRecyclerView.addItemDecoration(new DividerItemDecoration(this, R.drawable.divider));
+ LinearLayoutManager layoutManager = new LinearLayoutManager(this);
+ mRecyclerView.setLayoutManager(layoutManager);
+ mRecyclerView.setHasFixedSize(true);
+
+ mRecyclerAdapter = new ChatListAdapter(this, new MyChatListAdapterListener(this));
+ mRecyclerView.setAdapter(mRecyclerAdapter);
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Log.d(TAG, "onStart");
+
+ // Try to get the user if they don't exist, return to login.
+ try {
+ mUser = SharedPreferencesHelper.readUserFromJsonPref(this);
+ } catch (IOException e) {
+ Log.e(TAG, "User is not stored locally");
+ }
+ if (mUser == null) {
+ onGoogleSignInFailure();
+ } else {
+ mRecyclerAdapter.setChats(MockDatabase.getAllChats());
+ }
+ }
+
+ /**
+ * A handler for the adapter to launch the correct activities based on the type of item selected
+ */
+ private class MyChatListAdapterListener implements ChatListAdapter.ChatAdapterListener {
+
+ private final WearableActivity activity;
+
+ MyChatListAdapterListener(WearableActivity activity) {
+ this.activity = activity;
+ }
+
+ @Override
+ public void newChatSelected() {
+ Intent intent = new Intent(activity, ContactsListActivity.class);
+ activity.startActivityForResult(intent, CONTACTS_SELECTED_REQUEST_CODE);
+ }
+
+ @Override
+ public void openChat(Chat chat) {
+ Intent startChat = new Intent(activity, ChatActivity.class);
+ startChat.putExtra(Constants.EXTRA_CHAT, chat);
+ activity.startActivity(startChat);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == CONTACTS_SELECTED_REQUEST_CODE) {
+ // Selected contacts confirmed.
+ if (resultCode == RESULT_OK) {
+ ArrayList<Profile> contacts =
+ data.getParcelableArrayListExtra(Constants.RESULT_CONTACTS_KEY);
+ // TODO: this should be moved to the background,
+ Chat newChatWithSelectedContacts = MockDatabase.createChat(contacts, getUser());
+
+ Log.d(TAG, String.format("Starting chat with %d contact(s)", contacts.size()));
+
+ // Launch ChatActivity with new chat.
+ Intent startChat = new Intent(this, ChatActivity.class);
+ startChat.putExtra(Constants.EXTRA_CHAT, newChatWithSelectedContacts);
+ startActivity(startChat);
+ }
+ }
+ }
+}
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/chatlist/ChatListAdapter.java b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/chatlist/ChatListAdapter.java
new file mode 100644
index 00000000..7fb8edb4
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/java/com/example/android/wearable/wear/messaging/chatlist/ChatListAdapter.java
@@ -0,0 +1,249 @@
+/*
+ * 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.chatlist;
+
+import android.content.Context;
+import android.support.v7.util.SortedList;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import com.bumptech.glide.Glide;
+import com.example.android.wearable.wear.messaging.R;
+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 de.hdodenhof.circleimageview.CircleImageView;
+import java.util.Collection;
+
+/**
+ * Adapter for list of chats.
+ *
+ * <p>If chat is empty, displays icon to start a new chat. Otherwise, activity displays the title of
+ * chat as first element followed by the rest of the conversation.
+ *
+ * <p>The RecyclerView holds the title so it can scroll off the screen as a user scrolls down the
+ * page.
+ */
+public class ChatListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+
+ private static final String TAG = "ChatListAdapter";
+
+ private static final int TYPE_ADD = 0;
+ private static final int TYPE_CONTENT = 1;
+
+ private static final int INDEX_OFFSET = 1;
+
+ private final Context context;
+ private final ChatAdapterListener listener;
+ private final SortedList<Chat> mChats;
+
+ public ChatListAdapter(Context context, ChatAdapterListener listener) {
+ this.context = context;
+ this.listener = listener;
+ mChats =
+ new SortedList<>(
+ Chat.class,
+ new SortedList.Callback<Chat>() {
+
+ // Descending list based on chat's last message time.
+ @Override
+ public int compare(Chat chat1, Chat chat2) {
+ Long diff =
+ (chat2.getLastMessage().getSentTime()
+ - chat1.getLastMessage().getSentTime());
+ return diff.intValue();
+ }
+
+ @Override
+ public void onInserted(int position, int count) {
+ notifyItemRangeInserted(position + INDEX_OFFSET, count);
+ }
+
+ @Override
+ public void onRemoved(int position, int count) {
+ notifyItemRangeRemoved(position + INDEX_OFFSET, count);
+ }
+
+ @Override
+ public void onMoved(int fromPosition, int toPosition) {
+ notifyItemMoved(
+ fromPosition + INDEX_OFFSET, toPosition + INDEX_OFFSET);
+ }
+
+ @Override
+ public void onChanged(int position, int count) {
+ Log.d(
+ TAG,
+ String.format("Item changed %d", position + INDEX_OFFSET));
+ // Since position 0 is the title
+ notifyItemRangeChanged(position + INDEX_OFFSET, count);
+ }
+
+ @Override
+ public boolean areContentsTheSame(Chat oldItem, Chat newItem) {
+ return oldItem.equals(newItem);
+ }
+
+ @Override
+ public boolean areItemsTheSame(Chat item1, Chat item2) {
+ return item1.getId().equals(item2.getId());
+ }
+ });
+ }
+
+ /**
+ * A listener for a client to receive events about which actionable items occurred in the
+ * adapter; i.e., either the user wants a brand new chat started or they are opening an existing
+ * chat.
+ */
+ public interface ChatAdapterListener {
+ void newChatSelected();
+
+ void openChat(Chat chat);
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (position == 0) {
+ return TYPE_ADD;
+ }
+ return TYPE_CONTENT;
+ }
+
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ if (viewType == TYPE_ADD) {
+ return new AddChatViewHolder(
+ LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.action_new_chat, parent, false));
+ } else {
+ return new ChatItemViewHolder(
+ LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.chat_list_item, parent, false));
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
+
+ if (holder instanceof AddChatViewHolder) {
+ ((AddChatViewHolder) holder)
+ .row.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Log.d(TAG, "New chat has been selected");
+ listener.newChatSelected();
+ }
+ });
+ } else if (holder instanceof ChatItemViewHolder) {
+ final ChatItemViewHolder chatItemViewHolder = (ChatItemViewHolder) holder;
+
+ //need to account for having the add button before the list of mChats
+ final Chat chat = mChats.get(position - INDEX_OFFSET);
+
+ chatItemViewHolder.row.setOnClickListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ listener.openChat(chat);
+ }
+ });
+
+ chatItemViewHolder.alias.setText(chat.getAlias());
+
+ Message lastMessage = chat.getLastMessage();
+ if (lastMessage != null) {
+ Profile lastMessageSender;
+ if (chat.getParticipants().size() == 1) {
+ lastMessageSender = chat.getParticipants().values().iterator().next();
+ } else {
+ lastMessageSender = chat.getParticipants().get(lastMessage.getSenderId());
+ }
+
+ if (lastMessageSender == null) {
+ chatItemViewHolder.aliasImage.setImageResource(R.drawable.ic_face_white_24dp);
+ // Blank out any text that may be left after updating the view holder contents.
+ chatItemViewHolder.lastMessage.setText("");
+ } else {
+ String lastMessageText = lastMessage.getText();
+ String messageString = lastMessageSender.getName() + ": " + lastMessageText;
+
+ chatItemViewHolder.lastMessage.setText(messageString);
+
+ Glide.with(context)
+ .load(lastMessageSender.getProfileImageUri())
+ .placeholder(R.drawable.ic_face_white_24dp)
+ .into(chatItemViewHolder.aliasImage);
+ }
+ }
+
+ // Show group icon if more than 1 participant
+ int numParticipants = chat.getParticipants().size();
+ if (numParticipants > 1) {
+ chatItemViewHolder.aliasImage.setImageResource(R.drawable.ic_group_white_48dp);
+ }
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return mChats.size() + INDEX_OFFSET;
+ }
+
+ public void setChats(final Collection<Chat> chats) {
+ // There is a bug with {@link SortedList#addAll} that will add new items to the end
+ // of the list allowing duplications in the view which is unexpected behavior
+ // https://code.google.com/p/android/issues/detail?id=201618
+ // so we will mimic the add all operation (add individually but execute in one batch)
+ mChats.beginBatchedUpdates();
+ for (Chat chat : chats) {
+ mChats.add(chat);
+ }
+ mChats.endBatchedUpdates();
+ }
+
+ private class AddChatViewHolder extends RecyclerView.ViewHolder {
+
+ private final ViewGroup row;
+
+ AddChatViewHolder(View itemView) {
+ super(itemView);
+
+ row = (ViewGroup) itemView.findViewById(R.id.percent_layout);
+ }
+ }
+
+ private class ChatItemViewHolder extends RecyclerView.ViewHolder {
+
+ private ViewGroup row;
+ private final TextView alias;
+ private final CircleImageView aliasImage;
+ private final TextView lastMessage;
+
+ ChatItemViewHolder(View itemView) {
+ super(itemView);
+
+ row = (ViewGroup) itemView.findViewById(R.id.layout_chat_list_item);
+ alias = (TextView) itemView.findViewById(R.id.text_alias);
+ aliasImage = (CircleImageView) itemView.findViewById(R.id.profile);
+ lastMessage = (TextView) itemView.findViewById(R.id.text_last_message);
+ }
+ }
+}
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/res/layout/activity_chat_list.xml b/wearable/wear/WearMessagingApp/Wearable/src/main/res/layout/activity_chat_list.xml
new file mode 100644
index 00000000..e703e1b4
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/res/layout/activity_chat_list.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/mainFrameLayout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="com.example.android.wearable.wear.messaging.chatlist.ChatListActivity">
+
+ <android.support.wearable.view.WearableRecyclerView
+ android:id="@+id/recycler_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@null"
+ android:clipToPadding="false"
+ android:paddingBottom="@dimen/list_item_height"
+ android:paddingTop="@dimen/list_item_height"
+ android:scrollbars="vertical" />
+
+</FrameLayout> \ No newline at end of file
diff --git a/wearable/wear/WearMessagingApp/Wearable/src/main/res/layout/chat_list_item.xml b/wearable/wear/WearMessagingApp/Wearable/src/main/res/layout/chat_list_item.xml
new file mode 100644
index 00000000..8135586a
--- /dev/null
+++ b/wearable/wear/WearMessagingApp/Wearable/src/main/res/layout/chat_list_item.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<android.support.percent.PercentRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/layout_chat_list_item"
+ android:paddingTop="@dimen/vertical_spacing"
+ android:paddingBottom="@dimen/vertical_spacing"
+ android:layout_marginBottom="@dimen/divider"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <RelativeLayout
+ android:id="@+id/profile_layout"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ app:layout_marginStartPercent="@dimen/padding_15">
+
+ <de.hdodenhof.circleimageview.CircleImageView
+ android:id="@+id/profile"
+ android:layout_width="@dimen/circle_image_diameter"
+ android:layout_height="@dimen/circle_image_diameter"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ tools:src="@drawable/ic_face_white_24dp"
+ app:circle_border_color="@color/blue_15"
+ app:circle_border_width="2dp"
+ app:layout_marginStartPercent="@dimen/padding_15"
+ android:layout_centerInParent="true" />
+
+ </RelativeLayout>
+
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_toEndOf="@id/profile_layout"
+ android:orientation="vertical"
+ android:layout_marginStart="@dimen/keyline_large"
+ app:layout_marginEndPercent="@dimen/padding_10">
+ <TextView
+ android:id="@+id/text_alias"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:includeFontPadding="false"
+ android:maxLines="1"
+ tools:text="Alias"
+ android:layout_marginBottom="@dimen/vertical_spacing_small"/>
+ <TextView
+ android:id="@+id/text_last_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:includeFontPadding="false"
+ android:maxLines="1"
+ tools:text="@string/holder_text" />
+ </LinearLayout>
+</android.support.percent.PercentRelativeLayout> \ No newline at end of file