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 5cd22dff32eb94779f90074dec4b7f92f1793cfa..6090f62567e27607b1bb190daf4c1707c20f4983 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 @@ public class Card { 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 4ce4824552c4e243b4279da230d12238bbafdb1a..3a3c9e3758ca96fa8ff053170b4477562007adac 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 @@ public class CardPresenterSelector extends PresenterSelector { 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 ea292a15787afec5d900e544404e643e6f29681c..d8059dd4080113aadd2d489ae37ded72462a8746 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 @@ public class ContactCard extends Card { 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 34d3f51e57c2f7405537d71484ac46b8c79b49ee..f77fb5f6f698066bc77a09433882c7ff8162a044 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 java.util.List; 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 @@ public class MainFragment extends BaseBrowseFragment<MainPresenter> implements M } @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 @@ public class MainFragment extends BaseBrowseFragment<MainPresenter> implements M 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 3879b7d9c6326085561a356123b41bfbef6d60b1..7bc49d3d8095c020ff3af5f0162b3ed41d2bb80b 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.Account; 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 @@ public class MainPresenter extends RootPresenter<MainView> implements Observer<S private ContactService mContactService; + private PresenceService mPresenceService; + private ExecutorService mExecutor; private ArrayList<Conversation> mConversations; @@ -54,10 +60,12 @@ public class MainPresenter extends RootPresenter<MainView> implements Observer<S @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 @@ public class MainPresenter extends RootPresenter<MainView> implements Observer<S mAccountService.addObserver(this); mConversationFacade.addObserver(this); mContactService.addObserver(this); + mPresenceService.addObserver(this); } @Override @@ -77,6 +86,7 @@ public class MainPresenter extends RootPresenter<MainView> implements Observer<S mAccountService.removeObserver(this); mConversationFacade.removeObserver(this); mContactService.removeObserver(this); + mPresenceService.removeObserver(this); } @Override @@ -93,6 +103,27 @@ public class MainPresenter extends RootPresenter<MainView> implements Observer<S 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 class MainPresenter extends RootPresenter<MainView> implements Observer<S 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 class MainPresenter extends RootPresenter<MainView> implements Observer<S 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 2125c4a13195acff53167a6773356249dfd8904d..896dba02e0458dfa97f12490948aac9068a5c83b 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 @@ package cx.ring.tv.main; 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 dcc10f7764befa3fdd6a38b5f2712f35a18bd92c..22eadf724cba52295da5f6173293e8375de1e633 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 butterknife.Unbinder; 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 @@ public class RingSearchFragment extends BaseSearchFragment<RingSearchPresenter> } @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 @@ public class RingSearchFragment extends BaseSearchFragment<RingSearchPresenter> @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 7250b237d36506b5079a495c483c9a18d799d733..d9c1167122d311340e1c1c8fa81af6c4514d04fb 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 class RingSearchPresenter extends RootPresenter<RingSearchView> implement } 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 109d9117af5bc7db9611b1eada3f62053edbd6e4..c0dbd583562bd37fbf3ab7559785103664e20b23 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 @@ public interface RingSearchView { 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 0000000000000000000000000000000000000000..6c7ec0c55d33b4f196e271edc56950d14468c103 --- /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 c11ea17bd5bb82df65355e77673897821e32df5e..0a0f51736d3ff13ac3fc70af7a6454891a9f32c0 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