Commit 483ae847 authored by Rafael Kellermann Streit's avatar Rafael Kellermann Streit Committed by GitHub

Merge pull request #452 from RocketChat/develop

[RELEASE] Merge 1.0.18 release into master
parents 3beec3e6 d773477b
# Rocket.Chat Android native application
[![CircleCI](https://circleci.com/gh/RocketChat/Rocket.Chat.Android/tree/develop.svg?style=shield)](https://circleci.com/gh/RocketChat/Rocket.Chat.Android/tree/develop) [![Build Status](https://travis-ci.org/RocketChat/Rocket.Chat.Android.svg?branch=develop)](https://travis-ci.org/RocketChat/Rocket.Chat.Android) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a81156a8682e4649994270d3670c3c83)](https://www.codacy.com/app/matheusjardimb/Rocket.Chat.Android)
# Rocket.Chat.Android
Rocket.Chat Native Android Application.
# Get it from Google Play
[![Rocket.Chat on Google Play](https://user-images.githubusercontent.com/551004/29770692-a20975c6-8bc6-11e7-8ab0-1cde275496e0.png)](https://play.google.com/store/apps/details?id=chat.rocket.android)
## How to build
......
......@@ -45,8 +45,8 @@ android {
applicationId "chat.rocket.android"
minSdkVersion 16
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 31
versionName "1.0.17"
versionCode 36
versionName "1.0.18"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
multiDexEnabled true
......
{
"project_info": {
"project_number": "361979207101",
"firebase_url": "https://rocketchatandroid-92e1e.firebaseio.com",
"project_id": "rocketchatandroid-92e1e",
"storage_bucket": "rocketchatandroid-92e1e.appspot.com"
"project_number": "1020987621558",
"firebase_url": "https://rocketchatnative.firebaseio.com",
"project_id": "rocketchatnative",
"storage_bucket": "rocketchatnative.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:361979207101:android:16da2e50aff9f0c9",
"mobilesdk_app_id": "1:1020987621558:android:16da2e50aff9f0c9",
"android_client_info": {
"package_name": "chat.rocket.android"
}
},
"oauth_client": [
{
"client_id": "361979207101-68jt4s85vqfidsgtb0jircio1s4l0la6.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "chat.rocket.android",
"certificate_hash": "5540F34145397BBDE62DEE1433BE7FF9D991D5A2"
}
},
{
"client_id": "361979207101-tvvl8a3s98vd933svlepieo81mul17da.apps.googleusercontent.com",
"client_id": "1020987621558-trk61fjrahho0ujtjap095p1jmi48pfq.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyCjInoCWiVXbC02aKgBPeH3EqiHGt6vGyE"
"current_key": "AIzaSyDc7VYUdU6kRkoRTToiCn1rh-W0wJvhLWk"
}
],
"services": {
......@@ -37,13 +29,8 @@
"status": 1
},
"appinvite_service": {
"status": 2,
"other_platform_oauth_client": [
{
"client_id": "361979207101-tvvl8a3s98vd933svlepieo81mul17da.apps.googleusercontent.com",
"client_type": 3
}
]
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
......
......@@ -30,6 +30,7 @@ import hugo.weaving.DebugLog;
public class MainActivity extends AbstractAuthedActivity implements MainContract.View {
private RoomToolbar toolbar;
private StatusTicker statusTicker;
private SlidingPaneLayout pane;
private MainContract.Presenter presenter;
@Override
......@@ -43,7 +44,8 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
setContentView(R.layout.activity_main);
toolbar = (RoomToolbar) findViewById(R.id.activity_main_toolbar);
statusTicker = new StatusTicker();
setupSidebar();
pane = (SlidingPaneLayout) findViewById(R.id.sliding_pane);
setupToolbar();
}
@Override
......@@ -63,52 +65,34 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
super.onPause();
}
private void setupSidebar() {
SlidingPaneLayout pane = (SlidingPaneLayout) findViewById(R.id.sliding_pane);
if (pane == null) {
return;
}
final SlidingPaneLayout subPane = (SlidingPaneLayout) findViewById(R.id.sub_sliding_pane);
pane.setPanelSlideListener(new SlidingPaneLayout.SimplePanelSlideListener() {
@Override
public void onPanelClosed(View panel) {
super.onPanelClosed(panel);
if (subPane != null) {
subPane.closePane();
}
}
});
toolbar.setNavigationOnClickListener(view -> {
if (pane.isSlideable() && !pane.isOpen()) {
pane.openPane();
}
});
//ref: ActionBarDrawerToggle#setProgress
private void setupToolbar() {
pane.setPanelSlideListener(new SlidingPaneLayout.PanelSlideListener() {
@Override
public void onPanelSlide(View panel, float slideOffset) {
toolbar.setNavigationIconProgress(slideOffset);
public void onPanelSlide(View view, float v) {
//Ref: ActionBarDrawerToggle#setProgress
toolbar.setNavigationIconProgress(v);
}
@Override
public void onPanelOpened(View panel) {
public void onPanelOpened(View view) {
toolbar.setNavigationIconVerticalMirror(true);
}
@Override
public void onPanelClosed(View panel) {
public void onPanelClosed(View view) {
toolbar.setNavigationIconVerticalMirror(false);
closeUserActionContainer();
}
});
toolbar.setNavigationOnClickListener(view -> {
if (pane.isSlideable() && !pane.isOpen()) {
pane.openPane();
}
});
}
private boolean closeSidebarIfNeeded() {
// REMARK: Tablet UI doesn't have SlidingPane!
SlidingPaneLayout pane = (SlidingPaneLayout) findViewById(R.id.sliding_pane);
if (pane != null && pane.isSlideable() && pane.isOpen()) {
pane.closePane();
return true;
......@@ -156,14 +140,6 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
.commit();
}
private void closeUserActionContainer() {
SidebarMainFragment sidebarFragment = (SidebarMainFragment) getSupportFragmentManager()
.findFragmentById(R.id.sidebar_fragment_container);
if (sidebarFragment != null) {
sidebarFragment.closeUserActionContainer();
}
}
@Override
protected void onRoomIdUpdated() {
super.onRoomIdUpdated();
......
......@@ -483,7 +483,11 @@ public class MethodCallHelper {
final JSONObject result = task.getResult();
JSONArray roomJsonArray = (JSONArray) result.get("rooms");
if (roomJsonArray.length() > 0) {
int roomTotal = roomJsonArray.length();
if (roomTotal > 0) {
for (int i = 0; i < roomTotal; ++i) {
RealmSpotlight.Companion.customizeRoomJSONObject(roomJsonArray.getJSONObject(i));
}
jsonString = roomJsonArray.toString();
}
......
......@@ -33,6 +33,10 @@ abstract class AbstractChatRoomFragment extends AbstractFragment {
roomToolbar.showPublicChannelIcon();
}
protected void showToolbarLivechatChannelIcon() {
roomToolbar.showLivechatChannelIcon();
}
protected void showToolbarUserStatuslIcon(@Nullable String status) {
if (status == null) {
roomToolbar.showUserStatusIcon(RoomToolbar.STATUS_OFFLINE);
......@@ -47,9 +51,6 @@ abstract class AbstractChatRoomFragment extends AbstractFragment {
case User.STATUS_AWAY:
roomToolbar.showUserStatusIcon(RoomToolbar.STATUS_AWAY);
break;
case User.STATUS_OFFLINE:
roomToolbar.showUserStatusIcon(RoomToolbar.STATUS_OFFLINE);
break;
default:
roomToolbar.showUserStatusIcon(RoomToolbar.STATUS_OFFLINE);
break;
......
......@@ -20,8 +20,9 @@ import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;
import chat.rocket.android.fragment.sidebar.SidebarMainFragment;
import chat.rocket.android.widget.RoomToolbar;
import chat.rocket.core.models.User;
import java.lang.reflect.Field;
import java.util.ArrayList;
......@@ -126,6 +127,9 @@ public class RoomFragment extends AbstractChatRoomFragment implements
private Message edittingMessage = null;
private SlidingPaneLayout pane;
private SidebarMainFragment sidebarFragment;
public RoomFragment() {}
/**
......@@ -190,6 +194,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
@Override
protected void onSetupView() {
pane = getActivity().findViewById(R.id.sliding_pane);
messageRecyclerView = rootView.findViewById(R.id.messageRecyclerView);
messageListAdapter = new MessageListAdapter(getContext(), hostname);
......@@ -236,6 +241,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
}
};
setupSidebar();
setupSideMenu();
setupMessageComposer();
setupMessageActions();
......@@ -306,7 +312,6 @@ public class RoomFragment extends AbstractChatRoomFragment implements
});
DrawerLayout drawerLayout = rootView.findViewById(R.id.drawer_layout);
SlidingPaneLayout pane = getActivity().findViewById(R.id.sliding_pane);
if (drawerLayout != null && pane != null) {
compositeDisposable.add(RxDrawerLayout.drawerOpen(drawerLayout, GravityCompat.END)
.compose(bindToLifecycle())
......@@ -326,8 +331,47 @@ public class RoomFragment extends AbstractChatRoomFragment implements
}
}
private void setupSidebar() {
SlidingPaneLayout subPane = getActivity().findViewById(R.id.sub_sliding_pane);
RoomToolbar toolbar = getActivity().findViewById(R.id.activity_main_toolbar);
sidebarFragment = (SidebarMainFragment) getActivity().getSupportFragmentManager().findFragmentById(R.id.sidebar_fragment_container);
pane.setPanelSlideListener(new SlidingPaneLayout.PanelSlideListener() {
@Override
public void onPanelSlide(View view, float v) {
messageFormManager.enableComposingText(false);
sidebarFragment.clearSearchViewFocus();
//Ref: ActionBarDrawerToggle#setProgress
toolbar.setNavigationIconProgress(v);
}
@Override
public void onPanelOpened(View view) {
toolbar.setNavigationIconVerticalMirror(true);
}
@Override
public void onPanelClosed(View view) {
messageFormManager.enableComposingText(true);
toolbar.setNavigationIconVerticalMirror(false);
subPane.closePane();
closeUserActionContainer();
}
});
toolbar.setNavigationOnClickListener(view -> {
if (pane.isSlideable() && !pane.isOpen()) {
pane.openPane();
}
});
}
public void closeUserActionContainer() {
sidebarFragment.closeUserActionContainer();
}
private boolean closeSideMenuIfNeeded() {
DrawerLayout drawerLayout = (DrawerLayout) rootView.findViewById(R.id.drawer_layout);
DrawerLayout drawerLayout = rootView.findViewById(R.id.drawer_layout);
if (drawerLayout != null && drawerLayout.isDrawerOpen(GravityCompat.END)) {
drawerLayout.closeDrawer(GravityCompat.END);
return true;
......@@ -336,12 +380,12 @@ public class RoomFragment extends AbstractChatRoomFragment implements
}
private void setupMessageComposer() {
final MessageFormLayout messageFormLayout = (MessageFormLayout) rootView.findViewById(R.id.messageComposer);
final MessageFormLayout messageFormLayout = rootView.findViewById(R.id.messageComposer);
messageFormManager = new MessageFormManager(messageFormLayout, this::showExtraActionSelectionDialog);
messageFormManager.setSendMessageCallback(this::sendMessage);
messageFormLayout.setEditTextCommitContentListener(this::onCommitContent);
autocompleteManager = new AutocompleteManager((ViewGroup) rootView.findViewById(R.id.messageListRelativeLayout));
autocompleteManager = new AutocompleteManager(rootView.findViewById(R.id.messageListRelativeLayout));
autocompleteManager.registerSource(
new ChannelSource(
......@@ -542,6 +586,10 @@ public class RoomFragment extends AbstractChatRoomFragment implements
if (room.isPrivate()) {
showToolbarPrivateChannelIcon();
}
if (room.isLivechat()) {
showToolbarLivechatChannelIcon();
}
}
@Override
......@@ -607,4 +655,4 @@ public class RoomFragment extends AbstractChatRoomFragment implements
edittingMessage = message;
messageFormManager.setEditMessage(message.getMessage());
}
}
}
\ No newline at end of file
......@@ -2,10 +2,10 @@ package chat.rocket.android.fragment.sidebar;
import android.support.annotation.NonNull;
import chat.rocket.core.models.RoomSidebar;
import io.reactivex.Flowable;
import java.util.List;
import chat.rocket.android.shared.BaseContract;
import chat.rocket.core.models.Room;
import chat.rocket.core.models.Spotlight;
import chat.rocket.core.models.User;
......@@ -17,19 +17,23 @@ public interface SidebarMainContract {
void showEmptyScreen();
void showRoomList(@NonNull List<Room> roomList);
void showRoomSidebarList(@NonNull List<RoomSidebar> roomSidebarList);
void filterRoomSidebarList(CharSequence term);
void show(User user);
}
interface Presenter extends BaseContract.Presenter<View> {
void onRoomSelected(Room room);
void onRoomSelected(RoomSidebar roomSidebar);
void onSpotlightSelected(Spotlight spotlight);
Flowable<List<Spotlight>> searchSpotlight(String term);
void disposeSubscriptions();
void onUserOnline();
void onUserAway();
......@@ -40,4 +44,4 @@ public interface SidebarMainContract {
void onLogout();
}
}
}
\ No newline at end of file
......@@ -11,6 +11,7 @@ import android.support.v7.widget.SearchView;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.TextView;
import chat.rocket.android.BuildConfig;
import chat.rocket.android.R;
import chat.rocket.android.RocketChatCache;
......@@ -23,13 +24,14 @@ import chat.rocket.android.helper.Logger;
import chat.rocket.android.layouthelper.chatroom.roomlist.ChannelRoomListHeader;
import chat.rocket.android.layouthelper.chatroom.roomlist.DirectMessageRoomListHeader;
import chat.rocket.android.layouthelper.chatroom.roomlist.FavoriteRoomListHeader;
import chat.rocket.android.layouthelper.chatroom.roomlist.LivechatRoomListHeader;
import chat.rocket.android.layouthelper.chatroom.roomlist.RoomListAdapter;
import chat.rocket.android.layouthelper.chatroom.roomlist.RoomListHeader;
import chat.rocket.android.layouthelper.chatroom.roomlist.UnreadRoomListHeader;
import chat.rocket.android.renderer.UserRenderer;
import chat.rocket.core.interactors.RoomInteractor;
import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.core.models.Room;
import chat.rocket.core.models.RoomSidebar;
import chat.rocket.core.models.Spotlight;
import chat.rocket.core.models.User;
import chat.rocket.persistence.realm.repositories.RealmRoomRepository;
......@@ -39,17 +41,20 @@ import chat.rocket.persistence.realm.repositories.RealmSpotlightRepository;
import chat.rocket.persistence.realm.repositories.RealmUserRepository;
import com.jakewharton.rxbinding2.support.v7.widget.RxSearchView;
import com.jakewharton.rxbinding2.widget.RxCompoundButton;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class SidebarMainFragment extends AbstractFragment implements SidebarMainContract.View {
private SidebarMainContract.Presenter presenter;
private RoomListAdapter adapter;
private SearchView searchView;
private TextView loadMoreResultsText;
private List<RoomSidebar> roomSidebarList;
private Disposable spotlightDisposable;
private String hostname;
private static final String HOSTNAME = "hostname";
......@@ -123,9 +128,9 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
adapter = new RoomListAdapter();
adapter.setOnItemClickListener(new RoomListAdapter.OnItemClickListener() {
@Override
public void onItemClick(Room room) {
public void onItemClick(RoomSidebar roomSidebar) {
searchView.clearFocus();
presenter.onRoomSelected(room);
presenter.onRoomSelected(roomSidebar);
}
@Override
......@@ -140,20 +145,63 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
recyclerView.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(adapter);
loadMoreResultsText = rootView.findViewById(R.id.text_load_more_results);
RxSearchView.queryTextChanges(searchView)
.compose(bindToLifecycle())
.debounce(300, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.switchMap(charSequence -> {
if (charSequence.length() == 0) {
adapter.setMode(RoomListAdapter.MODE_ROOM);
return Observable.just(Collections.<Spotlight>emptyList());
} else {
adapter.setMode(RoomListAdapter.MODE_SPOTLIGHT);
return presenter.searchSpotlight(charSequence.toString()).toObservable();
.observeOn(AndroidSchedulers.mainThread())
.subscribe(charSequence -> {
if (spotlightDisposable != null && !spotlightDisposable.isDisposed()) {
spotlightDisposable.dispose();
}
presenter.disposeSubscriptions();
if (charSequence.length() == 0) {
loadMoreResultsText.setVisibility(View.GONE);
adapter.setMode(RoomListAdapter.MODE_ROOM);
presenter.bindView(this);
} else {
filterRoomSidebarList(charSequence);
}
});
loadMoreResultsText.setOnClickListener(view -> loadMoreResults());
}
@Override
public void showRoomSidebarList(@NonNull List<RoomSidebar> roomSidebarList) {
this.roomSidebarList = roomSidebarList;
adapter.setRoomSidebarList(roomSidebarList);
}
@Override
public void filterRoomSidebarList(CharSequence term) {
List<RoomSidebar> filteredRoomSidebarList = new ArrayList<>();
for (RoomSidebar roomSidebar: roomSidebarList) {
if (roomSidebar.getRoomName().contains(term)) {
filteredRoomSidebarList.add(roomSidebar);
}
})
.subscribe(this::showSearchSuggestions, Logger::report);
}
if (filteredRoomSidebarList.isEmpty()) {
loadMoreResults();
} else {
loadMoreResultsText.setVisibility(View.VISIBLE);
adapter.setMode(RoomListAdapter.MODE_ROOM);
adapter.setRoomSidebarList(filteredRoomSidebarList);
}
}
private void loadMoreResults() {
spotlightDisposable = presenter.searchSpotlight(searchView.getQuery().toString())
.toObservable()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::showSearchSuggestions);
}
private void showSearchSuggestions(List<Spotlight> spotlightList) {
loadMoreResultsText.setVisibility(View.GONE);
adapter.setMode(RoomListAdapter.MODE_SPOTLIGHT);
adapter.setSpotlightList(spotlightList);
}
@SuppressLint("RxLeakedSubscription")
......@@ -186,15 +234,10 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
rootView.setVisibility(View.INVISIBLE);
}
@Override
public void showRoomList(@NonNull List<Room> roomList) {
adapter.setRooms(roomList);
}
@Override
public void show(User user) {
onRenderCurrentUser(user);
updateRoomListMode(user);
updateRoomListMode();
}
private void setupUserStatusButtons() {
......@@ -225,20 +268,21 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
}
}
private void updateRoomListMode(User user) {
private void updateRoomListMode() {
final List<RoomListHeader> roomListHeaders = new ArrayList<>();
if (user != null && user.getSettings() != null && user.getSettings().getPreferences() != null
&& user.getSettings().getPreferences().isUnreadRoomsMode()) {
roomListHeaders.add(new UnreadRoomListHeader(
getString(R.string.fragment_sidebar_main_unread_rooms_title)
));
}
roomListHeaders.add(new UnreadRoomListHeader(
getString(R.string.fragment_sidebar_main_unread_rooms_title)
));
roomListHeaders.add(new FavoriteRoomListHeader(
getString(R.string.fragment_sidebar_main_favorite_title)
));
roomListHeaders.add(new LivechatRoomListHeader(
getString(R.string.fragment_sidebar_main_livechat_title)
));
roomListHeaders.add(new ChannelRoomListHeader(
getString(R.string.fragment_sidebar_main_channels_title),
() -> showAddRoomDialog(AddChannelDialogFragment.create(hostname))
......@@ -260,6 +304,10 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
});
}
public void clearSearchViewFocus() {
searchView.clearFocus();
}
public void closeUserActionContainer() {
final CompoundButton toggleUserAction = rootView.findViewById(R.id.toggle_user_action);
if (toggleUserAction != null && toggleUserAction.isChecked()) {
......@@ -276,7 +324,4 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
dialog.show(getFragmentManager(), "AbstractAddRoomDialogFragment");
}
private void showSearchSuggestions(List<Spotlight> spotlightList) {
adapter.setSpotlightList(spotlightList);
}
}
\ No newline at end of file
package chat.rocket.android.layouthelper.chatroom;
import chat.rocket.android.widget.message.MessageFormLayout;
/**
* handling MessageForm.
*/
public class MessageFormManager {
private final MessageFormLayout messageFormLayout;
private SendMessageCallback sendMessageCallback;
public MessageFormManager(MessageFormLayout messageFormLayout,
MessageFormLayout.ExtraActionSelectionClickListener callback) {
this.messageFormLayout = messageFormLayout;
init(callback);
}
private void init(MessageFormLayout.ExtraActionSelectionClickListener listener) {
messageFormLayout.setExtraActionSelectionClickListener(listener);
messageFormLayout.setSubmitTextListener(this::sendMessage);
}
public void setSendMessageCallback(SendMessageCallback sendMessageCallback) {
this.sendMessageCallback = sendMessageCallback;
}
public void clearComposingText() {
messageFormLayout.setText("");
}
public void onMessageSend() {
clearComposingText();
messageFormLayout.setEnabled(true);
}
public void setEditMessage(String message) {
clearComposingText();
messageFormLayout.setText(message);
}
private void sendMessage(String message) {
if (sendMessageCallback == null) {
return;
}
messageFormLayout.setEnabled(false);
sendMessageCallback.onSubmitText(message);
}
public interface SendMessageCallback {
void onSubmitText(String messageText);
}
}
package chat.rocket.android.layouthelper.chatroom
import chat.rocket.android.widget.message.MessageFormLayout
class MessageFormManager(private val messageFormLayout: MessageFormLayout, val callback: MessageFormLayout.ExtraActionSelectionClickListener) {
private var sendMessageCallback: SendMessageCallback? = null
init {
messageFormLayout.setExtraActionSelectionClickListener(callback)
messageFormLayout.setSubmitTextListener(this::sendMessage)
}
fun setSendMessageCallback(sendMessageCallback: SendMessageCallback) {
this.sendMessageCallback = sendMessageCallback
}
fun onMessageSend() {
clearComposingText()
}
fun setEditMessage(message: String) {
clearComposingText()
messageFormLayout.setText(message)
}
fun clearComposingText() {
messageFormLayout.setText("")
}
fun enableComposingText(enable: Boolean) {
messageFormLayout.isEnabled = enable
}
private fun sendMessage(message: String) {
sendMessageCallback?.onSubmitText(message)
}
interface SendMessageCallback {
fun onSubmitText(messageText: String)
}
}
\ No newline at end of file
......@@ -2,6 +2,7 @@ package chat.rocket.android.layouthelper.chatroom.roomlist;
import android.support.annotation.NonNull;
import chat.rocket.core.models.RoomSidebar;
import java.util.List;
import chat.rocket.core.models.Room;
......@@ -21,17 +22,25 @@ public class ChannelRoomListHeader implements RoomListHeader {
}
@Override
public boolean owns(Room room) {
return room.isChannel() || room.isPrivate();
public boolean owns(RoomSidebar roomSidebar) {
return roomSidebar.getType().equals(Room.TYPE_CHANNEL) || roomSidebar.getType().equals(Room.TYPE_PRIVATE);
}
@Override
public boolean shouldShow(@NonNull List<Room> roomList) {
return true;
public boolean shouldShow(@NonNull List<RoomSidebar> roomSidebarList) {
for (RoomSidebar roomSidebar: roomSidebarList) {
if ((roomSidebar.getType().equals(Room.TYPE_CHANNEL)
|| roomSidebar.getType().equals(Room.TYPE_PRIVATE))
&& !roomSidebar.isAlert()
&& !roomSidebar.isFavorite()) {
return true;
}
}
return false;
}
@Override
public ClickListener getClickListener() {
return clickListener;
}
}
}
\ No newline at end of file
......@@ -2,6 +2,7 @@ package chat.rocket.android.layouthelper.chatroom.roomlist;
import android.support.annotation.NonNull;
import chat.rocket.core.models.RoomSidebar;
import java.util.List;
import chat.rocket.core.models.Room;
......@@ -21,17 +22,24 @@ public class DirectMessageRoomListHeader implements RoomListHeader {
}
@Override
public boolean owns(Room room) {
return room.isDirectMessage();
public boolean owns(RoomSidebar roomSidebar) {
return roomSidebar.getType().equals(Room.TYPE_DIRECT_MESSAGE);
}
@Override
public boolean shouldShow(@NonNull List<Room> roomList) {
return true;
public boolean shouldShow(@NonNull List<RoomSidebar> roomSidebarList) {
for (RoomSidebar roomSidebar: roomSidebarList) {
if (roomSidebar.getType().equals(Room.TYPE_DIRECT_MESSAGE)
&& !roomSidebar.isAlert()
&& !roomSidebar.isFavorite()) {
return true;
}
}
return false;
}
@Override
public ClickListener getClickListener() {
return clickListener;
}
}
}
\ No newline at end of file
......@@ -2,8 +2,8 @@ package chat.rocket.android.layouthelper.chatroom.roomlist;
import android.support.annotation.NonNull;
import chat.rocket.core.models.RoomSidebar;
import java.util.List;
import chat.rocket.core.models.Room;
public class FavoriteRoomListHeader implements RoomListHeader {
......@@ -19,18 +19,17 @@ public class FavoriteRoomListHeader implements RoomListHeader {
}
@Override
public boolean owns(Room room) {
return room.isFavorite();
public boolean owns(RoomSidebar roomSidebar) {
return roomSidebar.isFavorite();
}
@Override
public boolean shouldShow(@NonNull List<Room> roomList) {
for (int i = 0, size = roomList.size(); i < size; i++) {
if (roomList.get(i).isFavorite()) {
public boolean shouldShow(@NonNull List<RoomSidebar> roomSidebarList) {
for (RoomSidebar roomSidebar: roomSidebarList) {
if (roomSidebar.isFavorite() && !roomSidebar.isAlert()) {
return true;
}
}
return false;
}
......@@ -38,4 +37,4 @@ public class FavoriteRoomListHeader implements RoomListHeader {
public ClickListener getClickListener() {
return null;
}
}
}
\ No newline at end of file
package chat.rocket.android.layouthelper.chatroom.roomlist;
import android.support.annotation.NonNull;
import java.util.List;
import chat.rocket.core.models.Room;
import chat.rocket.core.models.RoomSidebar;
public class LivechatRoomListHeader implements RoomListHeader {
private final String title;
public LivechatRoomListHeader(String title) {
this.title = title;
}
@Override
public String getTitle() {
return title;
}
@Override
public boolean owns(RoomSidebar roomSidebar) {
return Room.TYPE_LIVECHAT.equals(roomSidebar.getType());
}
@Override
public boolean shouldShow(@NonNull List<RoomSidebar> roomSidebarList) {
for (RoomSidebar roomSidebar: roomSidebarList) {
if (owns(roomSidebar)) {
return true;
}
}
return false;
}
@Override
public ClickListener getClickListener() {
return null;
}
}
\ No newline at end of file
......@@ -5,6 +5,7 @@ import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import chat.rocket.core.models.RoomSidebar;
import chat.rocket.core.models.Spotlight;
import java.util.Collections;
import java.util.HashMap;
......@@ -12,7 +13,6 @@ import java.util.List;
import java.util.Map;
import chat.rocket.android.R;
import chat.rocket.android.widget.internal.RoomListItemView;
import chat.rocket.core.models.Room;
public class RoomListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
......@@ -22,7 +22,7 @@ public class RoomListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
private static final int VIEW_TYPE_HEADER = 0;
private static final int VIEW_TYPE_ROOM = 1;
private List<Room> roomList = Collections.emptyList();
private List<RoomSidebar> roomSidebarList = Collections.emptyList();
private List<Spotlight> spotlightList = Collections.emptyList();
private List<RoomListHeader> roomListHeaders = Collections.emptyList();
private Map<Integer, RoomListHeader> headersPosition = new HashMap<>();
......@@ -32,9 +32,9 @@ public class RoomListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
private OnItemClickListener externalListener;
private OnItemClickListener listener = new OnItemClickListener() {
@Override
public void onItemClick(Room room) {
public void onItemClick(RoomSidebar roomSidebar) {
if (externalListener != null) {
externalListener.onItemClick(room);
externalListener.onItemClick(roomSidebar);
}
}
......@@ -48,17 +48,17 @@ public class RoomListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
public void setRoomListHeaders(@NonNull List<RoomListHeader> roomListHeaders) {
this.roomListHeaders = roomListHeaders;
updateRoomList();
updateRoomSidebarList();
}
public void setRooms(@NonNull List<Room> roomList) {
this.roomList = roomList;
updateRoomList();
public void setRoomSidebarList(@NonNull List<RoomSidebar> roomSidebarList) {
this.roomSidebarList = roomSidebarList;
updateRoomSidebarList();
}
public void setSpotlightList(@NonNull List<Spotlight> spotlightList) {
this.spotlightList = spotlightList;
updateRoomList();
updateRoomSidebarList();
}
public void setMode(int mode) {
......@@ -77,9 +77,9 @@ public class RoomListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_HEADER) {
return new RoomListHeaderViewHolder(
LayoutInflater.from(parent.getContext())
.inflate(R.layout.room_list_header, parent, false)
return new RoomListHeaderViewHolder(LayoutInflater
.from(parent.getContext())
.inflate(R.layout.room_list_header, parent, false)
);
}
return new RoomListItemViewHolder(new RoomListItemView(parent.getContext()), listener);
......@@ -89,13 +89,12 @@ public class RoomListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (mode == MODE_ROOM) {
if (getItemViewType(position) == VIEW_TYPE_HEADER) {
((RoomListHeaderViewHolder) holder)
.bind(headersPosition.get(position));
((RoomListHeaderViewHolder) holder).bind(headersPosition.get(position));
return;
}
((RoomListItemViewHolder) holder)
.bind(roomList.get(position - getTotalHeadersBeforePosition(position)));
RoomSidebar roomSidebar = roomSidebarList.get(position - getTotalHeadersBeforePosition(position));
((RoomListItemViewHolder) holder).bind(roomSidebar);
} else if (mode == MODE_SPOTLIGHT) {
((RoomListItemViewHolder) holder).bind(spotlightList.get(position));
}
......@@ -106,7 +105,7 @@ public class RoomListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
if (mode == MODE_SPOTLIGHT) {
return spotlightList.size();
}
return roomList.size() + headersPosition.size();
return roomSidebarList.size() + headersPosition.size();
}
@Override
......@@ -121,7 +120,7 @@ public class RoomListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
return VIEW_TYPE_ROOM;
}
private void updateRoomList() {
private void updateRoomSidebarList() {
if (mode == MODE_ROOM) {
sortRoomList();
calculateHeadersPosition();
......@@ -132,18 +131,18 @@ public class RoomListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
private void sortRoomList() {
int totalHeaders = roomListHeaders.size();
Collections.sort(roomList, (room, anotherRoom) -> {
Collections.sort(roomSidebarList, (roomSidebar, anotherRoom) -> {
for (int i = 0; i < totalHeaders; i++) {
final RoomListHeader header = roomListHeaders.get(i);
if (header.owns(room) && !header.owns(anotherRoom)) {
if (header.owns(roomSidebar) && !header.owns(anotherRoom)) {
return -1;
} else if (!header.owns(room) && header.owns(anotherRoom)) {
} else if (!header.owns(roomSidebar) && header.owns(anotherRoom)) {
return 1;
}
}
return room.getName().compareTo(anotherRoom.getName());
return roomSidebar.getRoomName().compareTo(anotherRoom.getRoomName());
});
}
......@@ -151,19 +150,19 @@ public class RoomListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
headersPosition.clear();
int roomIdx = 0;
int totalRooms = roomList.size();
int totalRooms = roomSidebarList.size();
int totalHeaders = roomListHeaders.size();
for (int i = 0; i < totalHeaders; i++) {
final RoomListHeader header = roomListHeaders.get(i);
if (!header.shouldShow(roomList)) {
if (!header.shouldShow(roomSidebarList)) {
continue;
}
headersPosition.put(roomIdx + headersPosition.size(), header);
for (; roomIdx < totalRooms; roomIdx++) {
final Room room = roomList.get(roomIdx);
if (!header.owns(room)) {
final RoomSidebar roomSidebar = roomSidebarList.get(roomIdx);
if (!header.owns(roomSidebar)) {
break;
}
}
......@@ -185,7 +184,7 @@ public class RoomListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolde
}
public interface OnItemClickListener {
void onItemClick(Room room);
void onItemClick(RoomSidebar roomSidebar);
void onItemClick(Spotlight spotlight);
}
......
......@@ -2,16 +2,16 @@ package chat.rocket.android.layouthelper.chatroom.roomlist;
import android.support.annotation.NonNull;
import chat.rocket.core.models.RoomSidebar;
import java.util.List;
import chat.rocket.core.models.Room;
public interface RoomListHeader {
String getTitle();
boolean owns(Room room);
boolean owns(RoomSidebar roomSidebar);
boolean shouldShow(@NonNull List<Room> roomList);
boolean shouldShow(@NonNull List<RoomSidebar> roomSidebarList);
ClickListener getClickListener();
......
......@@ -14,8 +14,8 @@ public class RoomListHeaderViewHolder extends RecyclerView.ViewHolder {
public RoomListHeaderViewHolder(View itemView) {
super(itemView);
title = (TextView) itemView.findViewById(R.id.title);
button = (Button) itemView.findViewById(R.id.btn_add);
title = itemView.findViewById(R.id.title);
button = itemView.findViewById(R.id.btn_add);
}
public void bind(RoomListHeader roomListHeader) {
......@@ -29,4 +29,4 @@ public class RoomListHeaderViewHolder extends RecyclerView.ViewHolder {
title.setText(roomListHeader.getTitle());
}
}
}
\ No newline at end of file
......@@ -4,43 +4,102 @@ import android.support.v7.widget.RecyclerView;
import chat.rocket.android.widget.internal.RoomListItemView;
import chat.rocket.core.models.Room;
import chat.rocket.core.models.RoomSidebar;
import chat.rocket.core.models.Spotlight;
import chat.rocket.core.models.User;
public class RoomListItemViewHolder extends RecyclerView.ViewHolder {
public RoomListItemViewHolder(RoomListItemView itemView,
RoomListAdapter.OnItemClickListener listener) {
private RoomListItemView itemView;
public RoomListItemViewHolder(RoomListItemView itemView, RoomListAdapter.OnItemClickListener listener) {
super(itemView);
this.itemView = itemView;
itemView.setOnClickListener(view -> {
if (listener != null) {
Object tag = view.getTag();
if (tag instanceof Room) {
listener.onItemClick((Room) view.getTag());
} else if (tag instanceof Spotlight) {
listener.onItemClick((Spotlight) view.getTag());
}
Object object = view.getTag();
if (object instanceof RoomSidebar) {
listener.onItemClick((RoomSidebar)object);
} else if (object instanceof Spotlight) {
listener.onItemClick((Spotlight)object);
}
});
}
public void bind(Room room) {
((RoomListItemView) itemView)
.setRoomId(room.getRoomId())
.setRoomName(room.getName())
.setRoomType(room.getType())
.setAlert(room.isAlert())
.setUnreadCount(room.getUnread())
.setTag(room);
public void bind(RoomSidebar roomSidebar) {
itemView.setRoomId(roomSidebar.getRoomId());
itemView.setRoomName(roomSidebar.getRoomName());
itemView.setAlert(roomSidebar.isAlert());
itemView.setUnreadCount(roomSidebar.getUnread());
itemView.setTag(roomSidebar);
String roomType = roomSidebar.getType();
if (roomType.equals(Room.TYPE_DIRECT_MESSAGE)) {
showUserStatusIcon(roomSidebar.getUserStatus());
} else {
showRoomIcon(roomType);
}
}
public void bind(Spotlight spotlight) {
((RoomListItemView) itemView)
.setRoomId(spotlight.getId())
.setRoomName(spotlight.getName())
.setRoomType(spotlight.getType())
.setAlert(false)
.setUnreadCount(0)
.setTag(spotlight);
itemView.setRoomId(spotlight.getId());
itemView.setRoomName(spotlight.getName());
itemView.setAlert(false);
itemView.setUnreadCount(0);
itemView.setTag(spotlight);
String roomType = spotlight.getType();
if (roomType.equals(Room.TYPE_DIRECT_MESSAGE)) {
showUserStatusIcon(spotlight.getStatus());
} else {
showRoomIcon(roomType);
}
}
/**
* Shows the user status icon.
* @param userStatus The user status to show the correspondent icon.
* @see User
*/
private void showUserStatusIcon(String userStatus) {
if (userStatus == null) {
itemView.showOfflineUserStatusIcon();
} else {
switch (userStatus) {
case User.STATUS_ONLINE:
itemView.showOnlineUserStatusIcon();
break;
case User.STATUS_BUSY:
itemView.showBusyUserStatusIcon();
break;
case User.STATUS_AWAY:
itemView.showAwayUserStatusIcon();
break;
default:
itemView.showOfflineUserStatusIcon();
break;
}
}
}
/**
* Only shows the room icon if it is a PRIVATE CHANNEL or PUBLIC CHANNEL, otherwise you should use {@link #showUserStatusIcon(String)} to show the icon.
* @param roomType The type of Room.
* @see Room
*/
private void showRoomIcon(String roomType) {
switch (roomType) {
case Room.TYPE_CHANNEL:
itemView.showPublicChannelIcon();
break;
case Room.TYPE_PRIVATE:
itemView.showPrivateChannelIcon();
break;
case Room.TYPE_LIVECHAT:
itemView.showLivechatChannelIcon();
break;
default:
throw new AssertionError("Room type doesn't satisfies the method documentation. Room type is:" + roomType);
}
}
}
}
\ No newline at end of file
......@@ -2,8 +2,8 @@ package chat.rocket.android.layouthelper.chatroom.roomlist;
import android.support.annotation.NonNull;
import chat.rocket.core.models.RoomSidebar;
import java.util.List;
import chat.rocket.core.models.Room;
public class UnreadRoomListHeader implements RoomListHeader {
......@@ -19,23 +19,22 @@ public class UnreadRoomListHeader implements RoomListHeader {
}
@Override
public boolean owns(Room room) {
return room.isAlert();
public boolean owns(RoomSidebar roomSidebar) {
return roomSidebar.isAlert();
}
@Override
public boolean shouldShow(@NonNull List<Room> roomList) {
for (int i = 0, size = roomList.size(); i < size; i++) {
if (roomList.get(i).isAlert()) {
public boolean shouldShow(@NonNull List<RoomSidebar> roomSidebarList) {
for (RoomSidebar roomSidebar: roomSidebarList) {
if (roomSidebar.isAlert()) {
return true;
}
}
return false;
return false;
}
@Override
public ClickListener getClickListener() {
return null;
}
}
}
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M16.5,6v11.5c0,2.21 -1.79,4 -4,4s-4,-1.79 -4,-4V5c0,-1.38 1.12,-2.5 2.5,-2.5s2.5,1.12 2.5,2.5v10.5c0,0.55 -0.45,1 -1,1s-1,-0.45 -1,-1V6H10v9.5c0,1.38 1.12,2.5 2.5,2.5s2.5,-1.12 2.5,-2.5V5c0,-2.21 -1.79,-4 -4,-4S7,2.79 7,5v12.5c0,3.04 2.46,5.5 5.5,5.5s5.5,-2.46 5.5,-5.5V6h-1.5z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:alpha="0.78"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0"
android:width="24dp">
<path
android:fillColor="#FF000000"
android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
</vector>
......@@ -4,7 +4,7 @@
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:theme="@style/Theme.AppCompat.Light">
android:background="@android:color/white">
<TextView
android:layout_width="match_parent"
......
......@@ -5,6 +5,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="@style/AppTheme.Dark"
android:background="?attr/colorPrimary"
tools:context="chat.rocket.android.fragment.sidebar.SidebarMainFragment">
<LinearLayout
......@@ -82,9 +83,18 @@
android:id="@+id/room_list_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/search_box"
android:layout_above="@+id/text_load_more_results"
android:layout_below="@+id/search_box" />
<TextView
android:id="@+id/text_load_more_results"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:layout_alignParentBottom="true"
android:background="?attr/colorPrimary" />
android:text="@string/spotlight_load_more_results"
android:background="?attr/colorPrimary"
android:visibility="gone" />
<android.support.v4.widget.NestedScrollView
android:id="@+id/user_action_outer_container"
......
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="gcm_sender_id">YOUR-SENDER-ID</string>
<string name="gcm_sender_id">673693445664</string>
</resources>
\ No newline at end of file
......@@ -3,6 +3,7 @@
<string name="fragment_sidebar_main_favorite_title">FAVORITES</string>
<string name="fragment_sidebar_main_channels_title">CHANNELS</string>
<string name="fragment_sidebar_main_direct_messages_title">DIRECT MESSAGES</string>
<string name="fragment_sidebar_main_livechat_title">LIVECHAT</string>
<string name="user_status_online">Online</string>
<string name="user_status_away">Away</string>
<string name="user_status_busy">Busy</string>
......@@ -63,6 +64,7 @@
<string name="open_your_authentication_app_and_enter_the_code">Open your authentication app and enter the code</string>
<string name="two_factor_code">Two-factor code</string>
<string name="spotlight_search">Search</string>
<string name="spotlight_load_more_results">Load more results</string>
<string name="edit_message">Edit message</string>
<string name="message_options_no_message_info">Ooops. Something\'s up!</string>
<string name="message_options_no_permissions_info">You have no permissions</string>
......
......@@ -10,21 +10,26 @@ open class RealmSpotlight : RealmObject() {
@PrimaryKey var _id: String? = null
var name: String? = null
var t: String? = null
var status: String? = null
fun asSpotlight(): Spotlight {
return Spotlight.builder()
.setId(_id)
.setName(name)
.setType(t)
.setStatus(status)
.build()
}
companion object {
fun customizeRoomJSONObject(roomJsonObject: JSONObject) {
roomJsonObject.put(Columns.STATUS, "")
}
fun customizeUserJSONObject(userJsonObject: JSONObject) {
userJsonObject.put(Columns.NAME, userJsonObject.get("username"))
userJsonObject.put(Columns.TYPE, "d")
userJsonObject.remove("username")
userJsonObject.remove("status")
}
}
......@@ -33,6 +38,7 @@ open class RealmSpotlight : RealmObject() {
const val ID = "_id"
const val NAME = "name"
const val TYPE = "t"
const val STATUS = "status"
}
}
}
\ No newline at end of file
......@@ -107,7 +107,6 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito
if (optional.isPresent()) {
return Optional.of(optional.get().asRoomHistoryState());
}
return Optional.absent();
}));
}
......
......@@ -27,6 +27,21 @@ public class RealmUserRepository extends RealmRepository implements UserReposito
this.hostname = hostname;
}
@Override
public Flowable<List<User>> getAll() {
return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(
pair.first.where(RealmUser.class)
.findAll()
.asObservable()),
pair -> close(pair.first, pair.second))
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
.filter(roomSubscriptions -> roomSubscriptions != null && roomSubscriptions.isLoaded()
&& roomSubscriptions.isValid())
.map(this::toList));
}
@Override
public Flowable<Optional<User>> getCurrent() {
return Flowable.defer(() ->
......
......@@ -7,7 +7,6 @@ import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.graphics.drawable.VectorDrawableCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.graphics.drawable.DrawerArrowDrawable;
import android.support.v7.widget.AppCompatImageView;
import android.support.v7.widget.Toolbar;
......@@ -17,6 +16,7 @@ import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import chat.rocket.android.widget.helper.DrawableHelper;
import com.amulyakhare.textdrawable.TextDrawable;
import java.lang.reflect.Field;
......@@ -29,6 +29,7 @@ public class RoomToolbar extends Toolbar {
private Drawable privateChannelDrawable;
private Drawable publicChannelDrawable;
private Drawable livechatChannelDrawable;
private Drawable userStatusDrawable;
private DrawerArrowDrawable drawerArrowDrawable;
......@@ -63,6 +64,7 @@ public class RoomToolbar extends Toolbar {
privateChannelDrawable = VectorDrawableCompat.create(getResources(), R.drawable.ic_lock_white_24dp, null);
publicChannelDrawable = VectorDrawableCompat.create(getResources(), R.drawable.ic_hashtag_white_24dp, null);
livechatChannelDrawable = VectorDrawableCompat.create(getResources(), R.drawable.ic_livechat_white_24dp, null);
userStatusDrawable = VectorDrawableCompat.create(getResources(), R.drawable.ic_user_status_black_24dp, null);
}
......@@ -102,24 +104,31 @@ public class RoomToolbar extends Toolbar {
roomTypeImage.setVisibility(VISIBLE);
}
public void showLivechatChannelIcon() {
roomTypeImage.setImageDrawable(livechatChannelDrawable);
userStatusImage.setVisibility(GONE);
roomTypeImage.setVisibility(VISIBLE);
}
public void showUserStatusIcon(int status) {
wrapDrawable(userStatusDrawable);
DrawableHelper.INSTANCE.wrapDrawable(userStatusDrawable);
Context context = getContext();
switch (status) {
case STATUS_ONLINE:
tintDrawable(userStatusDrawable, R.color.color_user_status_online);
DrawableHelper.INSTANCE.tintDrawable(userStatusDrawable, context, R.color.color_user_status_online);
break;
case STATUS_BUSY:
tintDrawable(userStatusDrawable, R.color.color_user_status_busy);
DrawableHelper.INSTANCE.tintDrawable(userStatusDrawable, context, R.color.color_user_status_busy);
break;
case STATUS_AWAY:
tintDrawable(userStatusDrawable, R.color.color_user_status_away);
DrawableHelper.INSTANCE.tintDrawable(userStatusDrawable, context, R.color.color_user_status_away);
break;
case STATUS_OFFLINE:
tintDrawable(userStatusDrawable, R.color.color_user_status_offline);
DrawableHelper.INSTANCE.tintDrawable(userStatusDrawable, context, R.color.color_user_status_offline);
break;
default:
tintDrawable(userStatusDrawable, R.color.color_user_status_offline);
DrawableHelper.INSTANCE.tintDrawable(userStatusDrawable, context, R.color.color_user_status_offline);
break;
}
......@@ -128,25 +137,6 @@ public class RoomToolbar extends Toolbar {
userStatusImage.setVisibility(VISIBLE);
}
/**
* Wraps a drawable to be used for example for tinting.
* @param drawable The drawable to wrap.
* @see #tintDrawable(Drawable, int)
*/
private void wrapDrawable(Drawable drawable) {
DrawableCompat.wrap(drawable);
}
/**
* REMARK: You MUST always wrap the drawable before tint it.
* @param drawable The drawable to tint.
* @param color The color to tint the drawable.
* @see #wrapDrawable(Drawable)
*/
private void tintDrawable(Drawable drawable, int color) {
DrawableCompat.setTint(drawable, ContextCompat.getColor(getContext(), color));
}
public void setUnreadBudge(int numUnreadChannels, int numMentionsSum) {
if (getNavigationIcon() == null) {
return;
......@@ -166,7 +156,7 @@ public class RoomToolbar extends Toolbar {
badgeImageView.setImageDrawable(getBadgeDrawable(numMentionsSum));
} else {
badgeImageView.setScaleType(ImageView.ScaleType.CENTER);
badgeImageView.setImageResource(R.drawable.badge_without_number);
badgeImageView.setImageResource(R.drawable.ic_badge_without_number_red_10dp);
}
badgeImageView.setVisibility(View.VISIBLE);
} else {
......@@ -180,7 +170,7 @@ public class RoomToolbar extends Toolbar {
.beginConfig()
.useFont(Typeface.SANS_SERIF)
.endConfig()
.buildRound(icon, ContextCompat.getColor(getContext(), R.color.color_user_status_busy));
.buildRound(icon, ContextCompat.getColor(getContext(), R.color.color_alert));
}
@Override
......
package chat.rocket.android.widget.helper
import android.content.Context
import android.graphics.drawable.Drawable
import android.support.v4.content.ContextCompat
import android.support.v4.graphics.drawable.DrawableCompat
object DrawableHelper {
/**
* Wraps a drawable to be used for example for tinting.
*
* @param drawable The drawable to wrap.
* @see tintDrawable
*/
fun wrapDrawable(drawable: Drawable) {
DrawableCompat.wrap(drawable)
}
/**
* REMARK: You MUST always wrap the drawable before tint it.
*
* @param drawable The drawable to tint.
* @param context The context.
* @param resId The resource id color to tint the drawable.
* @see wrapDrawable
*/
fun tintDrawable(drawable: Drawable, context: Context, resId: Int) {
DrawableCompat.setTint(drawable, ContextCompat.getColor(context, resId))
}
}
\ No newline at end of file
package chat.rocket.android.widget.internal;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.HashMap;
import chat.rocket.android.widget.R;
/**
* Room list-item view used in sidebar.
*/
public class RoomListItemView extends FrameLayout {
private static HashMap<String, Integer> ICON_TABLE = new HashMap<String, Integer>() {
{
put("c", R.string.fa_hashtag);
put("p", R.string.fa_lock);
put("d", R.string.fa_at);
}
};
private String roomId;
private String roomName;
public RoomListItemView(Context context) {
super(context);
initialize(context);
}
public RoomListItemView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context);
}
public RoomListItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public RoomListItemView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initialize(context);
}
private void initialize(Context context) {
setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
TypedArray array2 = context.getTheme().obtainStyledAttributes(new int[]{
R.attr.selectableItemBackground
});
setBackground(array2.getDrawable(0));
array2.recycle();
View.inflate(context, R.layout.room_list_item, this);
}
public String getRoomId() {
return roomId;
}
public RoomListItemView setRoomId(String roomId) {
this.roomId = roomId;
return this;
}
public RoomListItemView setRoomType(String type) {
if (ICON_TABLE.containsKey(type)) {
TextView icon = (TextView) findViewById(R.id.icon);
icon.setText(ICON_TABLE.get(type));
}
return this;
}
public RoomListItemView setUnreadCount(int count) {
View alertCountContainer = findViewById(R.id.alert_count_container);
TextView alertCount = (TextView) findViewById(R.id.alert_count);
if (count > 0) {
alertCount.setText(Integer.toString(count));
alertCountContainer.setVisibility(View.VISIBLE);
} else {
alertCountContainer.setVisibility(View.GONE);
}
return this;
}
public RoomListItemView setAlert(boolean alert) {
setAlpha(alert ? 1.0f : 0.62f);
return this;
}
public String getRoomName() {
return roomName;
}
public RoomListItemView setRoomName(String roomName) {
this.roomName = roomName;
TextView text = (TextView) findViewById(R.id.text);
text.setText(roomName);
return this;
}
}
package chat.rocket.android.widget.internal
import android.annotation.TargetApi
import android.content.Context
import android.content.res.TypedArray
import android.graphics.drawable.Drawable
import android.os.Build
import android.support.annotation.ColorRes
import android.support.annotation.StringRes
import android.support.graphics.drawable.VectorDrawableCompat
import android.util.AttributeSet
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import chat.rocket.android.widget.R
import chat.rocket.android.widget.helper.DrawableHelper
/**
* Room list-item view used in sidebar.
*/
class RoomListItemView : FrameLayout {
lateinit private var roomId: String
lateinit private var roomTypeImage: ImageView
lateinit private var userStatusImage: ImageView
lateinit private var roomNameText: TextView
lateinit private var alertCountText: TextView
lateinit private var privateChannelDrawable: Drawable
lateinit private var publicChannelDrawable: Drawable
lateinit private var livechatChannelDrawable: Drawable
lateinit private var userStatusDrawable: Drawable
constructor(context: Context) : super(context) {
initialize(context)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
initialize(context)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
initialize(context)
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
initialize(context)
}
private fun initialize(context: Context) {
layoutParams = LinearLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT)
val array = context
.theme
.obtainStyledAttributes(intArrayOf(R.attr.selectableItemBackground))
background = array.getDrawable(0)
array.recycle()
View.inflate(context, R.layout.room_list_item, this)
roomTypeImage = findViewById(R.id.image_room_type)
userStatusImage = findViewById(R.id.image_user_status)
roomNameText = findViewById(R.id.text_room_name)
alertCountText = findViewById(R.id.text_alert_count)
privateChannelDrawable = VectorDrawableCompat.create(resources, R.drawable.ic_lock_white_24dp, null)!!
publicChannelDrawable = VectorDrawableCompat.create(resources, R.drawable.ic_hashtag_white_24dp, null)!!
livechatChannelDrawable = VectorDrawableCompat.create(resources, R.drawable.ic_livechat_white_24dp, null)!!
userStatusDrawable = VectorDrawableCompat.create(resources, R.drawable.ic_user_status_black_24dp, null)!!
}
fun setRoomId(roomId: String) {
this.roomId = roomId
}
fun setUnreadCount(count: Int) {
if (count > 0) {
alertCountText.text = count.toString()
alertCountText.visibility = View.VISIBLE
} else {
alertCountText.visibility = View.GONE
}
}
fun setAlert(alert: Boolean) {
alpha = if (alert) 1.0f else 0.62f
}
fun setRoomName(roomName: String) {
roomNameText.text = roomName
}
fun showPrivateChannelIcon() {
roomTypeImage.setImageDrawable(privateChannelDrawable)
userStatusImage.visibility = View.GONE
roomTypeImage.visibility = View.VISIBLE
}
fun showPublicChannelIcon() {
roomTypeImage.setImageDrawable(publicChannelDrawable)
userStatusImage.visibility = View.GONE
roomTypeImage.visibility = View.VISIBLE
}
fun showLivechatChannelIcon() {
roomTypeImage.setImageDrawable(livechatChannelDrawable)
userStatusImage.visibility = View.GONE
roomTypeImage.visibility = View.VISIBLE
}
fun showOnlineUserStatusIcon() {
prepareDrawableAndShow(R.color.color_user_status_online)
}
fun showBusyUserStatusIcon() {
prepareDrawableAndShow(R.color.color_user_status_busy)
}
fun showAwayUserStatusIcon() {
prepareDrawableAndShow(R.color.color_user_status_away)
}
fun showOfflineUserStatusIcon() {
prepareDrawableAndShow(R.color.color_user_status_offline)
}
private fun prepareDrawableAndShow(@ColorRes resId: Int) {
DrawableHelper.wrapDrawable(userStatusDrawable)
DrawableHelper.tintDrawable(userStatusDrawable, context, resId)
userStatusImage.setImageDrawable(userStatusDrawable)
roomTypeImage.visibility = View.GONE
userStatusImage.visibility = View.VISIBLE
}
}
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="10dp"
android:height="10dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FFF44336"
android:pathData="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M 15.274 6.595 C 14.884 4.095 12.724 2.234 10.029 2.234 C 7.049 2.234 4.715 4.506 4.715 7.407 L 4.715 7.669 C 4.714 7.685 4.71 7.701 4.71 7.719 L 4.71 12.84 C 3.363 12.746 2.317 11.627 2.313 10.277 C 2.313 9.344 2.819 8.484 3.634 8.03 C 3.898 7.871 3.988 7.531 3.838 7.262 C 3.688 6.993 3.352 6.891 3.078 7.031 C 1.9 7.687 1.17 8.929 1.17 10.277 C 1.172 12.327 2.834 13.989 4.884 13.991 C 5.011 13.991 5.14 13.981 5.269 13.968 L 5.281 13.968 C 5.3 13.968 5.317 13.965 5.335 13.964 L 5.339 13.964 C 5.631 13.935 5.853 13.69 5.853 13.397 L 5.853 8.344 C 5.855 8.327 5.858 8.311 5.858 8.294 L 5.858 7.407 C 5.858 5.109 7.651 3.377 10.029 3.377 C 12.407 3.377 14.2 5.108 14.2 7.406 L 14.2 7.643 C 14.197 7.667 14.193 7.692 14.193 7.718 L 14.193 13.237 C 14.192 13.252 14.189 13.267 14.189 13.281 L 14.189 14.567 C 14.189 15.445 13.575 16.058 12.697 16.058 L 11.927 16.058 L 11.911 16.06 C 11.692 15.484 11.14 15.102 10.523 15.1 L 9.543 15.1 C 8.72 15.102 8.054 15.768 8.053 16.591 C 8.053 17.414 8.721 18.081 9.543 18.082 L 10.523 18.082 C 11.129 18.082 11.649 17.717 11.883 17.196 C 11.898 17.198 11.912 17.202 11.928 17.202 L 12.698 17.202 C 14.224 17.202 15.331 16.093 15.331 14.567 L 15.331 14.008 C 17.312 13.916 18.872 12.285 18.875 10.302 C 18.872 8.297 17.278 6.656 15.274 6.594 Z M 10.523 16.94 L 9.543 16.94 C 9.352 16.937 9.199 16.783 9.196 16.592 C 9.196 16.403 9.356 16.244 9.544 16.244 L 10.523 16.244 C 10.711 16.244 10.871 16.404 10.871 16.592 C 10.871 16.781 10.711 16.94 10.523 16.94 Z M 15.336 12.866 L 15.336 8.369 C 15.339 8.344 15.343 8.319 15.343 8.294 L 15.343 7.741 C 16.688 7.836 17.731 8.955 17.733 10.303 C 17.733 11.663 16.673 12.776 15.336 12.866 Z" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/badge_color" />
<size android:width="@dimen/badge_size" android:height="@dimen/badge_size"/>
android:shape="rectangle">
<solid
android:color="@color/color_alert" />
<corners
android:radius="4dp" />
</shape>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="?attr/listPreferredItemPaddingLeft"
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
android:paddingRight="?attr/listPreferredItemPaddingRight"
android:paddingEnd="?attr/listPreferredItemPaddingRight">
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingStart="?attr/listPreferredItemPaddingLeft"
android:paddingEnd="?attr/listPreferredItemPaddingRight"
android:paddingLeft="?attr/listPreferredItemPaddingLeft"
android:paddingRight="?attr/listPreferredItemPaddingRight"
android:paddingBottom="10dp">
<FrameLayout
android:layout_width="32dp"
android:layout_height="?attr/listPreferredItemHeightSmall"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp">
<ImageView
android:id="@+id/image_room_type"
android:layout_width="20dp"
android:layout_height="wrap_content"
android:visibility="gone" />
<io.github.yusukeiwaki.android.widget.FontAwesomeTextView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
</FrameLayout>
<ImageView
android:id="@+id/image_user_status"
android:layout_marginLeft="5dp"
android:layout_marginStart="5dp"
android:layout_marginRight="5dp"
android:layout_marginEnd="5dp"
android:layout_width="10dp"
android:layout_height="wrap_content"
android:visibility="gone" />
<TextView
android:id="@+id/text"
android:layout_width="0dp"
android:layout_height="?attr/listPreferredItemHeightSmall"
android:layout_weight="1"
android:gravity="center_vertical"
android:textAppearance="@style/TextAppearance.AppCompat.Body2" />
android:id="@+id/text_room_name"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.9"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:maxLines="1"
android:ellipsize="end"
android:gravity="center_vertical"
android:textAppearance="@style/TextAppearance.AppCompat.Body2" />
<FrameLayout
android:id="@+id/alert_count_container"
android:layout_width="wrap_content"
android:minWidth="20dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:padding="3dp"
android:layout_gravity="center_vertical"
android:background="@drawable/unread_count_background"
android:layout_marginLeft="8dp">
<TextView
android:id="@+id/alert_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14dp"
android:layout_gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
</FrameLayout>
<TextView
android:id="@+id/text_alert_count"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.1"
android:gravity="center"
android:textSize="14sp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
android:background="@drawable/style_alert_count"
android:visibility="gone" />
</LinearLayout>
\ No newline at end of file
......@@ -13,7 +13,7 @@
android:layout_height="10dp"
android:layout_marginRight="10dp"
android:layout_marginEnd="10dp"
android:visibility="gone"/>
android:visibility="gone" />
<ImageView
android:id="@+id/image_room_type"
......@@ -21,7 +21,7 @@
android:layout_height="24dp"
android:layout_marginRight="10dp"
android:layout_marginEnd="10dp"
android:visibility="gone"/>
android:visibility="gone" />
<TextView
android:id="@+id/text_toolbar"
......
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="badge_color">#FFFFFFFF</color>
<dimen name="badge_size">10dp</dimen>
</resources>
\ No newline at end of file
......@@ -6,6 +6,8 @@
<color name="color_timestamp">#FFA8A8A8</color>
<color name="color_embed_hostname">@color/color_timestamp</color>
<color name="color_alert">#FFF44336</color> <!-- Red 500-->
<!-- User status colors-->
<color name="color_user_status_online">#FF4CAF50</color> <!-- Green 500 -->
......
......@@ -8,6 +8,7 @@ public abstract class Room {
public static final String TYPE_CHANNEL = "c";
public static final String TYPE_PRIVATE = "p";
public static final String TYPE_DIRECT_MESSAGE = "d";
public static final String TYPE_LIVECHAT = "l";
public abstract String getId();
......@@ -41,6 +42,10 @@ public abstract class Room {
return TYPE_DIRECT_MESSAGE.equals(getType());
}
public boolean isLivechat() {
return TYPE_LIVECHAT.equals(getType());
}
public static Builder builder() {
return new AutoValue_Room.Builder();
}
......
package chat.rocket.core.models
class RoomSidebar {
lateinit var id: String
lateinit var roomId: String
lateinit var roomName: String
lateinit var type: String
var userStatus: String? = null
var isAlert: Boolean = false
var isFavorite: Boolean = false
var unread: Int = 0
var updateAt: Long = 0
var lastSeen: Long = 0
}
\ No newline at end of file
......@@ -11,6 +11,8 @@ public abstract class Spotlight {
public abstract String getType();
public abstract String getStatus();
public static Spotlight.Builder builder() {
return new AutoValue_Spotlight.Builder();
}
......@@ -24,6 +26,8 @@ public abstract class Spotlight {
public abstract Builder setType(String type);
public abstract Builder setStatus(String status);
public abstract Spotlight build();
}
}
\ No newline at end of file
......@@ -6,6 +6,8 @@ import chat.rocket.core.models.User
interface UserRepository {
fun getAll(): Flowable<List<User>>
fun getCurrent(): Flowable<Optional<User>>
fun getByUsername(username: String): Flowable<Optional<User>>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment