AndroidTv: add presence
This patch adds a small green icon on online contacts. Easy to custom
with styles.
Change-Id: Ic8543d489301c44e4ce1ab851e6084dca4b5783d
diff --git a/ring-android/app/src/main/java/cx/ring/tv/cards/Card.java b/ring-android/app/src/main/java/cx/ring/tv/cards/Card.java
index 5cd22df..6090f62 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/cards/Card.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/cards/Card.java
@@ -129,7 +129,9 @@
ACCOUNT_ADD_DEVICE,
ABOUT_LICENCES,
CONTACT,
+ CONTACT_ONLINE,
CONTACT_WITH_USERNAME,
+ CONTACT_WITH_USERNAME_ONLINE,
}
}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/tv/cards/CardPresenterSelector.java b/ring-android/app/src/main/java/cx/ring/tv/cards/CardPresenterSelector.java
index 4ce4824..3a3c9e3 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/cards/CardPresenterSelector.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/cards/CardPresenterSelector.java
@@ -58,9 +58,15 @@
case CONTACT:
presenter = new ContactCardPresenter(mContext, R.style.ContactCardTheme);
break;
+ case CONTACT_ONLINE:
+ presenter = new ContactCardPresenter(mContext, R.style.ContactCardOnlineTheme);
+ break;
case CONTACT_WITH_USERNAME:
presenter = new ContactCardPresenter(mContext, R.style.ContactCompleteCardTheme);
break;
+ case CONTACT_WITH_USERNAME_ONLINE:
+ presenter = new ContactCardPresenter(mContext, R.style.ContactCompleteCardOnlineTheme);
+ break;
default:
throw new InvalidParameterException("Uncatched card type");
}
diff --git a/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCard.java b/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCard.java
index ea292a1..d8059dd 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCard.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/cards/contacts/ContactCard.java
@@ -19,15 +19,19 @@
*/
package cx.ring.tv.cards.contacts;
+import java.util.Arrays;
+
import cx.ring.model.CallContact;
+import cx.ring.smartlist.SmartListViewModel;
import cx.ring.tv.cards.Card;
public class ContactCard extends Card {
- CallContact mCallContact = null;
+ SmartListViewModel mModel = null;
+ CallContact mContact = null;
private byte[] mPhoto = null;
public ContactCard(CallContact pCallContact, Type type) {
- mCallContact = pCallContact;
+ mContact = pCallContact;
setId(pCallContact.getId());
setTitle(pCallContact.getDisplayName());
setDescription(pCallContact.getRingUsername());
@@ -37,26 +41,60 @@
setType(type);
}
- public ContactCard(CallContact pCallContact) {
- mCallContact = pCallContact;
- setId(pCallContact.getId());
- setTitle(pCallContact.getDisplayName());
- setDescription(pCallContact.getRingUsername());
- if (pCallContact.getPhoto() != null) {
- mPhoto = pCallContact.getPhoto();
+ public ContactCard(SmartListViewModel model) {
+ mModel = model;
+ setTitle(mModel.getContactName());
+ setDescription(mModel.getUuid());
+ if (mModel.getPhotoData() != null) {
+ mPhoto = mModel.getPhotoData();
}
- if (pCallContact.getDisplayName().equals(pCallContact.getRingUsername())) {
- setType(Type.CONTACT);
+ if (mModel.getContactName().equals(mModel.getUuid())) {
+ if (model.isOnline()) {
+ setType(Type.CONTACT_ONLINE);
+ } else {
+ setType(Type.CONTACT);
+ }
} else {
- setType(Type.CONTACT_WITH_USERNAME);
+ if (model.isOnline()) {
+ setType(Type.CONTACT_WITH_USERNAME_ONLINE);
+ } else {
+ setType(Type.CONTACT_WITH_USERNAME);
+ }
}
}
- public CallContact getCallContact() {
- return mCallContact;
+ public SmartListViewModel getModel() {
+ return mModel;
+ }
+
+ public CallContact getContact() {
+ return mContact;
}
public byte[] getPhoto() {
return mPhoto;
}
+
+ @Override
+ public boolean equals(Object pO) {
+ if (this == pO) return true;
+ if (pO == null || getClass() != pO.getClass()) return false;
+
+ ContactCard that = (ContactCard) pO;
+
+ if (mModel != null )
+ return mModel.getUuid().equals(that.mModel.getUuid());
+ if (mContact != null ? !mContact.equals(that.mContact) : that.mContact != null)
+ return false;
+ return Arrays.equals(mPhoto, that.mPhoto);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mModel != null ? mModel.hashCode() : 0;
+ result = 31 * result + (mContact != null ? mContact.hashCode() : 0);
+ result = 31 * result + Arrays.hashCode(mPhoto);
+ return result;
+ }
}
diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.java b/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.java
index 34d3f51..f77fb5f 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/main/MainFragment.java
@@ -34,7 +34,7 @@
import cx.ring.R;
import cx.ring.application.RingApplication;
-import cx.ring.model.CallContact;
+import cx.ring.smartlist.SmartListViewModel;
import cx.ring.tv.about.AboutActivity;
import cx.ring.tv.account.TVAccountExport;
import cx.ring.tv.call.TVCallActivity;
@@ -177,12 +177,30 @@
}
@Override
- public void showContacts(final ArrayList<CallContact> contacts) {
+ public void refreshContact(final SmartListViewModel contact) {
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ ContactCard updatedCard = new ContactCard(contact);
+ int pos = cardRowAdapter.indexOf(updatedCard);
+ if (pos > -1) {
+ ContactCard previousCard = (ContactCard) cardRowAdapter.get(pos);
+ if (previousCard.getModel().isOnline() != updatedCard.getModel().isOnline()) {
+ cardRowAdapter.replace(pos, updatedCard);
+ mRowsAdapter.notifyArrayItemRangeChanged(pos, 1);
+ }
+ }
+ }
+ });
+ }
+
+ @Override
+ public void showContacts(final ArrayList<SmartListViewModel> contacts) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
cardRowAdapter.clear();
- for (CallContact contact : contacts) {
+ for (SmartListViewModel contact : contacts) {
cardRowAdapter.add(new ContactCard(contact));
}
mRowsAdapter.notifyArrayItemRangeChanged(0, contacts.size());
@@ -224,7 +242,7 @@
RowPresenter.ViewHolder rowViewHolder, Row row) {
if (item instanceof ContactCard) {
- presenter.contactClicked(((ContactCard) item).getCallContact());
+ presenter.contactClicked(((ContactCard) item).getModel());
} else if (item instanceof IconCard) {
IconCard card = (IconCard) item;
switch (card.getType()) {
diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.java b/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.java
index 3879b7d..7bc49d3 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/main/MainPresenter.java
@@ -20,6 +20,7 @@
package cx.ring.tv.main;
import java.util.ArrayList;
+import java.util.Set;
import java.util.concurrent.ExecutorService;
import javax.inject.Inject;
@@ -30,9 +31,12 @@
import cx.ring.model.CallContact;
import cx.ring.model.Conversation;
import cx.ring.model.ServiceEvent;
+import cx.ring.model.Uri;
import cx.ring.mvp.RootPresenter;
import cx.ring.services.AccountService;
import cx.ring.services.ContactService;
+import cx.ring.services.PresenceService;
+import cx.ring.smartlist.SmartListViewModel;
import cx.ring.utils.Observable;
import cx.ring.utils.Observer;
@@ -47,6 +51,8 @@
private ContactService mContactService;
+ private PresenceService mPresenceService;
+
private ExecutorService mExecutor;
private ArrayList<Conversation> mConversations;
@@ -54,10 +60,12 @@
@Inject
public MainPresenter(AccountService accountService,
ContactService contactService,
+ PresenceService presenceService,
@Named("ApplicationExecutor") ExecutorService executor,
ConversationFacade conversationfacade) {
mAccountService = accountService;
mContactService = contactService;
+ mPresenceService = presenceService;
mConversationFacade = conversationfacade;
mExecutor = executor;
mConversations = new ArrayList<>();
@@ -69,6 +77,7 @@
mAccountService.addObserver(this);
mConversationFacade.addObserver(this);
mContactService.addObserver(this);
+ mPresenceService.addObserver(this);
}
@Override
@@ -77,6 +86,7 @@
mAccountService.removeObserver(this);
mConversationFacade.removeObserver(this);
mContactService.removeObserver(this);
+ mPresenceService.removeObserver(this);
}
@Override
@@ -93,6 +103,27 @@
reloadAccountInfos();
break;
}
+ if (observable instanceof PresenceService) {
+ switch (event.getEventType()) {
+ case NEW_BUDDY_NOTIFICATION:
+ refreshContact(
+ event.getString(ServiceEvent.EventInput.BUDDY_URI));
+ break;
+ }
+ }
+ }
+
+ private void refreshContact(String buddy) {
+ for (Conversation conversation : mConversations) {
+ CallContact callContact = conversation.getContact();
+ if (callContact.getIds().get(0).equals("ring:"+buddy)) {
+ SmartListViewModel smartListViewModel = new SmartListViewModel(conversation,
+ callContact.getDisplayName(),
+ callContact.getPhoto());
+ smartListViewModel.setOnline(mPresenceService.isBuddyOnline(callContact.getIds().get(0)));
+ getView().refreshContact(smartListViewModel);
+ }
+ }
}
public void reloadConversations() {
@@ -102,24 +133,30 @@
public void run() {
mConversations.clear();
mConversations.addAll(mConversationFacade.getConversationsList());
- ArrayList<CallContact> contacts = new ArrayList<>();
+ ArrayList<SmartListViewModel> contacts = new ArrayList<>();
if (mConversations != null && mConversations.size() > 0) {
for (int i = 0; i < mConversations.size(); i++) {
Conversation conversation = mConversations.get(i);
CallContact callContact = conversation.getContact();
mContactService.loadContactData(callContact);
- contacts.add(callContact);
+ SmartListViewModel smartListViewModel = new SmartListViewModel(conversation,
+ callContact.getDisplayName(),
+ callContact.getPhoto());
+ smartListViewModel.setOnline(mPresenceService.isBuddyOnline(callContact.getIds().get(0)));
+ contacts.add(smartListViewModel);
}
}
getView().showLoading(false);
getView().showContacts(contacts);
}
});
+
+ subscribePresence();
}
- public void contactClicked(CallContact item) {
+ public void contactClicked(SmartListViewModel item) {
String accountID = mAccountService.getCurrentAccount().getAccountID();
- String ringID = item.getPhones().get(0).getNumber().toString();
+ String ringID = item.getUuid();
getView().callContact(accountID, ringID);
}
@@ -137,4 +174,18 @@
public void onExportClicked() {
getView().showExportDialog(mAccountService.getCurrentAccount().getAccountID());
}
+
+ private void subscribePresence() {
+ if (mAccountService.getCurrentAccount() == null) {
+ return;
+ }
+ String accountId = mAccountService.getCurrentAccount().getAccountID();
+ Set<String> keys = mConversationFacade.getConversations().keySet();
+ for (String key : keys) {
+ Uri uri = new Uri(key);
+ if (uri.isRingId()) {
+ mPresenceService.subscribeBuddy(accountId, key, true);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/tv/main/MainView.java b/ring-android/app/src/main/java/cx/ring/tv/main/MainView.java
index 2125c4a..896dba0 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/main/MainView.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/main/MainView.java
@@ -21,13 +21,15 @@
import java.util.ArrayList;
-import cx.ring.model.CallContact;
+import cx.ring.smartlist.SmartListViewModel;
public interface MainView {
void showLoading(boolean show);
- void showContacts(ArrayList<CallContact> contacts);
+ void refreshContact(SmartListViewModel contact);
+
+ void showContacts(ArrayList<SmartListViewModel> contacts);
void callContact(String accountID, String ringID);
diff --git a/ring-android/app/src/main/java/cx/ring/tv/search/RingSearchFragment.java b/ring-android/app/src/main/java/cx/ring/tv/search/RingSearchFragment.java
index dcc10f7..22eadf7 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/search/RingSearchFragment.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/search/RingSearchFragment.java
@@ -41,7 +41,6 @@
import cx.ring.R;
import cx.ring.application.RingApplication;
import cx.ring.model.CallContact;
-import cx.ring.model.Uri;
import cx.ring.tv.call.TVCallActivity;
import cx.ring.tv.cards.Card;
import cx.ring.tv.cards.CardPresenterSelector;
@@ -137,11 +136,11 @@
}
@Override
- public void startCall(String accountID, Uri number) {
+ public void startCall(String accountID, String number) {
Intent intent = new Intent(getActivity(), TVCallActivity.class);
intent.putExtra("account", accountID);
- intent.putExtra("ringId", number.toString());
- startActivity(intent);
+ intent.putExtra("ringId", number);
+ getActivity().startActivity(intent, null);
getActivity().finish();
}
@@ -149,7 +148,7 @@
@Override
public void onItemClicked(Presenter.ViewHolder itemViewHolder, Object item,
RowPresenter.ViewHolder rowViewHolder, Row row) {
- presenter.contactClicked(((ContactCard) item).getCallContact());
+ presenter.contactClicked(((ContactCard) item).getContact());
}
}
diff --git a/ring-android/app/src/main/java/cx/ring/tv/search/RingSearchPresenter.java b/ring-android/app/src/main/java/cx/ring/tv/search/RingSearchPresenter.java
index 7250b23..d9c1167 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/search/RingSearchPresenter.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/search/RingSearchPresenter.java
@@ -145,6 +145,6 @@
}
public void contactClicked(CallContact contact) {
- getView().startCall(mAccountService.getCurrentAccount().getAccountID(), contact.getPhones().get(0).getNumber());
+ getView().startCall(mAccountService.getCurrentAccount().getAccountID(), contact.getPhones().get(0).getNumber().toString());
}
}
\ No newline at end of file
diff --git a/ring-android/app/src/main/java/cx/ring/tv/search/RingSearchView.java b/ring-android/app/src/main/java/cx/ring/tv/search/RingSearchView.java
index 109d911..c0dbd58 100644
--- a/ring-android/app/src/main/java/cx/ring/tv/search/RingSearchView.java
+++ b/ring-android/app/src/main/java/cx/ring/tv/search/RingSearchView.java
@@ -28,5 +28,5 @@
void clearSearch();
- void startCall(String accountID, Uri number);
+ void startCall(String accountID, String number);
}
diff --git a/ring-android/app/src/main/res/drawable/ic_tv_online_indicator.xml b/ring-android/app/src/main/res/drawable/ic_tv_online_indicator.xml
new file mode 100644
index 0000000..6c7ec0c
--- /dev/null
+++ b/ring-android/app/src/main/res/drawable/ic_tv_online_indicator.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval">
+ <stroke android:width="1px" android:color="#fafafa"/>
+ <solid android:color="#4CAF50" />
+ <size
+ android:width="10dp"
+ android:height="10dp" />
+</shape>
\ No newline at end of file
diff --git a/ring-android/app/src/main/res/values/styles.xml b/ring-android/app/src/main/res/values/styles.xml
index c11ea17..0a0f517 100644
--- a/ring-android/app/src/main/res/values/styles.xml
+++ b/ring-android/app/src/main/res/values/styles.xml
@@ -152,10 +152,16 @@
<style name="ContactTitleViewStyle" parent="DefaultCardStyle">
<item name="lbImageCardViewType">Title</item>
</style>
+ <style name="ContactTitleViewOnlineStyle" parent="DefaultCardStyle">
+ <item name="lbImageCardViewType">Title|Content|IconOnRight</item>
+ </style>
<style name="ContactCompleteCardViewStyle" parent="DefaultCardStyle">
<item name="lbImageCardViewType">Title|Content</item>
</style>
+ <style name="ContactCompleteCardOnlineViewStyle" parent="DefaultCardStyle">
+ <item name="lbImageCardViewType">Title|Content|IconOnRight</item>
+ </style>
<style name="IconCardImageStyle" parent="Widget.Leanback.ImageCardView.ImageStyle">
<item name="android:layout_width">96dp</item>
@@ -208,5 +214,24 @@
<item name="imageCardViewImageStyle">@style/IconCardImageStyle</item>
<item name="imageCardViewInfoAreaStyle">@style/IconCardInfoAreaStyle</item>
</style>
+ <style name="Widget.Leanback.ImageCardView.BadgeStyle">
+ <item name="android:id">@id/extra_badge</item>
+ <item name="android:layout_width">@dimen/lb_basic_card_info_badge_size</item>
+ <item name="android:layout_height">@dimen/lb_basic_card_info_badge_size</item>
+ <item name="android:contentDescription">@null</item>
+ <item name="android:scaleType">fitCenter</item>
+ </style>
+ <style name="OnlineBadgeStyle" parent="Widget.Leanback.ImageCardView.BadgeStyle">
+ <item name="android:src">@drawable/ic_tv_online_indicator</item>
+ </style>
+ <style name="ContactCardOnlineTheme" parent="ContactCardTheme">
+ <item name="imageCardViewBadgeStyle">@style/OnlineBadgeStyle</item>
+ <item name="imageCardViewStyle">@style/ContactCompleteCardViewStyle</item>
+ </style>
+
+ <style name="ContactCompleteCardOnlineTheme" parent="ContactCompleteCardTheme">
+ <item name="imageCardViewStyle">@style/ContactTitleViewOnlineStyle</item>
+ <item name="imageCardViewBadgeStyle">@style/OnlineBadgeStyle</item>
+ </style>
</resources>
\ No newline at end of file