Commit 44241c63 authored by Rafael Kellermann Streit's avatar Rafael Kellermann Streit Committed by GitHub

Merge branch 'develop' into fix/logout-single-instance

parents cf96cd2f 9d8197a7
...@@ -7,6 +7,7 @@ repositories { ...@@ -7,6 +7,7 @@ repositories {
} }
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'me.tatarka.retrolambda' apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'com.jakewharton.hugo' apply plugin: 'com.jakewharton.hugo'
apply plugin: 'com.github.triplet.play' apply plugin: 'com.github.triplet.play'
...@@ -103,7 +104,10 @@ android { ...@@ -103,7 +104,10 @@ android {
manifest.srcFile 'src/release/AndroidManifest.xml' manifest.srcFile 'src/release/AndroidManifest.xml'
} }
test {
test.java.srcDirs += 'src/test/kotlin' test.java.srcDirs += 'src/test/kotlin'
androidTest.java.srcDirs += 'src/androidTest/kotlin'
}
} }
} }
...@@ -137,6 +141,8 @@ dependencies { ...@@ -137,6 +141,8 @@ dependencies {
compile 'com.android.support:multidex:1.0.1' compile 'com.android.support:multidex:1.0.1'
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$rootProject.ext.kotlinVersion" compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$rootProject.ext.kotlinVersion"
testCompile "org.jetbrains.kotlin:kotlin-test:$rootProject.ext.kotlinVersion"
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$rootProject.ext.kotlinVersion"
compile "com.google.firebase:firebase-core:$playLibVersion" compile "com.google.firebase:firebase-core:$playLibVersion"
compile "com.google.firebase:firebase-crash:$playLibVersion" compile "com.google.firebase:firebase-crash:$playLibVersion"
......
...@@ -10,6 +10,13 @@ import okhttp3.OkHttpClient ...@@ -10,6 +10,13 @@ import okhttp3.OkHttpClient
object OkHttpHelper { object OkHttpHelper {
fun getClient(): OkHttpClient {
if (httpClient == null) {
httpClient = OkHttpClient()
}
return httpClient ?: throw AssertionError("httpClient set to null by another thread")
}
fun getClientForUploadFile(): OkHttpClient { fun getClientForUploadFile(): OkHttpClient {
if (httpClientForUploadFile == null) { if (httpClientForUploadFile == null) {
httpClientForUploadFile = OkHttpClient.Builder().build() httpClientForUploadFile = OkHttpClient.Builder().build()
...@@ -41,6 +48,7 @@ object OkHttpHelper { ...@@ -41,6 +48,7 @@ object OkHttpHelper {
return httpClientForWS ?: throw AssertionError("httpClientForWS set to null by another thread") return httpClientForWS ?: throw AssertionError("httpClientForWS set to null by another thread")
} }
private var httpClient: OkHttpClient? = null
private var httpClientForUploadFile: OkHttpClient? = null private var httpClientForUploadFile: OkHttpClient? = null
private var httpClientForDownloadFile: OkHttpClient? = null private var httpClientForDownloadFile: OkHttpClient? = null
private var httpClientForWS: OkHttpClient? = null private var httpClientForWS: OkHttpClient? = null
......
...@@ -32,6 +32,11 @@ ...@@ -32,6 +32,11 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".activity.room.RoomActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="adjustResize"/>
<activity <activity
android:name=".activity.AddServerActivity" android:name=".activity.AddServerActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
......
...@@ -48,7 +48,6 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -48,7 +48,6 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
private SlidingPaneLayout pane; private SlidingPaneLayout pane;
private MainContract.Presenter presenter; private MainContract.Presenter presenter;
@Override
protected int getLayoutContainerForFragment() { protected int getLayoutContainerForFragment() {
return R.id.activity_main_container; return R.id.activity_main_container;
} }
......
package chat.rocket.android.activity.room
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.app.AppCompatActivity
import chat.rocket.android.R
import chat.rocket.android.fragment.chatroom.list.RoomListFragment
import kotlinx.android.synthetic.main.activity_room.*
class RoomActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_room)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true)
val extras = intent.extras
val roomListFragment = RoomListFragment.newInstance(extras.getInt("actionId"),
extras.getString("roomId"),
extras.getString("roomType"),
extras.getString("hostname"),
extras.getString("token"),
extras.getString("userId"))
addFragment(roomListFragment, "roomListFragment")
}
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
private fun addFragment(fragment: Fragment, tag: String) {
supportFragmentManager
.beginTransaction()
.add(R.id.fragment_container, fragment, tag)
.commit()
}
}
\ No newline at end of file
package chat.rocket.android.api.rest
import chat.rocket.android.helper.UrlHelper
import chat.rocket.core.models.Room
import okhttp3.HttpUrl
import okhttp3.Request
/**
* Helper class for dealing with Rest API calls.
*
* @see <a href="https://rocket.chat/docs/developer-guides/rest-api">https://rocket.chat/docs/developer-guides/rest-api</a>.
*/
object RestApiHelper {
/**
* Returns an OkHttp3 request for pinned messages.
*
* @param roomId The ID of the room.
* @param roomType The type of the room.
* @param hostname The server hostname.
* @param token The token.
* @param userId The user Id.
* @param offset The offset to paging which specifies the first entry to return from a collection.
* @return An OkHttp3 request.
*/
fun getRequestForPinnedMessages(roomId: String,
roomType: String,
hostname: String,
token: String,
userId: String,
offset: String): Request {
val parsedHttpUrl = HttpUrl.parse(getEndpointUrlForMessages(roomType, hostname))
?.newBuilder()
?.addQueryParameter("roomId", roomId)
?.addQueryParameter("query", "{\"pinned\":true}")
?.addQueryParameter("offset", offset)
?.build()
return Request.Builder()
.url(parsedHttpUrl)
.get()
.addHeader("X-Auth-Token", token)
.addHeader("X-User-Id", userId)
.build()
}
/**
* Returns an OkHttp3 request for favorite messages.
*
* @param roomId The ID of the room.
* @param roomType The type of the room.
* @param hostname The server hostname.
* @param token The token.
* @param userId The user Id.
* @param offset The offset to paging which specifies the first entry to return from a collection.
* @return An OkHttp3 request.
*/
fun getRequestForFavoriteMessages(roomId: String,
roomType: String,
hostname: String,
token: String,
userId: String,
offset: String): Request {
val parsedHttpUrl = HttpUrl.parse(getEndpointUrlForMessages(roomType, hostname))
?.newBuilder()
?.addQueryParameter("roomId", roomId)
?.addQueryParameter("query", "{\"starred._id\":{\"\$in\":[\"$userId\"] } }")
?.addQueryParameter("offset", offset)
?.build()
return Request.Builder()
.url(parsedHttpUrl)
.get()
.addHeader("X-Auth-Token", token)
.addHeader("X-User-Id", userId)
.build()
}
/**
* Returns an OkHttp3 request for file list.
*
* @param roomId The ID of the room.
* @param roomType The type of the room.
* @param hostname The server hostname.
* @param token The token.
* @param userId The user Id.
* @param offset The offset to paging which specifies the first entry to return from a collection.
* @return An OkHttp3 request.
*/
fun getRequestForFileList(roomId: String,
roomType: String,
hostname: String,
token: String,
userId: String,
offset: String): Request {
val parsedHttpUrl = HttpUrl.parse(getEndpointUrlForFileList(roomType, hostname))
?.newBuilder()
?.addQueryParameter("roomId", roomId)
?.addQueryParameter("offset", offset)
?.build()
return Request.Builder()
.url(parsedHttpUrl)
.get()
.addHeader("X-Auth-Token", token)
.addHeader("X-User-Id", userId)
.build()
}
/**
* Returns an OkHttp3 request for member list.
*
* @param roomId The ID of the room.
* @param roomType The type of the room.
* @param hostname The server hostname.
* @param token The token.
* @param userId The user Id.
* @param offset The offset to paging which specifies the first entry to return from a collection.
* @return An OkHttp3 request.
*/
fun getRequestForMemberList(roomId: String,
roomType: String,
hostname: String,
token: String,
userId: String,
offset: String): Request {
val parsedHttpUrl = HttpUrl.parse(getEndpointUrlForMemberList(roomType, hostname))
?.newBuilder()
?.addQueryParameter("roomId", roomId)
?.addQueryParameter("offset", offset)
?.build()
return Request.Builder()
.url(parsedHttpUrl)
.get()
.addHeader("X-Auth-Token", token)
.addHeader("X-User-Id", userId)
.build()
}
/**
* Returns a Rest API endpoint URL for favorite or pinned messages accordingly with the room type and the server hostname.
*
* @param roomType The type of the room.
* @param hostname The server hostname.
* @return A Rest API URL endpoint.
*/
fun getEndpointUrlForMessages(roomType: String, hostname: String): String =
UrlHelper.getSafeHostname(hostname) + getRestApiUrlForMessages(roomType)
/**
* Returns a Rest API endpoint URL for file list accordingly with the room type and the server hostname.
*
* @param roomType The type of the room.
* @param hostname The server hostname.
* @return A Rest API URL endpoint.
*/
fun getEndpointUrlForFileList(roomType: String, hostname: String): String =
UrlHelper.getSafeHostname(hostname) + getRestApiUrlForFileList(roomType)
/**
* Returns a Rest API endpoint URL for member list accordingly with the room type and the server hostname.
*
* @param roomType The type of the room.
* @param hostname The server hostname.
* @return A Rest API URL endpoint.
*/
fun getEndpointUrlForMemberList(roomType: String, hostname: String): String =
UrlHelper.getSafeHostname(hostname) + getRestApiUrlForMemberList(roomType)
/**
* Returns the correspondent Rest API URL accordingly with the room type to get its favorite or pinned messages.
*
* @param roomType The type of the room.
* @return A Rest API URL or null if the room type does not match.
*/
fun getRestApiUrlForMessages(roomType: String): String? {
var restApiUrl: String? = null
when (roomType) {
Room.TYPE_CHANNEL -> restApiUrl = "/api/v1/channels.messages"
Room.TYPE_PRIVATE -> restApiUrl = "/api/v1/groups.messages"
Room.TYPE_DIRECT_MESSAGE -> restApiUrl = "/api/v1/dm.messages"
}
return restApiUrl
}
/**
* Returns the correspondent Rest API URL accordingly with the room type to get its file list.
*
* @param roomType The type of the room.
* @return A Rest API URL or null if the room type does not match.
*/
fun getRestApiUrlForFileList(roomType: String): String? {
var restApiUrl: String? = null
when (roomType) {
Room.TYPE_CHANNEL -> restApiUrl = "/api/v1/channels.files"
Room.TYPE_PRIVATE -> restApiUrl = "/api/v1/groups.files"
Room.TYPE_DIRECT_MESSAGE -> restApiUrl = "/api/v1/dm.files"
}
return restApiUrl
}
/**
* Returns the correspondent Rest API URL accordingly with the room type to get its members list.
*
* @param roomType The type of the room.
* @return A Rest API URL or null if the room type does not match.
*/
fun getRestApiUrlForMemberList(roomType: String): String? {
var restApiUrl: String? = null
when (roomType) {
Room.TYPE_CHANNEL -> restApiUrl = "/api/v1/channels.members"
Room.TYPE_PRIVATE -> restApiUrl = "/api/v1/groups.members"
Room.TYPE_DIRECT_MESSAGE -> restApiUrl = "/api/v1/dm.members"
}
return restApiUrl
}
}
\ No newline at end of file
...@@ -21,4 +21,12 @@ public class RocketChatAbsoluteUrl implements AbsoluteUrl { ...@@ -21,4 +21,12 @@ public class RocketChatAbsoluteUrl implements AbsoluteUrl {
public String from(String url) { public String from(String url) {
return url.startsWith("/") ? baseUrl + url + "?rc_uid=" + userId + "&rc_token=" + token : url; return url.startsWith("/") ? baseUrl + url + "?rc_uid=" + userId + "&rc_token=" + token : url;
} }
public String getUserId() {
return userId;
}
public String getToken() {
return token;
}
} }
\ No newline at end of file
...@@ -13,8 +13,6 @@ import android.support.v13.view.inputmethod.InputContentInfoCompat; ...@@ -13,8 +13,6 @@ import android.support.v13.view.inputmethod.InputContentInfoCompat;
import android.support.v4.app.DialogFragment; import android.support.v4.app.DialogFragment;
import android.support.v4.os.BuildCompat; import android.support.v4.os.BuildCompat;
import android.support.v4.util.Pair; import android.support.v4.util.Pair;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.SlidingPaneLayout; import android.support.v4.widget.SlidingPaneLayout;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
...@@ -22,23 +20,20 @@ import android.support.v7.widget.RecyclerView; ...@@ -22,23 +20,20 @@ import android.support.v7.widget.RecyclerView;
import android.view.View; import android.view.View;
import com.hadisatrio.optional.Optional; import com.hadisatrio.optional.Optional;
import com.jakewharton.rxbinding2.support.v4.widget.RxDrawerLayout;
import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import chat.rocket.android.BackgroundLooper; import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.activity.room.RoomActivity;
import chat.rocket.android.api.MethodCallHelper; import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.fragment.chatroom.dialog.FileUploadProgressDialogFragment; import chat.rocket.android.fragment.chatroom.dialog.FileUploadProgressDialogFragment;
import chat.rocket.android.fragment.chatroom.dialog.MessageOptionsDialogFragment; import chat.rocket.android.fragment.chatroom.dialog.MessageOptionsDialogFragment;
import chat.rocket.android.fragment.chatroom.dialog.UsersOfRoomDialogFragment;
import chat.rocket.android.fragment.sidebar.SidebarMainFragment; import chat.rocket.android.fragment.sidebar.SidebarMainFragment;
import chat.rocket.android.helper.AbsoluteUrlHelper; import chat.rocket.android.helper.AbsoluteUrlHelper;
import chat.rocket.android.helper.FileUploadHelper; import chat.rocket.android.helper.FileUploadHelper;
import chat.rocket.android.helper.LoadMoreScrollListener; import chat.rocket.android.helper.LoadMoreScrollListener;
import chat.rocket.android.helper.Logger;
import chat.rocket.android.helper.OnBackPressListener; import chat.rocket.android.helper.OnBackPressListener;
import chat.rocket.android.helper.RecyclerViewAutoScrollManager; import chat.rocket.android.helper.RecyclerViewAutoScrollManager;
import chat.rocket.android.helper.RecyclerViewScrolledToBottomListener; import chat.rocket.android.helper.RecyclerViewScrolledToBottomListener;
...@@ -52,10 +47,8 @@ import chat.rocket.android.layouthelper.extra_action.AbstractExtraActionItem; ...@@ -52,10 +47,8 @@ import chat.rocket.android.layouthelper.extra_action.AbstractExtraActionItem;
import chat.rocket.android.layouthelper.extra_action.MessageExtraActionBehavior; import chat.rocket.android.layouthelper.extra_action.MessageExtraActionBehavior;
import chat.rocket.android.layouthelper.extra_action.upload.AbstractUploadActionItem; import chat.rocket.android.layouthelper.extra_action.upload.AbstractUploadActionItem;
import chat.rocket.android.layouthelper.extra_action.upload.AudioUploadActionItem; import chat.rocket.android.layouthelper.extra_action.upload.AudioUploadActionItem;
import chat.rocket.android.layouthelper.extra_action.upload.FileUploadActionItem;
import chat.rocket.android.layouthelper.extra_action.upload.ImageUploadActionItem; import chat.rocket.android.layouthelper.extra_action.upload.ImageUploadActionItem;
import chat.rocket.android.layouthelper.extra_action.upload.VideoUploadActionItem; import chat.rocket.android.layouthelper.extra_action.upload.VideoUploadActionItem;
import chat.rocket.android.log.RCLog;
import chat.rocket.android.renderer.RocketChatUserStatusProvider; import chat.rocket.android.renderer.RocketChatUserStatusProvider;
import chat.rocket.android.service.ConnectivityManager; import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.android.service.temp.DeafultTempSpotlightRoomCaller; import chat.rocket.android.service.temp.DeafultTempSpotlightRoomCaller;
...@@ -104,7 +97,10 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -104,7 +97,10 @@ public class RoomFragment extends AbstractChatRoomFragment implements
private static final String ROOM_ID = "roomId"; private static final String ROOM_ID = "roomId";
private String hostname; private String hostname;
private String token;
private String userId;
private String roomId; private String roomId;
private String roomType;
private LoadMoreScrollListener scrollListener; private LoadMoreScrollListener scrollListener;
private MessageFormManager messageFormManager; private MessageFormManager messageFormManager;
private RecyclerView messageRecyclerView; private RecyclerView messageRecyclerView;
...@@ -128,10 +124,13 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -128,10 +124,13 @@ public class RoomFragment extends AbstractChatRoomFragment implements
private Message edittingMessage = null; private Message edittingMessage = null;
private RoomToolbar toolbar;
private SlidingPaneLayout pane; private SlidingPaneLayout pane;
private SidebarMainFragment sidebarFragment; private SidebarMainFragment sidebarFragment;
public RoomFragment() {} public RoomFragment() {
}
/** /**
* create fragment with roomId. * create fragment with roomId.
...@@ -150,6 +149,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -150,6 +149,7 @@ public class RoomFragment extends AbstractChatRoomFragment implements
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
Bundle args = getArguments(); Bundle args = getArguments();
hostname = args.getString(HOSTNAME); hostname = args.getString(HOSTNAME);
...@@ -242,18 +242,17 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -242,18 +242,17 @@ public class RoomFragment extends AbstractChatRoomFragment implements
} }
}; };
setupToolbar();
setupSidebar(); setupSidebar();
setupSideMenu();
setupMessageComposer(); setupMessageComposer();
setupMessageActions(); setupMessageActions();
} }
private void setupMessageActions() { private void setupMessageActions() {
extraActionItems = new ArrayList<>(4); // fixed number as of now extraActionItems = new ArrayList<>(3); // fixed number as of now
extraActionItems.add(new ImageUploadActionItem()); extraActionItems.add(new ImageUploadActionItem());
extraActionItems.add(new AudioUploadActionItem()); extraActionItems.add(new AudioUploadActionItem());
extraActionItems.add(new VideoUploadActionItem()); extraActionItems.add(new VideoUploadActionItem());
extraActionItems.add(new FileUploadActionItem());
} }
private void scrollToLatestMessage() { private void scrollToLatestMessage() {
...@@ -305,37 +304,40 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -305,37 +304,40 @@ public class RoomFragment extends AbstractChatRoomFragment implements
return true; return true;
} }
private void setupSideMenu() { private void setupToolbar() {
View sideMenu = rootView.findViewById(R.id.room_side_menu); toolbar = getActivity().findViewById(R.id.activity_main_toolbar);
sideMenu.findViewById(R.id.btn_users).setOnClickListener(view -> { toolbar.getMenu().clear();
UsersOfRoomDialogFragment.create(roomId, hostname) toolbar.inflateMenu(R.menu.menu_room);
.show(getFragmentManager(), "UsersOfRoomDialogFragment");
closeSideMenuIfNeeded();
});
DrawerLayout drawerLayout = rootView.findViewById(R.id.drawer_layout); toolbar.setNavigationOnClickListener(view -> {
if (drawerLayout != null && pane != null) { if (pane.isSlideable() && !pane.isOpen()) {
compositeDisposable.add(RxDrawerLayout.drawerOpen(drawerLayout, GravityCompat.END) pane.openPane();
.compose(bindToLifecycle())
.subscribe(
opened -> {
try {
Field fieldSlidable = pane.getClass().getDeclaredField("mCanSlide");
fieldSlidable.setAccessible(true);
fieldSlidable.setBoolean(pane, !opened);
} catch (Exception exception) {
RCLog.w(exception);
} }
}, });
Logger::report
) toolbar.setOnMenuItemClickListener(menuItem -> {
); switch (menuItem.getItemId()) {
case R.id.action_pinned_messages:
showRoomListFragment(R.id.action_pinned_messages);
break;
case R.id.action_favorite_messages:
showRoomListFragment(R.id.action_favorite_messages);
break;
// case R.id.action_file_list:
// showRoomListFragment(R.id.action_file_list);
// break;
case R.id.action_member_list:
showRoomListFragment(R.id.action_member_list);
break;
default:
return super.onOptionsItemSelected(menuItem);
} }
return true;
});
} }
private void setupSidebar() { private void setupSidebar() {
SlidingPaneLayout subPane = getActivity().findViewById(R.id.sub_sliding_pane); 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); sidebarFragment = (SidebarMainFragment) getActivity().getSupportFragmentManager().findFragmentById(R.id.sidebar_fragment_container);
pane.setPanelSlideListener(new SlidingPaneLayout.PanelSlideListener() { pane.setPanelSlideListener(new SlidingPaneLayout.PanelSlideListener() {
...@@ -360,27 +362,12 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -360,27 +362,12 @@ public class RoomFragment extends AbstractChatRoomFragment implements
closeUserActionContainer(); closeUserActionContainer();
} }
}); });
toolbar.setNavigationOnClickListener(view -> {
if (pane.isSlideable() && !pane.isOpen()) {
pane.openPane();
}
});
} }
public void closeUserActionContainer() { public void closeUserActionContainer() {
sidebarFragment.closeUserActionContainer(); sidebarFragment.closeUserActionContainer();
} }
private boolean closeSideMenuIfNeeded() {
DrawerLayout drawerLayout = rootView.findViewById(R.id.drawer_layout);
if (drawerLayout != null && drawerLayout.isDrawerOpen(GravityCompat.END)) {
drawerLayout.closeDrawer(GravityCompat.END);
return true;
}
return false;
}
private void setupMessageComposer() { private void setupMessageComposer() {
final MessageFormLayout messageFormLayout = rootView.findViewById(R.id.messageComposer); final MessageFormLayout messageFormLayout = rootView.findViewById(R.id.messageComposer);
messageFormManager = new MessageFormManager(messageFormLayout, this::showExtraActionSelectionDialog); messageFormManager = new MessageFormManager(messageFormLayout, this::showExtraActionSelectionDialog);
...@@ -426,7 +413,8 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -426,7 +413,8 @@ public class RoomFragment extends AbstractChatRoomFragment implements
); );
} }
}, },
Logger::report throwable -> {
}
); );
compositeDisposable.add(disposable); compositeDisposable.add(disposable);
...@@ -470,7 +458,6 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -470,7 +458,6 @@ public class RoomFragment extends AbstractChatRoomFragment implements
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
presenter.bindView(this); presenter.bindView(this);
closeSideMenuIfNeeded();
} }
@Override @Override
...@@ -501,9 +488,8 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -501,9 +488,8 @@ public class RoomFragment extends AbstractChatRoomFragment implements
if (edittingMessage != null) { if (edittingMessage != null) {
edittingMessage = null; edittingMessage = null;
messageFormManager.clearComposingText(); messageFormManager.clearComposingText();
return true;
} }
return closeSideMenuIfNeeded(); return false;
} }
@Override @Override
...@@ -537,7 +523,6 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -537,7 +523,6 @@ public class RoomFragment extends AbstractChatRoomFragment implements
try { try {
inputContentInfo.requestPermission(); inputContentInfo.requestPermission();
} catch (Exception e) { } catch (Exception e) {
Logger.report(e);
return false; return false;
} }
} }
...@@ -552,7 +537,6 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -552,7 +537,6 @@ public class RoomFragment extends AbstractChatRoomFragment implements
try { try {
inputContentInfo.releasePermission(); inputContentInfo.releasePermission();
} catch (Exception e) { } catch (Exception e) {
Logger.report(e);
} }
return true; return true;
...@@ -568,11 +552,14 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -568,11 +552,14 @@ public class RoomFragment extends AbstractChatRoomFragment implements
@Override @Override
public void setupWith(RocketChatAbsoluteUrl rocketChatAbsoluteUrl) { public void setupWith(RocketChatAbsoluteUrl rocketChatAbsoluteUrl) {
token = rocketChatAbsoluteUrl.getToken();
userId = rocketChatAbsoluteUrl.getUserId();
messageListAdapter.setAbsoluteUrl(rocketChatAbsoluteUrl); messageListAdapter.setAbsoluteUrl(rocketChatAbsoluteUrl);
} }
@Override @Override
public void render(Room room) { public void render(Room room) {
roomType = room.getType();
setToolbarTitle(room.getName()); setToolbarTitle(room.getName());
boolean unreadMessageExists = room.isAlert(); boolean unreadMessageExists = room.isAlert();
...@@ -658,4 +645,14 @@ public class RoomFragment extends AbstractChatRoomFragment implements ...@@ -658,4 +645,14 @@ public class RoomFragment extends AbstractChatRoomFragment implements
edittingMessage = message; edittingMessage = message;
messageFormManager.setEditMessage(message.getMessage()); messageFormManager.setEditMessage(message.getMessage());
} }
private void showRoomListFragment(int actionId) {
Intent intent = new Intent(getActivity(), RoomActivity.class).putExtra("actionId", actionId)
.putExtra("roomId", roomId)
.putExtra("roomType", roomType)
.putExtra("hostname", hostname)
.putExtra("token", token)
.putExtra("userId", userId);
startActivity(intent);
}
} }
\ No newline at end of file
package chat.rocket.android.fragment.chatroom.dialog;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
import com.hadisatrio.optional.Optional;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.R;
import chat.rocket.android.fragment.chatroom.RocketChatAbsoluteUrl;
import chat.rocket.android.helper.AbsoluteUrlHelper;
import chat.rocket.android.helper.LogIfError;
import chat.rocket.android.helper.Logger;
import chat.rocket.android.layouthelper.chatroom.dialog.RoomUserAdapter;
import chat.rocket.android.log.RCLog;
import chat.rocket.core.SyncState;
import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.persistence.realm.models.internal.GetUsersOfRoomsProcedure;
import chat.rocket.persistence.realm.RealmObjectObserver;
import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.persistence.realm.repositories.RealmServerInfoRepository;
import chat.rocket.persistence.realm.repositories.RealmSessionRepository;
import chat.rocket.persistence.realm.repositories.RealmUserRepository;
/**
* Dialog to show members in a room.
*/
public class UsersOfRoomDialogFragment extends AbstractChatRoomDialogFragment {
private String hostname;
private RealmObjectObserver<GetUsersOfRoomsProcedure> procedureObserver;
private int previousSyncState;
private CompositeDisposable compositeDisposable = new CompositeDisposable();
public UsersOfRoomDialogFragment() {
}
/**
* create UsersOfRoomDialogFragment with required parameters.
*/
public static UsersOfRoomDialogFragment create(String roomId, String hostname) {
Bundle args = new Bundle();
args.putString("hostname", hostname);
args.putString("roomId", roomId);
UsersOfRoomDialogFragment fragment = new UsersOfRoomDialogFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
procedureObserver = realmHelper
.createObjectObserver(realm ->
realm.where(GetUsersOfRoomsProcedure.class).equalTo("roomId", roomId))
.setOnUpdateListener(this::onUpdateGetUsersOfRoomProcedure);
previousSyncState = SyncState.NOT_SYNCED;
if (savedInstanceState == null) {
requestGetUsersOfRoom();
}
}
@Override
protected void handleArgs(@NonNull Bundle args) {
super.handleArgs(args);
hostname = args.getString("hostname");
}
@Override
protected int getLayout() {
return R.layout.dialog_users_of_room;
}
@Override
protected void onSetupDialog() {
AbsoluteUrlHelper absoluteUrlHelper = new AbsoluteUrlHelper(
hostname,
new RealmServerInfoRepository(),
new RealmUserRepository(hostname),
new SessionInteractor(new RealmSessionRepository(hostname))
);
compositeDisposable.add(
absoluteUrlHelper.getRocketChatAbsoluteUrl()
.subscribeOn(AndroidSchedulers.from(BackgroundLooper.get()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
this::setupView,
Logger::report
)
);
}
private void setupView(Optional<RocketChatAbsoluteUrl> rocketChatAbsoluteUrlOptional) {
compositeDisposable.clear();
if (!rocketChatAbsoluteUrlOptional.isPresent()) {
return;
}
RecyclerView recyclerView = (RecyclerView) getDialog().findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
recyclerView.setAdapter(
new RoomUserAdapter(getContext(), realmHelper, rocketChatAbsoluteUrlOptional.get(), hostname));
}
private void requestGetUsersOfRoom() {
realmHelper.executeTransaction(realm -> {
realm.createOrUpdateObjectFromJson(GetUsersOfRoomsProcedure.class, new JSONObject()
.put("roomId", roomId)
.put("syncstate", SyncState.NOT_SYNCED)
.put("showAll", true));
return null;
}).onSuccessTask(task -> {
ConnectivityManager.getInstance(getContext().getApplicationContext())
.keepAliveServer();
return task;
}).continueWith(new LogIfError());
}
@Override
public void onResume() {
super.onResume();
procedureObserver.sub();
}
@Override
public void onPause() {
procedureObserver.unsub();
super.onPause();
}
private void onUpdateGetUsersOfRoomProcedure(GetUsersOfRoomsProcedure procedure) {
if (procedure == null) {
return;
}
int syncState = procedure.getSyncState();
if (previousSyncState != syncState) {
onSyncStateUpdated(syncState);
previousSyncState = syncState;
}
if (syncState == SyncState.SYNCED) {
onRenderTotalCount(procedure.getTotal());
try {
JSONArray array = new JSONArray(procedure.getRecords());
ArrayList<String> users = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
Object userObject = array.get(i);
if (userObject instanceof JSONObject) {
JSONObject user = (JSONObject) userObject;
users.add(user.getString("username"));
} else {
users.add((String) userObject);
}
}
onRenderUsers(users);
} catch (JSONException exception) {
RCLog.e(exception);
}
}
}
/**
* called only if prevSyncstate != newSyncstate.
*/
private void onSyncStateUpdated(int newSyncState) {
boolean show = newSyncState == SyncState.NOT_SYNCED || newSyncState == SyncState.SYNCING;
getDialog().findViewById(R.id.waiting).setVisibility(show ? View.VISIBLE : View.GONE);
}
/**
* called only if syncstate = SYNCED.
*/
private void onRenderTotalCount(long total) {
TextView userCount = (TextView) getDialog().findViewById(R.id.room_user_count);
userCount.setText(getResources().getQuantityString(R.plurals.fmt_room_user_count, (int) total, total));
}
/**
* called only if syncstate = SYNCED.
*/
private void onRenderUsers(List<String> usernames) {
RecyclerView recyclerView = (RecyclerView) getDialog().findViewById(R.id.recyclerview);
if (recyclerView != null && recyclerView.getAdapter() instanceof RoomUserAdapter) {
((RoomUserAdapter) recyclerView.getAdapter()).setUsernames(usernames);
}
}
}
package chat.rocket.android.fragment.chatroom.list
import chat.rocket.core.models.Message
import chat.rocket.core.models.User
/**
* Created by Filipe de Lima Brito (filipedelimabrito@gmail.com) on 9/22/17.
*/
interface RoomListContract {
interface View {
/**
* Shows a pinned message list of a room.
*
* @param dataSet The pinned message data set to show.
* @param total The total number of pinned messages.
*/
fun showPinnedMessages(dataSet: ArrayList<Message>, total: String)
/**
* Shows a favorite message list of a room.
*
* @param dataSet The favorite message data set to show.
* @param total The total number of favorite messages.
*/
fun showFavoriteMessages(dataSet: ArrayList<Message>, total: String)
/**
* Shows a file list of a room.
*
* @param dataSet The file data set to show.
* @param total The total number of files.
*/
fun showFileList(dataSet: ArrayList<String>, total: String)
/**
* Shows a list of members of a room.
*
* @param dataSet The member data set to show.
* @param total The total number of members.
*/
fun showMemberList(dataSet: ArrayList<User>, total: String)
/**
* Shows a message (e.g. An error or successful message after a request).
*
* @param message The message to show.
*/
fun showMessage(message: String)
/**
* Shows a waiting view whenever a (long) process is taken.
*
* @param shouldShow The Boolean value that indicates whether the view should be showed.
*/
fun showWaitingView(shouldShow: Boolean)
}
interface Presenter {
/**
* Requests the pinned messages of a room.
*
* @param roomId The room ID to process the request.
* @param roomType The room type to process the request.
* @param hostname The server hostname to process the request.
* @param token The token to process the request.
* @param userId The user ID to process the request.
* @param offset The offset to process the request.
*/
fun requestPinnedMessages(roomId: String,
roomType: String,
hostname: String,
token: String,
userId: String,
offset: Int)
/**
* Requests the favorite messages of a room.
*
* @param roomId The room ID to process the request.
* @param roomType The room type to process the request.
* @param hostname The server hostname to process the request.
* @param token The token to process the request.
* @param userId The user ID to process the request.
* @param offset The offset to process the request.
*/
fun requestFavoriteMessages(roomId: String,
roomType: String,
hostname: String,
token: String,
userId: String,
offset: Int)
/**
* Requests the file list of a room.
*
* @param roomId The room ID to process the request.
* @param roomType The room type to process the request.
* @param hostname The server hostname to process the request.
* @param token The token to process the request.
* @param userId The user ID to process the request.
* @param offset The offset to process the request.
*/
fun requestFileList(roomId: String,
roomType: String,
hostname: String,
token: String,
userId: String,
offset: Int)
/**
* Requests the member list of a room.
*
* @param roomId The room ID to process the request.
* @param roomType The room type to process the request.
* @param hostname The server hostname to process the request.
* @param token The token to process the request.
* @param userId The user ID to process the request.
* @param offset The offset to process the request.
*/
fun requestMemberList(roomId: String,
roomType: String,
hostname: String,
token: String,
userId: String,
offset: Int)
/**
* Immediately cancels any running request.
*/
fun cancelRequest()
}
}
\ No newline at end of file
package chat.rocket.android.fragment.chatroom.list
import android.os.Bundle
import android.support.v4.app.Fragment
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import chat.rocket.android.R
import chat.rocket.android.helper.EndlessRecyclerViewScrollListener
import chat.rocket.android.layouthelper.chatroom.list.RoomMemberListAdapter
import chat.rocket.android.layouthelper.chatroom.list.RoomMessagesAdapter
import chat.rocket.core.models.Message
import chat.rocket.core.models.User
import kotlinx.android.synthetic.main.fragment_room_list.*
import java.util.*
/**
* Created by Filipe de Lima Brito (filipedelimabrito@gmail.com) on 9/22/17.
*/
class RoomListFragment : Fragment(), RoomListContract.View {
companion object {
fun newInstance(actionId: Int,
roomId: String,
roomType: String,
hostname: String,
token: String,
userId: String): RoomListFragment {
val args = Bundle()
args.putInt("actionId", actionId)
args.putString("roomId", roomId)
args.putString("roomType", roomType)
args.putString("hostname", hostname)
args.putString("token", token)
args.putString("userId", userId)
val roomFileListDialogFragment = RoomListFragment()
roomFileListDialogFragment.arguments = args
return roomFileListDialogFragment
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity.title = ""
actionId = arguments.getInt("actionId")
roomId = arguments.getString("roomId")
roomType = arguments.getString("roomType")
hostname = arguments.getString("hostname")
token = arguments.getString("token")
userId = arguments.getString("userId")
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? = inflater?.inflate(R.layout.fragment_room_list, container, false)
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
presenter = RoomListPresenter(context, this)
}
override fun onResume() {
super.onResume()
if (!isDataRequested) {
requestData(0)
isDataRequested = true
}
}
override fun onPause() {
super.onPause()
presenter.cancelRequest()
}
private fun requestData(offset: Int) {
when (actionId) {
R.id.action_pinned_messages -> {
presenter.requestPinnedMessages(roomId,
roomType,
hostname,
token,
userId,
offset)
}
R.id.action_favorite_messages -> {
presenter.requestFavoriteMessages(roomId,
roomType,
hostname,
token,
userId,
offset)
}
R.id.action_member_list -> {
presenter.requestMemberList(roomId,
roomType,
hostname,
token,
userId,
offset)
}
}
}
override fun showPinnedMessages(dataSet: ArrayList<Message>, total: String) {
activity.title = getString(R.string.fragment_room_list_pinned_message_title, total)
if (recyclerView.adapter == null) {
recyclerView.adapter = RoomMessagesAdapter(dataSet, hostname, context)
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
recyclerView.layoutManager = linearLayoutManager
if (dataSet.size >= 50) {
recyclerView.addOnScrollListener(object : EndlessRecyclerViewScrollListener(linearLayoutManager) {
override fun onLoadMore(page: Int, totalItemsCount: Int, recyclerView: RecyclerView?) {
loadNextDataFromApi(page)
}
})
}
} else {
(recyclerView.adapter as RoomMessagesAdapter).addDataSet(dataSet)
}
}
override fun showFavoriteMessages(dataSet: ArrayList<Message>, total: String) {
activity.title = getString(R.string.fragment_room_list_favorite_message_title, total)
if (recyclerView.adapter == null) {
recyclerView.adapter = RoomMessagesAdapter(dataSet, hostname, context)
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
recyclerView.layoutManager = linearLayoutManager
if (dataSet.size >= 50) {
recyclerView.addOnScrollListener(object : EndlessRecyclerViewScrollListener(linearLayoutManager) {
override fun onLoadMore(page: Int, totalItemsCount: Int, recyclerView: RecyclerView?) {
loadNextDataFromApi(page)
}
})
}
} else {
(recyclerView.adapter as RoomMessagesAdapter).addDataSet(dataSet)
}
}
// TODO (after REST api fixes)
override fun showFileList(dataSet: ArrayList<String>, total: String) {}
override fun showMemberList(dataSet: ArrayList<User>, total: String) {
activity.title = getString(R.string.fragment_room_list_member_list_title, total)
if (recyclerView.adapter == null) {
recyclerView.adapter = RoomMemberListAdapter(dataSet, hostname, context)
val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
recyclerView.layoutManager = linearLayoutManager
if (dataSet.size >= 50) {
recyclerView.addOnScrollListener(object : EndlessRecyclerViewScrollListener(linearLayoutManager) {
override fun onLoadMore(page: Int, totalItemsCount: Int, recyclerView: RecyclerView?) {
loadNextDataFromApi(page)
}
})
}
} else {
(recyclerView.adapter as RoomMemberListAdapter).addDataSet(dataSet)
}
}
override fun showMessage(message: String) {
messageText.text = message
messageText.visibility = View.VISIBLE
}
override fun showWaitingView(shouldShow: Boolean) {
if (shouldShow) {
waitingView.visibility = View.VISIBLE
} else {
waitingView.visibility = View.GONE
}
}
private fun loadNextDataFromApi(page: Int) {
requestData(page * 50)
}
private var actionId: Int = 0
private lateinit var roomId: String
private lateinit var roomType: String
private lateinit var hostname: String
private lateinit var token: String
private lateinit var userId: String
private lateinit var presenter: RoomListContract.Presenter
private var isDataRequested: Boolean = false
}
\ No newline at end of file
package chat.rocket.android.fragment.chatroom.list
import android.content.Context
import android.os.Handler
import chat.rocket.android.R
import chat.rocket.android.api.rest.RestApiHelper
import chat.rocket.android.helper.OkHttpHelper
import chat.rocket.core.SyncState
import chat.rocket.core.models.Message
import chat.rocket.core.models.User
import okhttp3.Call
import okhttp3.Callback
import okhttp3.Response
import org.json.JSONObject
import java.io.IOException
import java.sql.Timestamp
/**
* Created by Filipe de Lima Brito (filipedelimabrito@gmail.com) on 9/22/17.
*/
class RoomListPresenter(val context: Context, val view: RoomListContract.View) : RoomListContract.Presenter {
override fun requestPinnedMessages(roomId: String,
roomType: String,
hostname: String,
token: String,
userId: String,
offset: Int) {
view.showWaitingView(true)
OkHttpHelper.getClient()
.newCall(RestApiHelper.getRequestForPinnedMessages(roomId,
roomType,
hostname,
token,
userId,
offset.toString()))
.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
if (!call.isCanceled) {
val message = e.message
if (message != null) {
showErrorMessage(message)
}
}
}
@Throws(IOException::class)
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
val result = response.body()?.string()
if (result != null) {
handleMessagesJson(result, true)
}
} else {
showErrorMessage(response.message())
}
}
})
}
override fun requestFavoriteMessages(roomId: String,
roomType: String,
hostname: String,
token: String,
userId: String,
offset: Int) {
view.showWaitingView(true)
OkHttpHelper.getClient()
.newCall(RestApiHelper.getRequestForFavoriteMessages(roomId,
roomType,
hostname,
token,
userId,
offset.toString()))
.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
if (!call.isCanceled) {
val message = e.message
if (message != null) {
showErrorMessage(message)
}
}
}
@Throws(IOException::class)
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
val result = response.body()?.string()
if (result != null) {
handleMessagesJson(result, false)
}
} else {
showErrorMessage(response.message())
}
}
})
}
// TODO (after the REST api fixes)
override fun requestFileList(roomId: String,
roomType: String,
hostname: String,
token: String,
userId: String,
offset: Int) {}
override fun requestMemberList(roomId: String,
roomType: String,
hostname: String,
token: String,
userId: String,
offset: Int) {
view.showWaitingView(true)
OkHttpHelper.getClient()
.newCall(RestApiHelper.getRequestForMemberList(roomId,
roomType,
hostname,
token,
userId,
offset.toString()))
.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
if (!call.isCanceled) {
val message = e.message
if (message != null) {
showErrorMessage(message)
}
}
}
@Throws(IOException::class)
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
val result = response.body()?.string()
if (result != null) {
handleMembersJson(result)
}
} else {
showErrorMessage(response.message())
}
}
})
}
override fun cancelRequest() {
OkHttpHelper.getClient().dispatcher().cancelAll()
}
private fun handleMessagesJson(json: String, isPinnedMessage: Boolean) {
val jSONObject = JSONObject(json)
val messagesJSONArray = jSONObject.getJSONArray("messages")
val total = messagesJSONArray.length()
val dataSet = ArrayList<Message>(total)
(0 until total).mapTo(dataSet) {
val messageJsonObject = messagesJSONArray.getJSONObject(it)
val userJsonObject = messageJsonObject.getJSONObject("u")
val timestampString = messageJsonObject.optString("ts")
val timestamp = if (timestampString.isBlank()) {
0
} else {
Timestamp.valueOf(timestampString.replace("T", " ").replace("Z", "")).time
}
val editedAtString = messageJsonObject.optString("_updatedAt")
val editedAt = if (editedAtString.isBlank()) {
0
} else {
Timestamp.valueOf(editedAtString.replace("T", " ").replace("Z", "")).time
}
Message.builder()
.setId(messageJsonObject.optString("_id"))
.setRoomId(messageJsonObject.optString("rid"))
.setMessage(messageJsonObject.optString("msg"))
.setUser(getUserFromJsonObject(userJsonObject))
.setTimestamp(timestamp)
.setEditedAt(editedAt)
.setGroupable(messageJsonObject.optBoolean("groupable"))
.setSyncState(SyncState.SYNCED)
.build()
}
if (dataSet.isEmpty() && !hasItem) {
showEmptyViewMessage(context.getString(R.string.fragment_room_list_no_favorite_message_to_show))
} else {
if (dataSet.isNotEmpty()) {
hasItem = true
if (isPinnedMessage) {
showPinnedMessageList(dataSet, jSONObject.optString("total"))
} else {
showFavoriteMessageList(dataSet, jSONObject.optString("total"))
}
}
}
}
private fun handleMembersJson(json: String) {
val jsonObject = JSONObject(json)
val membersJsonArray = jsonObject.getJSONArray("members")
val total = membersJsonArray.length()
val dataSet = ArrayList<User>(total)
(0 until total).mapTo(dataSet) {
getUserFromJsonObject(membersJsonArray.getJSONObject(it))
}
if (dataSet.isEmpty() && !hasItem) {
showEmptyViewMessage(context.getString(R.string.fragment_room_list_no_member_list_to_show))
} else {
if (dataSet.isNotEmpty()) {
hasItem = true
showMemberList(dataSet, jsonObject.optString("total"))
}
}
}
private fun getUserFromJsonObject(jsonObject: JSONObject): User {
return User.builder()
.setId(jsonObject.optString("_id"))
.setName(jsonObject.optString("name"))
.setUsername(jsonObject.optString("username"))
.setStatus(jsonObject.optString("status"))
.setUtcOffset(jsonObject.optLong("utcOffset").toDouble())
.build()
}
private fun showPinnedMessageList(dataSet: ArrayList<Message>, total: String) {
mainHandler.post {
view.showWaitingView(false)
view.showPinnedMessages(dataSet, total)
}
}
private fun showFavoriteMessageList(dataSet: ArrayList<Message>, total: String) {
mainHandler.post {
view.showWaitingView(false)
view.showFavoriteMessages(dataSet, total)
}
}
private fun showMemberList(dataSet: ArrayList<User>, total: String) {
mainHandler.post {
view.showWaitingView(false)
view.showMemberList(dataSet, total)
}
}
private fun showEmptyViewMessage(message: String) {
mainHandler.post {
view.showWaitingView(false)
view.showMessage(message)
}
}
private fun showErrorMessage(message: String) {
mainHandler.post {
view.showWaitingView(false)
view.showMessage(context.getString(R.string.fragment_room_list_could_not_load_your_request, message))
}
}
private val mainHandler = Handler(context.mainLooper)
private var hasItem: Boolean = false
}
\ No newline at end of file
package chat.rocket.android.helper
import android.support.v7.widget.GridLayoutManager
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.StaggeredGridLayoutManager
/**
* Created by Filipe de Lima Brito (filipedelimabrito@gmail.com) on 9/21/17.
* Source info: https://github.com/codepath/android_guides/wiki/Endless-Scrolling-with-AdapterViews-and-RecyclerView
*/
abstract class EndlessRecyclerViewScrollListener : RecyclerView.OnScrollListener {
private var visibleThreshold = 5 // The minimum amount of items to have below of the current scroll position before loading more.
private var currentPage = 0 // The current offset index of data loaded
private var previousTotalItemCount = 0 // The total number of items in the dataset after the last load
private var loading = true // True if we are still waiting for the last set of data to load.
private val startingPageIndex = 0 // Sets the starting page index
private var layoutManager: RecyclerView.LayoutManager
constructor(layoutManager: LinearLayoutManager) {
this.layoutManager = layoutManager
}
constructor(layoutManager: GridLayoutManager) {
this.layoutManager = layoutManager
visibleThreshold *= layoutManager.spanCount
}
constructor(layoutManager: StaggeredGridLayoutManager) {
this.layoutManager = layoutManager
visibleThreshold *= layoutManager.spanCount
}
private fun getLastVisibleItem(lastVisibleItemPositions: IntArray): Int {
var maxSize = 0
for (i in lastVisibleItemPositions.indices) {
if (i == 0) {
maxSize = lastVisibleItemPositions[i]
} else if (lastVisibleItemPositions[i] > maxSize) {
maxSize = lastVisibleItemPositions[i]
}
}
return maxSize
}
// This happens many times a second during a scroll, so be wary of the code you place here.
// We are given a few useful parameters to help us work out if we need to load some more data,
// but first we check if we are waiting for the previous load to finish.
override fun onScrolled(view: RecyclerView?, dx: Int, dy: Int) {
var lastVisibleItemPosition = 0
val totalItemCount = layoutManager.itemCount
when (layoutManager) {
is StaggeredGridLayoutManager -> {
val lastVisibleItemPositions = (layoutManager as StaggeredGridLayoutManager).findLastVisibleItemPositions(null)
// get maximum element within the list
lastVisibleItemPosition = getLastVisibleItem(lastVisibleItemPositions)
}
is GridLayoutManager -> lastVisibleItemPosition = (layoutManager as GridLayoutManager).findLastVisibleItemPosition()
is LinearLayoutManager -> lastVisibleItemPosition = (layoutManager as LinearLayoutManager).findLastVisibleItemPosition()
}
// If the total item count is zero and the previous isn't, assume the
// list is invalidated and should be reset back to initial state
// If it’s still loading, we check to see if the dataset count has
// changed, if so we conclude it has finished loading and update the current page
// number and total item count.
// If it isn’t currently loading, we check to see if we have breached
// the visibleThreshold and need to reload more data.
// If we do need to reload some more data, we execute onLoadMore to fetch the data.
// threshold should reflect how many total columns there are too
// If the total item count is zero and the previous isn't, assume the
// list is invalidated and should be reset back to initial state
if (totalItemCount < previousTotalItemCount) {
this.currentPage = this.startingPageIndex
this.previousTotalItemCount = totalItemCount
if (totalItemCount == 0) {
this.loading = true
}
}
// If it’s still loading, we check to see if the dataset count has
// changed, if so we conclude it has finished loading and update the current page
// number and total item count.
if (loading && totalItemCount > previousTotalItemCount) {
loading = false
previousTotalItemCount = totalItemCount
}
// If it isn’t currently loading, we check to see if we have breached
// the visibleThreshold and need to reload more data.
// If we do need to reload some more data, we execute onLoadMore to fetch the data.
// threshold should reflect how many total columns there are too
if (!loading && lastVisibleItemPosition + visibleThreshold > totalItemCount) {
currentPage++
onLoadMore(currentPage, totalItemCount, view)
loading = true
}
}
// Call this method whenever performing new searches
fun resetState() {
this.currentPage = this.startingPageIndex
this.previousTotalItemCount = 0
this.loading = true
}
// Defines the process for actually loading more data based on page
abstract fun onLoadMore(page: Int, totalItemsCount: Int, recyclerView: RecyclerView?)
}
\ No newline at end of file
package chat.rocket.android.helper;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@SuppressWarnings("PMD.AbstractNaming")
public abstract class LoadMoreScrollListener extends RecyclerView.OnScrollListener {
private final LinearLayoutManager layoutManager;
private final int loadThreshold;
private boolean isLoading;
/**
* constructor. loadThreshold is better to set to 0.4 * total.
*/
public LoadMoreScrollListener(LinearLayoutManager layoutManager, int loadThreshold) {
this.layoutManager = layoutManager;
this.loadThreshold = loadThreshold;
setLoadingDone();
}
@Override
public void onScrolled(RecyclerView recyclerView, int deltaX, int deltaY) {
super.onScrolled(recyclerView, deltaX, deltaY);
final int visibleItemCount = recyclerView.getChildCount();
final int totalItemCount = layoutManager.getItemCount();
final int firstVisibleItem = layoutManager.findFirstVisibleItemPosition();
if (!isLoading
&& firstVisibleItem + visibleItemCount >= totalItemCount - loadThreshold
&& visibleItemCount < totalItemCount
&& deltaY < 0) {
isLoading = true;
requestMoreItem();
}
}
public void setLoadingDone() {
isLoading = false;
}
public abstract void requestMoreItem();
}
\ No newline at end of file
package chat.rocket.android.helper
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
abstract class LoadMoreScrollListener(private val layoutManager: LinearLayoutManager, private val visibleThreshold: Int) : RecyclerView.OnScrollListener() {
private var isLoading: Boolean = false
override fun onScrolled(recyclerView: RecyclerView, deltaX: Int, deltaY: Int) {
super.onScrolled(recyclerView, deltaX, deltaY)
val visibleItemCount = recyclerView.childCount
val totalItemCount = layoutManager.itemCount
val firstVisibleItem = layoutManager.findFirstVisibleItemPosition()
if (!isLoading
&& (firstVisibleItem + visibleItemCount) >= (totalItemCount - visibleThreshold)
&& visibleItemCount < totalItemCount
&& deltaY < 0) {
isLoading = true
requestMoreItem()
}
}
fun setLoadingDone() {
isLoading = false
}
abstract fun requestMoreItem()
}
\ No newline at end of file
package chat.rocket.android.helper
object UrlHelper {
/**
* Returns an URI whit no scheme (HTTP or HTTPS)
*
* @param uri The URI.
* @return The URI whit no scheme (HTTP or HTTPS)
*/
fun removeUriScheme(uri: String) = uri.replace("http://", "").replace("https://", "")
/**
* Returns the hostname with the security protocol (scheme) HTTPS.
*
* @param hostname The hostname.
* @return The hostname with the security protocol (scheme) HTTPS.
*/
fun getSafeHostname(hostname: String): String =
"https://" + hostname.replace("http://", "").replace("https://", "")
/**
* Returns an URL with no spaces and inverted slashes.
*
* @param url The URL.
* @return The URL with no spaces and inverted slashes.
*/
fun getUrl(url: String) =
url.replace(" ", "%20").replace("\\", "")
/**
* Returns an URL for a file.
*
* @param path The path to the file.
* @param userId The user ID.
* @param token The token.
* @return The URL for a file
*/
fun getUrlForFile(path: String, userId: String, token: String): String =
"https://" + removeUriScheme(getUrl(path)) + "?rc_uid=$userId" + "&rc_token=$token"
}
\ No newline at end of file
...@@ -34,8 +34,8 @@ public abstract class AbstractMessageViewHolder extends ModelViewHolder<PairedMe ...@@ -34,8 +34,8 @@ public abstract class AbstractMessageViewHolder extends ModelViewHolder<PairedMe
subUsername = itemView.findViewById(R.id.sub_username); subUsername = itemView.findViewById(R.id.sub_username);
timestamp = itemView.findViewById(R.id.timestamp); timestamp = itemView.findViewById(R.id.timestamp);
userAndTimeContainer = itemView.findViewById(R.id.user_and_timestamp_container); userAndTimeContainer = itemView.findViewById(R.id.user_and_timestamp_container);
newDayContainer = itemView.findViewById(R.id.newday_container); newDayContainer = itemView.findViewById(R.id.dayContainer);
newDayText = itemView.findViewById(R.id.newday_text); newDayText = itemView.findViewById(R.id.day);
this.absoluteUrl = absoluteUrl; this.absoluteUrl = absoluteUrl;
this.hostname = hostname; this.hostname = hostname;
} }
......
package chat.rocket.android.layouthelper.chatroom.list
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import chat.rocket.android.R
import chat.rocket.android.widget.message.RocketChatMessageLayout
import kotlinx.android.synthetic.main.item_room_file.view.*
/**
* Created by Filipe de Lima Brito (filipedelimabrito@gmail.com) on 9/22/17.
*/
class RoomFileListAdapter(private var dataSet: List<String>) : RecyclerView.Adapter<RoomFileListAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_room_file, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.fileNameLink.setText(dataSet[position])
}
override fun getItemCount(): Int = dataSet.size
fun setDataSet(dataSet: List<String>) {
this.dataSet = dataSet
notifyDataSetChanged()
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val fileNameLink : RocketChatMessageLayout = itemView.fileLink
}
}
\ No newline at end of file
package chat.rocket.android.layouthelper.chatroom.list
import android.content.Context
import android.graphics.drawable.Drawable
import android.support.graphics.drawable.VectorDrawableCompat
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import chat.rocket.android.R
import chat.rocket.android.widget.RocketChatAvatar
import chat.rocket.android.widget.helper.AvatarHelper
import chat.rocket.android.widget.helper.DrawableHelper
import chat.rocket.core.models.User
import kotlinx.android.synthetic.main.item_room_member.view.*
/**
* Created by Filipe de Lima Brito (filipedelimabrito@gmail.com) on 9/22/17.
*/
class RoomMemberListAdapter(private var dataSet: List<User>, private val hostname: String, private val context: Context) : RecyclerView.Adapter<RoomMemberListAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_room_member, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val user = dataSet[position]
holder.name.text = user.name
val userStatusDrawable: Drawable? = VectorDrawableCompat.create(context.resources, chat.rocket.android.widget.R.drawable.ic_user_status_black_24dp, null)
DrawableHelper.wrapDrawable(userStatusDrawable)
when (user.status) {
User.STATUS_ONLINE -> DrawableHelper.tintDrawable(userStatusDrawable, context, chat.rocket.android.widget.R.color.color_user_status_online)
User.STATUS_BUSY -> DrawableHelper.tintDrawable(userStatusDrawable, context, chat.rocket.android.widget.R.color.color_user_status_busy)
User.STATUS_AWAY -> DrawableHelper.tintDrawable(userStatusDrawable, context, chat.rocket.android.widget.R.color.color_user_status_away)
User.STATUS_OFFLINE -> DrawableHelper.tintDrawable(userStatusDrawable, context, chat.rocket.android.widget.R.color.color_user_status_offline)
}
holder.status.setImageDrawable(userStatusDrawable)
val username = user.username
if (username != null) {
holder.username.text = context.getString(R.string.username, username)
val placeholderDrawable = AvatarHelper.getTextDrawable(username, holder.userAvatar.context)
holder.userAvatar.loadImage(AvatarHelper.getUri(hostname, username), placeholderDrawable)
} else {
holder.userAvatar.visibility = View.GONE
holder.username.visibility = View.GONE
}
}
override fun getItemCount(): Int = dataSet.size
fun addDataSet(dataSet: List<User>) {
val previousDataSetSize = this.dataSet.size
this.dataSet += dataSet
notifyItemRangeInserted(previousDataSetSize, dataSet.size)
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val userAvatar: RocketChatAvatar = itemView.userAvatar
val name: TextView = itemView.name
val status: ImageView = itemView.status
val username: TextView = itemView.username
}
}
\ No newline at end of file
package chat.rocket.android.layouthelper.chatroom.list
import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import chat.rocket.android.R
import chat.rocket.android.helper.DateTime
import chat.rocket.android.widget.RocketChatAvatar
import chat.rocket.android.widget.helper.AvatarHelper
import chat.rocket.android.widget.message.RocketChatMessageAttachmentsLayout
import chat.rocket.android.widget.message.RocketChatMessageLayout
import chat.rocket.android.widget.message.RocketChatMessageUrlsLayout
import chat.rocket.core.models.Message
import kotlinx.android.synthetic.main.day.view.*
import kotlinx.android.synthetic.main.item_room_message.view.*
/**
* Created by Filipe de Lima Brito (filipedelimabrito@gmail.com) on 9/22/17.
*/
class RoomMessagesAdapter(private var dataSet: List<Message>, private val hostname: String, private val context: Context) : RecyclerView.Adapter<RoomMessagesAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_room_message, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val message = dataSet[position]
holder.newDay.text = DateTime.fromEpocMs(message.timestamp, DateTime.Format.DATE)
val user = message.user
if (user != null) {
if (user.name.isNullOrBlank()) {
holder.name.visibility = View.GONE
} else {
holder.name.text = message.user?.name
}
val username = user.username
if (username != null) {
val placeholderDrawable = AvatarHelper.getTextDrawable(username, holder.userAvatar.context)
holder.userAvatar.loadImage(AvatarHelper.getUri(hostname, username), placeholderDrawable)
holder.username.text = context.getString(R.string.username, username)
} else {
holder.userAvatar.visibility = View.GONE
holder.username.visibility = View.GONE
}
}
holder.messageBody.setText(message.message)
val webContents = message.webContents
if (webContents == null || webContents.isEmpty()) {
holder.messageUrl.visibility = View.GONE
} else {
holder.messageUrl.setUrls(message.webContents, true)
}
val attachments = message.attachments
if (attachments == null || attachments.isEmpty()) {
holder.messageAttachment.visibility = View.GONE
} else {
// holder.messageAttachment.setAbsoluteUrl(absoluteUrl)
// holder.messageAttachment.setAttachments(attachments, true)
// holder.messageAttachment.visibility = View.VISIBLE
}
}
override fun getItemCount(): Int = dataSet.size
fun addDataSet(dataSet: List<Message>) {
val previousDataSetSize = this.dataSet.size
this.dataSet += dataSet
notifyItemRangeInserted(previousDataSetSize, dataSet.size)
}
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
val newDay: TextView = itemView.day
val userAvatar: RocketChatAvatar = itemView.userAvatar
val username: TextView = itemView.username
val name: TextView = itemView.name
val messageBody: RocketChatMessageLayout = itemView.messageBody
val messageUrl: RocketChatMessageUrlsLayout = itemView.messageUrl
val messageAttachment: RocketChatMessageAttachmentsLayout = itemView.messageAttachment
}
}
\ No newline at end of file
package chat.rocket.android.layouthelper.chatroom.dialog; package chat.rocket.android.layouthelper.chatroom.list;
import android.content.Context; import android.content.Context;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
......
package chat.rocket.android.layouthelper.chatroom.dialog; package chat.rocket.android.layouthelper.chatroom.list;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.View; import android.view.View;
......
...@@ -39,6 +39,7 @@ import java.util.Random; ...@@ -39,6 +39,7 @@ import java.util.Random;
import chat.rocket.android.activity.MainActivity; import chat.rocket.android.activity.MainActivity;
import chat.rocket.android.helper.ServerPolicyHelper; import chat.rocket.android.helper.ServerPolicyHelper;
import chat.rocket.android.service.ConnectivityManager; import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.android.widget.helper.AvatarHelper;
import chat.rocket.core.models.ServerInfo; import chat.rocket.core.models.ServerInfo;
public class PushNotificationHandler implements PushConstants { public class PushNotificationHandler implements PushConstants {
...@@ -497,7 +498,7 @@ public class PushNotificationHandler implements PushConstants { ...@@ -497,7 +498,7 @@ public class PushNotificationHandler implements PushConstants {
setNotification(notId, ""); setNotification(notId, "");
NotificationCompat.BigPictureStyle bigPicture = new NotificationCompat.BigPictureStyle(); NotificationCompat.BigPictureStyle bigPicture = new NotificationCompat.BigPictureStyle();
bigPicture.bigPicture(getBitmapFromURL(extras.getString(PICTURE))); bigPicture.bigPicture(getBitmapFromURL(extras.getString(PICTURE), null, null));
bigPicture.setBigContentTitle(fromHtml(extras.getString(TITLE))); bigPicture.setBigContentTitle(fromHtml(extras.getString(TITLE)));
bigPicture.setSummaryText(fromHtml(extras.getString(SUMMARY_TEXT))); bigPicture.setSummaryText(fromHtml(extras.getString(SUMMARY_TEXT)));
...@@ -566,7 +567,7 @@ public class PushNotificationHandler implements PushConstants { ...@@ -566,7 +567,7 @@ public class PushNotificationHandler implements PushConstants {
setNotification(notId, ""); setNotification(notId, "");
Notification.BigPictureStyle bigPicture = new Notification.BigPictureStyle(); Notification.BigPictureStyle bigPicture = new Notification.BigPictureStyle();
bigPicture.bigPicture(getBitmapFromURL(extras.getString(PICTURE))); bigPicture.bigPicture(getBitmapFromURL(extras.getString(PICTURE), null, null));
bigPicture.setBigContentTitle(fromHtml(extras.getString(TITLE))); bigPicture.setBigContentTitle(fromHtml(extras.getString(TITLE)));
bigPicture.setSummaryText(fromHtml(extras.getString(SUMMARY_TEXT))); bigPicture.setSummaryText(fromHtml(extras.getString(SUMMARY_TEXT)));
...@@ -726,13 +727,22 @@ public class PushNotificationHandler implements PushConstants { ...@@ -726,13 +727,22 @@ public class PushNotificationHandler implements PushConstants {
private void setNotificationLargeIcon(Context context, Bundle extras, String packageName, private void setNotificationLargeIcon(Context context, Bundle extras, String packageName,
Resources resources, NotificationCompat.Builder builder) { Resources resources, NotificationCompat.Builder builder) {
String gcmLargeIcon = extras.getString(IMAGE); // from gcm String hostname = getHostname(extras);
String username = getSenderUsername(extras);
String gcmLargeIcon;
if (username != null && !username.isEmpty()) {
gcmLargeIcon = "https://" + hostname + "/avatar/" + username;
} else {
gcmLargeIcon = extras.getString(IMAGE); // from gcm
}
if (gcmLargeIcon == null || "".equals(gcmLargeIcon)) { if (gcmLargeIcon == null || "".equals(gcmLargeIcon)) {
return; return;
} }
if (gcmLargeIcon.startsWith("http://") || gcmLargeIcon.startsWith("https://")) { if (gcmLargeIcon.startsWith("http://") || gcmLargeIcon.startsWith("https://")) {
builder.setLargeIcon(getBitmapFromURL(gcmLargeIcon)); builder.setLargeIcon(getBitmapFromURL(gcmLargeIcon, username, context));
Log.d(LOG_TAG, "using remote large-icon from gcm"); Log.d(LOG_TAG, "using remote large-icon from gcm");
} else { } else {
AssetManager assetManager = context.getAssets(); AssetManager assetManager = context.getAssets();
...@@ -757,13 +767,22 @@ public class PushNotificationHandler implements PushConstants { ...@@ -757,13 +767,22 @@ public class PushNotificationHandler implements PushConstants {
private void setNotificationLargeIcon(Context context, Bundle extras, String packageName, private void setNotificationLargeIcon(Context context, Bundle extras, String packageName,
Resources resources, Notification.Builder builder) { Resources resources, Notification.Builder builder) {
String gcmLargeIcon = extras.getString(IMAGE); // from gcm String hostname = getHostname(extras);
String username = getSenderUsername(extras);
String gcmLargeIcon;
if (username != null && !username.isEmpty()) {
gcmLargeIcon = "https://" + hostname + "/avatar/" + username;
} else {
gcmLargeIcon = extras.getString(IMAGE); // from gcm
}
if (gcmLargeIcon == null || "".equals(gcmLargeIcon)) { if (gcmLargeIcon == null || "".equals(gcmLargeIcon)) {
return; return;
} }
if (gcmLargeIcon.startsWith("http://") || gcmLargeIcon.startsWith("https://")) { if (gcmLargeIcon.startsWith("http://") || gcmLargeIcon.startsWith("https://")) {
builder.setLargeIcon(getBitmapFromURL(gcmLargeIcon)); builder.setLargeIcon(getBitmapFromURL(gcmLargeIcon, username, context));
Log.d(LOG_TAG, "using remote large-icon from gcm"); Log.d(LOG_TAG, "using remote large-icon from gcm");
} else { } else {
AssetManager assetManager = context.getAssets(); AssetManager assetManager = context.getAssets();
...@@ -854,14 +873,20 @@ public class PushNotificationHandler implements PushConstants { ...@@ -854,14 +873,20 @@ public class PushNotificationHandler implements PushConstants {
intent.putExtra(NOT_ID, notId); intent.putExtra(NOT_ID, notId);
} }
public Bitmap getBitmapFromURL(String strURL) { public Bitmap getBitmapFromURL(String strURL, String username, Context context) {
try { try {
URL url = new URL(strURL); URL url = new URL(strURL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true); connection.setDoInput(true);
connection.connect(); connection.connect();
InputStream input = connection.getInputStream(); InputStream input = connection.getInputStream();
return BitmapFactory.decodeStream(input); Bitmap bitmap = BitmapFactory.decodeStream(input);
if (bitmap == null && username != null && context != null) {
return AvatarHelper.INSTANCE.getTextBitmap(username, context);
}
return bitmap;
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
return null; return null;
...@@ -937,6 +962,15 @@ public class PushNotificationHandler implements PushConstants { ...@@ -937,6 +962,15 @@ public class PushNotificationHandler implements PushConstants {
} }
} }
private String getSenderUsername(Bundle extras) {
try {
JSONObject jsonObject = new JSONObject(extras.getString("ejson", "[]"));
return jsonObject.getJSONObject("sender").optString("username");
} catch (JSONException e) {
return null;
}
}
private boolean isValidHostname(Context context, String hostname) { private boolean isValidHostname(Context context, String hostname) {
final List<ServerInfo> serverInfoList = final List<ServerInfo> serverInfoList =
ConnectivityManager.getInstance(context.getApplicationContext()).getServerList(); ConnectivityManager.getInstance(context.getApplicationContext()).getServerList();
......
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp" android:height="24dp"
android:viewportHeight="48.0" android:viewportHeight="48.0"
android:viewportWidth="48.0" android:viewportWidth="48.0">
android:width="24dp">
<path <path
android:fillColor="#A3000000" android:fillColor="#FFFFFFFF"
android:pathData="M44.99,23.47C44.99,21.42 44.38,19.45 43.16,17.62C42.07,15.97 40.54,14.52 38.62,13.29C34.91,10.92 30.03,9.62 24.88,9.62C23.16,9.62 21.47,9.77 19.82,10.05C18.8,9.1 17.61,8.24 16.35,7.57C9.6,4.3 4,7.49 4,7.49C4,7.49 9.21,11.76 8.36,15.49C6.03,17.8 4.77,20.58 4.77,23.47C4.77,23.48 4.77,23.49 4.77,23.5C4.77,23.51 4.77,23.51 4.77,23.52C4.77,26.42 6.03,29.2 8.36,31.5C9.21,35.24 4,39.5 4,39.5C4,39.5 9.6,42.69 16.35,39.43C17.61,38.75 18.8,37.89 19.82,36.94C21.47,37.23 23.16,37.37 24.88,37.37C30.03,37.37 34.91,36.07 38.62,33.7C40.54,32.48 42.07,31.02 43.16,29.38C44.38,27.55 44.99,25.58 44.99,23.53C44.99,23.52 44.99,23.51 44.99,23.5L44.99,23.47ZM24.88,12.53C34.41,12.53 42.14,17.45 42.14,23.52C42.14,29.6 34.41,34.52 24.88,34.52C22.76,34.52 20.73,34.28 18.85,33.83C16.94,36.12 12.74,39.31 8.67,38.28C9.99,36.86 11.96,34.45 11.54,30.5C9.09,28.6 7.63,26.17 7.63,23.52C7.63,17.45 15.35,12.53 24.88,12.53Z" android:pathData="M44.99,23.47C44.99,21.42 44.38,19.45 43.16,17.62C42.07,15.97 40.54,14.52 38.62,13.29C34.91,10.92 30.03,9.62 24.88,9.62C23.16,9.62 21.47,9.77 19.82,10.05C18.8,9.1 17.61,8.24 16.35,7.57C9.6,4.3 4,7.49 4,7.49C4,7.49 9.21,11.76 8.36,15.49C6.03,17.8 4.77,20.58 4.77,23.47C4.77,23.48 4.77,23.49 4.77,23.5C4.77,23.51 4.77,23.51 4.77,23.52C4.77,26.42 6.03,29.2 8.36,31.5C9.21,35.24 4,39.5 4,39.5C4,39.5 9.6,42.69 16.35,39.43C17.61,38.75 18.8,37.89 19.82,36.94C21.47,37.23 23.16,37.37 24.88,37.37C30.03,37.37 34.91,36.07 38.62,33.7C40.54,32.48 42.07,31.02 43.16,29.38C44.38,27.55 44.99,25.58 44.99,23.53C44.99,23.52 44.99,23.51 44.99,23.5L44.99,23.47ZM24.88,12.53C34.41,12.53 42.14,17.45 42.14,23.52C42.14,29.6 34.41,34.52 24.88,34.52C22.76,34.52 20.73,34.28 18.85,33.83C16.94,36.12 12.74,39.31 8.67,38.28C9.99,36.86 11.96,34.45 11.54,30.5C9.09,28.6 7.63,26.17 7.63,23.52C7.63,17.45 15.35,12.53 24.88,12.53Z" />
android:strokeColor="#00000000"
android:strokeWidth="1"/>
<path <path
android:fillColor="#A3000000" android:fillColor="#FFFFFFFF"
android:pathData="M24.88,26.17C26.15,26.17 27.17,25.14 27.17,23.88C27.17,22.61 26.15,21.59 24.88,21.59C23.62,21.59 22.59,22.61 22.59,23.88C22.59,25.14 23.62,26.17 24.88,26.17ZM32.85,26.17C34.12,26.17 35.14,25.14 35.14,23.88C35.14,22.61 34.12,21.59 32.85,21.59C31.59,21.59 30.56,22.61 30.56,23.88C30.56,25.14 31.59,26.17 32.85,26.17ZM16.91,26.17C18.18,26.17 19.2,25.14 19.2,23.88C19.2,22.62 18.18,21.59 16.91,21.59C15.65,21.59 14.62,22.62 14.62,23.88C14.62,25.14 15.65,26.17 16.91,26.17L16.91,26.17Z" android:pathData="M24.88,26.17C26.15,26.17 27.17,25.14 27.17,23.88C27.17,22.61 26.15,21.59 24.88,21.59C23.62,21.59 22.59,22.61 22.59,23.88C22.59,25.14 23.62,26.17 24.88,26.17ZM32.85,26.17C34.12,26.17 35.14,25.14 35.14,23.88C35.14,22.61 34.12,21.59 32.85,21.59C31.59,21.59 30.56,22.61 30.56,23.88C30.56,25.14 31.59,26.17 32.85,26.17ZM16.91,26.17C18.18,26.17 19.2,25.14 19.2,23.88C19.2,22.62 18.18,21.59 16.91,21.59C15.65,21.59 14.62,22.62 14.62,23.88C14.62,25.14 15.65,26.17 16.91,26.17L16.91,26.17Z" />
android:strokeColor="#00000000"
android:strokeWidth="1"/>
<path
android:fillColor="#33000000"
android:pathData="M24.88,33.08C22.76,33.08 20.73,32.86 18.85,32.48C17.17,34.23 13.69,36.59 10.1,36.5C9.62,37.22 9.11,37.8 8.67,38.28C12.74,39.31 16.94,36.12 18.85,33.83C20.73,34.28 22.76,34.52 24.88,34.52C34.34,34.52 42.01,29.68 42.13,23.67C42.01,28.88 34.34,33.08 24.88,33.08L24.88,33.08Z"
android:strokeColor="#00000000"
android:strokeWidth="1"/>
</vector> </vector>
<!-- drawable/close_circle_outline.xml -->
<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="#FF000000"
android:pathData="M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,2C6.47,2 2,6.47 2,12C2,17.53 6.47,22 12,22C17.53,22 22,17.53 22,12C22,6.47 17.53,2 12,2M14.59,8L12,10.59L9.41,8L8,9.41L10.59,12L8,14.59L9.41,16L12,13.41L14.59,16L16,14.59L13.41,12L16,9.41L14.59,8Z" />
</vector>
\ No newline at end of file
...@@ -17,7 +17,9 @@ ...@@ -17,7 +17,9 @@
<chat.rocket.android.widget.RoomToolbar <chat.rocket.android.widget.RoomToolbar
android:id="@+id/activity_main_toolbar" android:id="@+id/activity_main_toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.AppBarLayout> </android.support.design.widget.AppBarLayout>
<FrameLayout <FrameLayout
......
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:context="chat.rocket.android.fragment.chatroom.RoomFragment">
<include layout="@layout/fragment_room_main" />
<include layout="@layout/room_side_menu" />
</LinearLayout>
\ No newline at end of file
...@@ -18,7 +18,9 @@ ...@@ -18,7 +18,9 @@
<chat.rocket.android.widget.RoomToolbar <chat.rocket.android.widget.RoomToolbar
android:id="@+id/activity_main_toolbar" android:id="@+id/activity_main_toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.AppBarLayout> </android.support.design.widget.AppBarLayout>
<FrameLayout <FrameLayout
......
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="chat.rocket.android.activity.room.RoomActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.AppBarLayout>
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/dayContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<View
android:layout_width="0px"
android:layout_height="1dp"
android:layout_weight="1"
android:background="@color/colorDivider" />
<TextView
android:id="@+id/day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:textAppearance="@style/TextAppearance.RocketChat.Message.Day"
tools:text="2016/01/23" />
<View
android:layout_width="0px"
android:layout_height="1dp"
android:layout_weight="1"
android:background="@color/colorDivider" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout" android:id="@+id/messageListRelativeLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@android:color/white"
tools:context="chat.rocket.android.fragment.chatroom.RoomFragment"> tools:context="chat.rocket.android.fragment.chatroom.RoomFragment">
<include layout="@layout/fragment_room_main" /> <android.support.v7.widget.RecyclerView
android:id="@+id/messageRecyclerView"
<FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="end" android:scrollbars="vertical"
android:clickable="true" android:layout_above="@+id/messageComposer" />
android:theme="@style/AppTheme.Dark">
<include layout="@layout/room_side_menu" /> <chat.rocket.android.widget.message.MessageFormLayout
</FrameLayout> android:id="@+id/messageComposer"
</android.support.v4.widget.DrawerLayout> android:layout_width="match_parent"
\ No newline at end of file android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:theme="@style/Theme.AppCompat.Light" />
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<chat.rocket.android.widget.WaitingView
android:id="@+id/waitingView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
<TextView
android:id="@+id/messageText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/messageListRelativeLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white">
<android.support.v7.widget.RecyclerView
android:id="@+id/messageRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
android:layout_above="@+id/messageComposer" />
<chat.rocket.android.widget.message.MessageFormLayout
android:id="@+id/messageComposer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:theme="@style/Theme.AppCompat.Light" />
</RelativeLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/margin_16">
<chat.rocket.android.widget.message.RocketChatMessageLayout
android:id="@+id/fileLink"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_16"
android:layout_marginStart="@dimen/margin_16" />
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1"
android:background="@color/colorDivider"
android:layout_marginTop="@dimen/margin_8"
app:layout_constraintTop_toBottomOf="@+id/fileLink" />
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/margin_16"
android:paddingRight="@dimen/margin_16"
android:paddingStart="@dimen/margin_16"
android:paddingLeft="@dimen/margin_16"
android:paddingEnd="@dimen/margin_16">
<chat.rocket.android.widget.RocketChatAvatar
android:id="@+id/userAvatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="4dp"
app:layout_constraintTop_toTopOf="parent"/>
<android.support.constraint.ConstraintLayout
android:id="@+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_16"
android:layout_marginStart="@dimen/margin_16"
app:layout_constraintLeft_toRightOf="@+id/userAvatar" >
<ImageView
android:id="@+id/status"
android:layout_width="8dp"
android:layout_height="8dp"
app:layout_constraintTop_toTopOf="@+id/name"
app:layout_constraintBottom_toBottomOf="@+id/name"
app:srcCompat="@drawable/ic_user_status_black_24dp" />
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.RocketChat.Message.Name"
app:layout_constraintLeft_toRightOf="@+id/status"
tools:text="John Doe" />
<TextView
android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.RocketChat.Message.Username"
app:layout_constraintTop_toBottomOf="@+id/name"
app:layout_constraintLeft_toLeftOf="@+id/name"
tools:text="\@john.doe" />
</android.support.constraint.ConstraintLayout>
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1"
android:background="@color/colorDivider"
android:layout_marginTop="20dp"
app:layout_constraintTop_toBottomOf="@+id/container" />
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingRight="@dimen/margin_16"
android:paddingStart="@dimen/margin_16"
android:paddingLeft="@dimen/margin_16"
android:paddingEnd="@dimen/margin_16"
android:paddingBottom="@dimen/margin_16">
<include
android:id="@+id/dayLayout"
layout="@layout/day" />
<chat.rocket.android.widget.RocketChatAvatar
android:id="@+id/userAvatar"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginTop="20dp"
app:layout_constraintTop_toBottomOf="@+id/dayLayout" />
<android.support.constraint.ConstraintLayout
android:id="@+id/userContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_16"
android:layout_marginLeft="@dimen/margin_16"
android:layout_marginStart="@dimen/margin_16"
app:layout_constraintTop_toBottomOf="@+id/dayLayout"
app:layout_constraintLeft_toRightOf="@+id/userAvatar">
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.RocketChat.Message.Name"
tools:text="John Doe" />
<TextView
android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.RocketChat.Message.Username"
app:layout_constraintLeft_toLeftOf="@+id/name"
app:layout_constraintTop_toBottomOf="@+id/name"
tools:text="\@john.doe" />
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
android:id="@+id/messageContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginLeft="@dimen/margin_16"
android:layout_marginStart="@dimen/margin_16"
app:layout_constraintTop_toBottomOf="@+id/userContainer"
app:layout_constraintLeft_toRightOf="@+id/userAvatar"
app:layout_constraintRight_toRightOf="parent">
<chat.rocket.android.widget.message.RocketChatMessageLayout
android:id="@+id/messageBody"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<chat.rocket.android.widget.message.RocketChatMessageUrlsLayout
android:id="@+id/messageUrl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="@+id/messageBody"
app:layout_constraintTop_toBottomOf="@+id/messageBody" />
<chat.rocket.android.widget.message.RocketChatMessageAttachmentsLayout
android:id="@+id/messageAttachment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="@+id/messageUrl"
app:layout_constraintTop_toBottomOf="@+id/messageUrl" />
</android.support.constraint.ConstraintLayout>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/newday_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<View
android:layout_width="0px"
android:layout_height="1dp"
android:layout_weight="1"
android:background="@color/newday_color" />
<TextView
android:id="@+id/newday_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:textColor="@color/newday_text_color"
android:textSize="8sp"
android:textStyle="bold"
tools:text="2016/01/23" />
<View
android:layout_width="0px"
android:layout_height="1dp"
android:layout_weight="1"
android:background="@color/newday_color" />
</LinearLayout>
\ No newline at end of file
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
android:orientation="vertical" android:orientation="vertical"
android:theme="@style/AppTheme"> android:theme="@style/AppTheme">
<include layout="@layout/list_item_message_newday" /> <include layout="@layout/day" />
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
android:orientation="vertical" android:orientation="vertical"
android:theme="@style/AppTheme"> android:theme="@style/AppTheme">
<include layout="@layout/list_item_message_newday" /> <include layout="@layout/day" />
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
......
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/room_side_menu"
android:layout_width="48dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:orientation="vertical">
<io.github.yusukeiwaki.android.widget.FontAwesomeButton
android:layout_width="48dp"
android:layout_height="48dp"
android:enabled="false"
android:text="@string/fa_search"
android:textSize="24dp" />
<io.github.yusukeiwaki.android.widget.FontAwesomeButton
android:id="@+id/btn_users"
android:layout_width="48dp"
android:layout_height="48dp"
android:text="@string/fa_users"
android:textSize="24dp" />
<io.github.yusukeiwaki.android.widget.FontAwesomeButton
android:layout_width="48dp"
android:layout_height="48dp"
android:enabled="false"
android:text="@string/fa_at"
android:textSize="24dp" />
</LinearLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/action_pinned_messages"
android:title="@string/menu_pinned_messages"
app:showAsAction="never" />
<item android:id="@+id/action_favorite_messages"
android:title="@string/menu_favorite_messages"
app:showAsAction="never" />
<!--<item android:id="@+id/action_file_list"-->
<!--android:title="@string/menu_file_list"-->
<!--app:showAsAction="never" />-->
<item android:id="@+id/action_member_list"
android:title="@string/menu_member_list"
app:showAsAction="never" />
</menu>
\ No newline at end of file
...@@ -8,4 +8,9 @@ ...@@ -8,4 +8,9 @@
<color name="colorAccent_a40">#662D91FA</color> <color name="colorAccent_a40">#662D91FA</color>
<color name="textColorLink">#008ce3</color> <color name="textColorLink">#008ce3</color>
<color name="colorRed400">#FFEF5350</color> <color name="colorRed400">#FFEF5350</color>
<color name="colorPrimaryTextDark">#DE000000</color>
<color name="colorSecondaryTextDark">#8A000000</color>
<color name="colorDivider">#1F000000</color>
</resources> </resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="fa_chevron_down" translatable="false">&#xf078;</string>
<string name="fa_twitter" translatable="false">&#xf099;</string>
<string name="fa_github" translatable="false">&#xf09b;</string>
<string name="fa_google" translatable="false">&#xf1a0;</string>
<string name="fa_facebook_official" translatable="false">&#xf230;</string>
<string name="fa_plus" translatable="false">&#xf067;</string> <string name="fa_plus" translatable="false">&#xf067;</string>
<string name="fa_sign_out" translatable="false">&#xf08b;</string> <string name="fa_sign_out" translatable="false">&#xf08b;</string>
<string name="fa_search" translatable="false">&#xf002;</string>
<string name="fa_users" translatable="false">&#xf0c0;</string>
<string name="fa_at" translatable="false">&#xf1fa;</string>
</resources> </resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="TextAppearance.RocketChat.Message.Day" parent="TextAppearance.AppCompat">
<item name="android:textSize">12sp</item>
<item name="android:textColor">@color/colorPrimaryTextDark</item>
</style>
<style name="TextAppearance.RocketChat.Message.SubUsername" parent="TextAppearance.AppCompat.Body1">
<item name="android:textColor">#5f000000</item>
</style>
<style name="TextAppearance.RocketChat.Message.Name" parent="TextAppearance.AppCompat.Body1">
<item name="android:textColor">@color/colorPrimaryTextDark</item>
<item name="android:fontFamily">sans-serif-condensed</item>
<item name="android:textSize">16sp</item>
<item name="android:maxLines">1</item>
<item name="android:ellipsize">end</item>
</style>
<style name="TextAppearance.RocketChat.Message.Username" parent="TextAppearance.AppCompat.Body2">
<item name="android:textColor">@color/colorSecondaryTextDark</item>
<item name="android:fontFamily">sans-serif-condensed</item>
<item name="android:textSize">14sp</item>
<item name="android:maxLines">1</item>
<item name="android:ellipsize">end</item>
</style>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="newday_color">#e0e0e0</color>
<color name="newday_text_color">#444444</color>
<style name="TextAppearance.RocketChat.Message.Username" parent="TextAppearance.AppCompat.Body2">
<item name="android:textColor">#bb000000</item>
</style>
<style name="TextAppearance.RocketChat.Message.SubUsername" parent="TextAppearance.AppCompat.Body1">
<item name="android:textColor">#5f000000</item>
</style>
</resources>
\ No newline at end of file
...@@ -13,6 +13,16 @@ ...@@ -13,6 +13,16 @@
<string name="dialog_add_channel_private">private</string> <string name="dialog_add_channel_private">private</string>
<string name="dialog_add_channel_read_only">read only</string> <string name="dialog_add_channel_read_only">read only</string>
<string name="fragment_room_list_pinned_message_title">Pinned Messages (%s)</string>
<string name="fragment_room_list_favorite_message_title">Favorite Messages (%s)</string>
<string name="fragment_room_list_file_list_title">File list (%s)</string>
<string name="fragment_room_list_member_list_title">Member list (%s)</string>
<string name="fragment_room_list_could_not_load_your_request">Could not load your request.\nResponse was: %s</string>
<string name="fragment_room_list_no_pinned_message_to_show">No pinned message to show</string>
<string name="fragment_room_list_no_favorite_message_to_show">No favorite message to show</string>
<string name="fragment_room_list_no_file_list_to_show">No file list to show</string>
<string name="fragment_room_list_no_member_list_to_show">No member list to show</string>
<string name="start_of_conversation">Start of conversation</string> <string name="start_of_conversation">Start of conversation</string>
<string name="users_of_room_title">Members List</string> <string name="users_of_room_title">Members List</string>
<plurals name="fmt_room_user_count"> <plurals name="fmt_room_user_count">
...@@ -36,6 +46,7 @@ ...@@ -36,6 +46,7 @@
<string name="dialog_user_registration_email">Email</string> <string name="dialog_user_registration_email">Email</string>
<string name="dialog_user_registration_username">Username</string> <string name="dialog_user_registration_username">Username</string>
<string name="sub_username">\@%s</string> <string name="sub_username">\@%s</string>
<string name="username">\@%s</string>
<string name="dialog_user_registration_password">Password</string> <string name="dialog_user_registration_password">Password</string>
<string name="fragment_home_welcome_message">Welcome to Rocket.Chat.Android\nSelect a channel from the drawer.</string> <string name="fragment_home_welcome_message">Welcome to Rocket.Chat.Android\nSelect a channel from the drawer.</string>
<string name="fragment_input_hostname_hostname">Hostname</string> <string name="fragment_input_hostname_hostname">Hostname</string>
...@@ -68,5 +79,11 @@ ...@@ -68,5 +79,11 @@
<string name="edit_message">Edit message</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_message_info">Ooops. Something\'s up!</string>
<string name="message_options_no_permissions_info">You have no permissions</string> <string name="message_options_no_permissions_info">You have no permissions</string>
<string name="menu_pinned_messages">Pinned messages</string>
<string name="menu_favorite_messages">Favorite messages</string>
<string name="menu_file_list">File list</string>
<string name="menu_member_list">Member list</string>
<string name="add_new_team">Add new Team</string> <string name="add_new_team">Add new Team</string>
</resources> </resources>
...@@ -9,6 +9,13 @@ import okhttp3.OkHttpClient ...@@ -9,6 +9,13 @@ import okhttp3.OkHttpClient
object OkHttpHelper { object OkHttpHelper {
fun getClient(): OkHttpClient {
if (httpClient == null) {
httpClient = OkHttpClient()
}
return httpClient ?: throw AssertionError("httpClient set to null by another thread")
}
fun getClientForUploadFile(): OkHttpClient { fun getClientForUploadFile(): OkHttpClient {
if (httpClientForUploadFile == null) { if (httpClientForUploadFile == null) {
httpClientForUploadFile = OkHttpClient.Builder().build() httpClientForUploadFile = OkHttpClient.Builder().build()
...@@ -19,6 +26,8 @@ object OkHttpHelper { ...@@ -19,6 +26,8 @@ object OkHttpHelper {
fun getClientForDownloadFile(context: Context): OkHttpClient { fun getClientForDownloadFile(context: Context): OkHttpClient {
if(httpClientForDownloadFile == null) { if(httpClientForDownloadFile == null) {
httpClientForDownloadFile = OkHttpClient.Builder() httpClientForDownloadFile = OkHttpClient.Builder()
.followRedirects(true)
.followSslRedirects(true)
.addInterceptor(CookieInterceptor(DefaultCookieProvider(RocketChatCache(context)))) .addInterceptor(CookieInterceptor(DefaultCookieProvider(RocketChatCache(context))))
.build() .build()
} }
...@@ -37,6 +46,7 @@ object OkHttpHelper { ...@@ -37,6 +46,7 @@ object OkHttpHelper {
return httpClientForWS ?: throw AssertionError("httpClientForWS set to null by another thread") return httpClientForWS ?: throw AssertionError("httpClientForWS set to null by another thread")
} }
private var httpClient: OkHttpClient? = null
private var httpClientForUploadFile: OkHttpClient? = null private var httpClientForUploadFile: OkHttpClient? = null
private var httpClientForDownloadFile: OkHttpClient? = null private var httpClientForDownloadFile: OkHttpClient? = null
private var httpClientForWS: OkHttpClient? = null private var httpClientForWS: OkHttpClient? = null
......
package chat.rocket.android.api.rest
import chat.rocket.core.models.Room
import org.junit.Test
import kotlin.test.assertEquals
class RestApiHelperTest {
@Test
fun getEndpointUrlForMessagesTest() {
assertEquals("https://demo.rocket.chat/api/v1/channels.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_CHANNEL, "demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/groups.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_PRIVATE, "demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/dm.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_DIRECT_MESSAGE, "demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/channels.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_CHANNEL, "https://demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/groups.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_PRIVATE, "https://demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/dm.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_DIRECT_MESSAGE, "https://demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/channels.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_CHANNEL, "http://demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/groups.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_PRIVATE, "http://demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/dm.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_DIRECT_MESSAGE, "http://demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/channels.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_CHANNEL, "www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/groups.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_PRIVATE, "www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/dm.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_DIRECT_MESSAGE, "www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/channels.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_CHANNEL, "https://www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/groups.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_PRIVATE, "https://www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/dm.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_DIRECT_MESSAGE, "https://www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/channels.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_CHANNEL, "http://www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/groups.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_PRIVATE, "http://www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/dm.messages", RestApiHelper.getEndpointUrlForMessages(Room.TYPE_DIRECT_MESSAGE, "http://www.demo.rocket.chat"))
}
@Test
fun getEndpointUrlForFileListTest() {
assertEquals("https://demo.rocket.chat/api/v1/channels.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_CHANNEL, "demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/groups.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_PRIVATE, "demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/dm.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_DIRECT_MESSAGE, "demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/channels.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_CHANNEL, "https://demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/groups.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_PRIVATE, "https://demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/dm.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_DIRECT_MESSAGE, "https://demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/channels.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_CHANNEL, "http://demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/groups.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_PRIVATE, "http://demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/dm.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_DIRECT_MESSAGE, "http://demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/channels.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_CHANNEL, "www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/groups.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_PRIVATE, "www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/dm.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_DIRECT_MESSAGE, "www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/channels.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_CHANNEL, "https://www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/groups.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_PRIVATE, "https://www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/dm.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_DIRECT_MESSAGE, "https://www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/channels.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_CHANNEL, "http://www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/groups.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_PRIVATE, "http://www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/dm.files", RestApiHelper.getEndpointUrlForFileList(Room.TYPE_DIRECT_MESSAGE, "http://www.demo.rocket.chat"))
}
@Test
fun getEndpointUrlForMemberListTest() {
assertEquals("https://demo.rocket.chat/api/v1/channels.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_CHANNEL, "demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/groups.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_PRIVATE, "demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/dm.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_DIRECT_MESSAGE, "demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/channels.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_CHANNEL, "https://demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/groups.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_PRIVATE, "https://demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/dm.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_DIRECT_MESSAGE, "https://demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/channels.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_CHANNEL, "http://demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/groups.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_PRIVATE, "http://demo.rocket.chat"))
assertEquals("https://demo.rocket.chat/api/v1/dm.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_DIRECT_MESSAGE, "http://demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/channels.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_CHANNEL, "www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/groups.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_PRIVATE, "www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/dm.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_DIRECT_MESSAGE, "www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/channels.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_CHANNEL, "https://www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/groups.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_PRIVATE, "https://www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/dm.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_DIRECT_MESSAGE, "https://www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/channels.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_CHANNEL, "http://www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/groups.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_PRIVATE, "http://www.demo.rocket.chat"))
assertEquals("https://www.demo.rocket.chat/api/v1/dm.members", RestApiHelper.getEndpointUrlForMemberList(Room.TYPE_DIRECT_MESSAGE, "http://www.demo.rocket.chat"))
}
@Test
fun getRestApiUrlForMessagesTest() {
assertEquals("/api/v1/channels.messages", RestApiHelper.getRestApiUrlForMessages(Room.TYPE_CHANNEL))
assertEquals("/api/v1/groups.messages", RestApiHelper.getRestApiUrlForMessages(Room.TYPE_PRIVATE))
assertEquals("/api/v1/dm.messages", RestApiHelper.getRestApiUrlForMessages(Room.TYPE_DIRECT_MESSAGE))
}
@Test
fun getRestApiUrlForFileListTest() {
assertEquals("/api/v1/channels.files", RestApiHelper.getRestApiUrlForFileList(Room.TYPE_CHANNEL))
assertEquals("/api/v1/groups.files", RestApiHelper.getRestApiUrlForFileList(Room.TYPE_PRIVATE))
assertEquals("/api/v1/dm.files", RestApiHelper.getRestApiUrlForFileList(Room.TYPE_DIRECT_MESSAGE))
}
@Test
fun getRestApiUrlForMemberListTest() {
assertEquals("/api/v1/channels.members", RestApiHelper.getRestApiUrlForMemberList(Room.TYPE_CHANNEL))
assertEquals("/api/v1/groups.members", RestApiHelper.getRestApiUrlForMemberList(Room.TYPE_PRIVATE))
assertEquals("/api/v1/dm.members", RestApiHelper.getRestApiUrlForMemberList(Room.TYPE_DIRECT_MESSAGE))
}
}
\ No newline at end of file
package chat.rocket.android.helper
import org.junit.Test
import kotlin.test.assertEquals
class UrlHelperTest {
@Test
fun removeUriSchemeTest() {
assertEquals("demo.rocket.chat", UrlHelper.removeUriScheme("https://demo.rocket.chat"))
assertEquals("demo.rocket.chat", UrlHelper.removeUriScheme("http://demo.rocket.chat"))
assertEquals("demo.rocket.chat", UrlHelper.removeUriScheme("demo.rocket.chat"))
}
@Test
fun getSafeHostnameTest() {
assertEquals("https://demo.rocket.chat", UrlHelper.getSafeHostname("https://demo.rocket.chat"))
assertEquals("https://demo.rocket.chat", UrlHelper.getSafeHostname("http://demo.rocket.chat"))
assertEquals("https://demo.rocket.chat", UrlHelper.getSafeHostname("demo.rocket.chat"))
}
@Test
fun getUrlTest() {
assertEquals("https://demo.rocket.chat/GENERAL/file.txt", UrlHelper.getUrl("https://demo.rocket.chat/GENERAL/file.txt"))
assertEquals("http://demo.rocket.chat/GENERAL/file.txt", UrlHelper.getUrl("http://demo.rocket.chat/GENERAL/file.txt"))
assertEquals("demo.rocket.chat/GENERAL/file.txt", UrlHelper.getUrl("demo.rocket.chat/GENERAL/file.txt"))
assertEquals("demo.rocket.chat/GENERAL/a%20sample%20file.txt", UrlHelper.getUrl("demo.rocket.chat/GENERAL/a sample file.txt"))
assertEquals("demo.rocket.chat/GENERAL/file.txt", UrlHelper.getUrl("demo.rocket.chat\\/GENERAL\\/file.txt"))
}
@Test
fun getUrlForFileTest() {
assertEquals("https://demo.rocket.chat/GENERAL/file.txt?rc_uid=userId&rc_token=token", UrlHelper.getUrlForFile("https://demo.rocket.chat/GENERAL/file.txt","userId", "token"))
assertEquals("https://demo.rocket.chat/GENERAL/file.txt?rc_uid=userId&rc_token=token", UrlHelper.getUrlForFile("http://demo.rocket.chat/GENERAL/file.txt","userId", "token"))
assertEquals("https://demo.rocket.chat/GENERAL/file.txt?rc_uid=userId&rc_token=token", UrlHelper.getUrlForFile("demo.rocket.chat/GENERAL/file.txt","userId", "token"))
assertEquals("https://demo.rocket.chat/GENERAL/a%20sample%20file.txt?rc_uid=userId&rc_token=token", UrlHelper.getUrlForFile("demo.rocket.chat/GENERAL/a sample file.txt","userId", "token"))
assertEquals("https://demo.rocket.chat/GENERAL/file.txt?rc_uid=userId&rc_token=token", UrlHelper.getUrlForFile("demo.rocket.chat\\/GENERAL\\/file.txt","userId", "token"))
}
}
\ No newline at end of file
...@@ -22,7 +22,7 @@ ext { ...@@ -22,7 +22,7 @@ ext {
buildToolsVersion = "26.0.0" buildToolsVersion = "26.0.0"
supportLibraryVersion = "25.4.0" supportLibraryVersion = "25.4.0"
constraintLayoutVersion = "1.0.2" constraintLayoutVersion = "1.0.2"
kotlinVersion = "1.1.4-2" kotlinVersion = "1.1.4-3"
okHttpVersion = "3.9.0" okHttpVersion = "3.9.0"
} }
......
package chat.rocket.android.widget.helper package chat.rocket.android.widget.helper
import android.content.Context import android.content.Context
import android.graphics.Bitmap
import android.graphics.Typeface import android.graphics.Typeface
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import chat.rocket.android.widget.AbsoluteUrl import chat.rocket.android.widget.AbsoluteUrl
import com.amulyakhare.textdrawable.TextDrawable import com.amulyakhare.textdrawable.TextDrawable
import java.net.URLEncoder import java.net.URLEncoder
/**
* Created by Filipe de Lima Brito (filipedelimabrito@gmail.com) on 8/02/17.
*/
object AvatarHelper { object AvatarHelper {
/** /**
...@@ -45,6 +49,7 @@ object AvatarHelper { ...@@ -45,6 +49,7 @@ object AvatarHelper {
* @param username The username. * @param username The username.
* @param context The context. * @param context The context.
* @return A drawable with username initials. * @return A drawable with username initials.
* @see getTextBitmap
* @see getUsernameInitials * @see getUsernameInitials
*/ */
fun getTextDrawable(username: String, context: Context): Drawable { fun getTextDrawable(username: String, context: Context): Drawable {
...@@ -57,6 +62,19 @@ object AvatarHelper { ...@@ -57,6 +62,19 @@ object AvatarHelper {
.buildRoundRect(getUsernameInitials(username), getUserAvatarBackgroundColor(username), round) .buildRoundRect(getUsernameInitials(username), getUserAvatarBackgroundColor(username), round)
} }
/**
* Returns a bitmap with username initials.
*
* @param username The username.
* @param context The context.
* @return A bitmap with username initials.
* @see getTextDrawable
*/
fun getTextBitmap(username: String, context: Context): Bitmap {
val textDrawable = getTextDrawable(username, context)
return DrawableHelper.getBitmapFromDrawable(textDrawable, 96, 96)
}
/** /**
* Returns a string with the username initials. For example: username John.Doe returns JD initials. * Returns a string with the username initials. For example: username John.Doe returns JD initials.
* *
...@@ -70,15 +88,15 @@ object AvatarHelper { ...@@ -70,15 +88,15 @@ object AvatarHelper {
val splitUsername = username.split(".") val splitUsername = username.split(".")
val splitCount = splitUsername.size val splitCount = splitUsername.size
if (splitCount > 1 && splitUsername[0].isNotEmpty() && splitUsername[splitCount-1].isNotEmpty()) { return if (splitCount > 1 && splitUsername[0].isNotEmpty() && splitUsername[splitCount-1].isNotEmpty()) {
val firstInitial = splitUsername[0].substring(0, 1) val firstInitial = splitUsername[0].substring(0, 1)
val secondInitial = splitUsername[splitCount-1].substring(0, 1) val secondInitial = splitUsername[splitCount-1].substring(0, 1)
return (firstInitial + secondInitial).toUpperCase() (firstInitial + secondInitial).toUpperCase()
} else { } else {
if (username.length > 1) { if (username.length > 1) {
return username.substring(0, 2).toUpperCase() username.substring(0, 2).toUpperCase()
} else { } else {
return username.substring(0, 1).toUpperCase() username.substring(0, 1).toUpperCase()
} }
} }
} }
......
package chat.rocket.android.widget.helper package chat.rocket.android.widget.helper
import android.content.Context import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.support.v4.content.ContextCompat import android.support.v4.content.ContextCompat
import android.support.v4.graphics.drawable.DrawableCompat import android.support.v4.graphics.drawable.DrawableCompat
/**
* Created by Filipe de Lima Brito (filipedelimabrito@gmail.com) on 8/29/17.
*/
object DrawableHelper { object DrawableHelper {
/**
* Returns a bitmap from drawable.
*
* @param drawable The drawable to get the bitmap.
* @return A bitmap.
*/
fun getBitmapFromDrawable(drawable: Drawable, intrinsicWidth: Int = 1, intrinsicHeight: Int = 1): Bitmap {
if (drawable is BitmapDrawable) {
return drawable.bitmap
}
val textDrawableIntrinsicWidth = drawable.intrinsicWidth
var textDrawableIntrinsicHeight = drawable.intrinsicHeight
val width = if (textDrawableIntrinsicWidth > 0) textDrawableIntrinsicWidth else intrinsicWidth
val height = if (textDrawableIntrinsicHeight > 0) textDrawableIntrinsicHeight else intrinsicHeight
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
return bitmap
}
/** /**
* Wraps a drawable to be used for example for tinting. * Wraps a drawable to be used for example for tinting.
* *
......
...@@ -2,6 +2,9 @@ import chat.rocket.android.widget.helper.AvatarHelper ...@@ -2,6 +2,9 @@ import chat.rocket.android.widget.helper.AvatarHelper
import org.junit.Test import org.junit.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
/**
* Created by Filipe de Lima Brito (filipedelimabrito@gmail.com) on 8/2/17.
*/
class AvatarHelperTest { class AvatarHelperTest {
@Test @Test
......
...@@ -15,6 +15,9 @@ public abstract class User { ...@@ -15,6 +15,9 @@ public abstract class User {
public abstract String getId(); public abstract String getId();
@Nullable
public abstract String getName();
@Nullable @Nullable
public abstract String getUsername(); public abstract String getUsername();
...@@ -38,6 +41,8 @@ public abstract class User { ...@@ -38,6 +41,8 @@ public abstract class User {
public abstract Builder setId(String id); public abstract Builder setId(String id);
public abstract Builder setName(String name);
public abstract Builder setUsername(String username); public abstract Builder setUsername(String username);
public abstract Builder setStatus(String status); public abstract Builder setStatus(String status);
......
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