Commit 4fdf19ad authored by Rafael Kellermann Streit's avatar Rafael Kellermann Streit Committed by GitHub

Merge branch 'develop' into feature/upload-any-file

parents 3f7d0533 bc6088d9
......@@ -45,8 +45,8 @@ android {
applicationId "chat.rocket.android"
minSdkVersion 16
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 36
versionName "1.0.18"
versionCode 37
versionName "1.0.20"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
multiDexEnabled true
......@@ -102,6 +102,8 @@ android {
release {
manifest.srcFile 'src/release/AndroidManifest.xml'
}
test.java.srcDirs += 'src/test/kotlin'
}
}
......@@ -174,6 +176,11 @@ dependencies {
provided 'io.reactivex:rxjava:1.3.0'
provided "com.github.akarnokd:rxjava2-interop:0.10.2"
provided 'com.hadisatrio:Optional:v1.0.1'
testCompile 'junit:junit:4.12'
testCompile "org.mockito:mockito-core:2.7.19"
testCompile "org.jetbrains.kotlin:kotlin-test:$rootProject.ext.kotlinVersion"
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$rootProject.ext.kotlinVersion"
}
apply plugin: 'com.google.gms.google-services'
......@@ -21,6 +21,8 @@ object OkHttpHelper {
if(httpClientForDownloadFile == null) {
httpClientForDownloadFile = OkHttpClient.Builder()
.addNetworkInterceptor(StethoInterceptor())
.followRedirects(true)
.followSslRedirects(true)
.addInterceptor(CookieInterceptor(DefaultCookieProvider(RocketChatCache(context))))
.build()
}
......
......@@ -5,18 +5,30 @@ import android.content.SharedPreferences;
import com.hadisatrio.optional.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import chat.rocket.android.log.RCLog;
import chat.rocket.core.utils.Pair;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.annotations.NonNull;
import io.reactivex.annotations.Nullable;
/**
* sharedpreference-based cache.
*/
public class RocketChatCache {
private static final String KEY_SELECTED_SERVER_HOSTNAME = "selectedServerHostname";
private static final String KEY_SELECTED_ROOM_ID = "selectedRoomId";
private static final String KEY_PUSH_ID = "pushId";
private static final String KEY_SELECTED_SERVER_HOSTNAME = "KEY_SELECTED_SERVER_HOSTNAME";
private static final String KEY_SELECTED_ROOM_ID = "KEY_SELECTED_ROOM_ID";
private static final String KEY_PUSH_ID = "KEY_PUSH_ID";
private static final String KEY_HOSTNAME_LIST = "KEY_HOSTNAME_LIST";
private Context context;
......@@ -29,15 +41,57 @@ public class RocketChatCache {
}
public void setSelectedServerHostname(String hostname) {
setString(KEY_SELECTED_SERVER_HOSTNAME, hostname);
setString(KEY_SELECTED_SERVER_HOSTNAME, hostname.toLowerCase());
}
public void addHostname(@NonNull String hostname, @Nullable String hostnameAvatarUri, String siteName) {
String hostnameList = getString(KEY_HOSTNAME_LIST, null);
try {
JSONObject json;
if (hostnameList == null) {
json = new JSONObject();
} else {
json = new JSONObject(hostnameList);
}
JSONObject serverInfoJson = new JSONObject();
serverInfoJson.put("hostname", hostnameAvatarUri);
serverInfoJson.put("sitename", siteName);
// Replace server avatar uri if exists.
json.put(hostname, hostnameAvatarUri == null ? JSONObject.NULL : serverInfoJson);
setString(KEY_HOSTNAME_LIST, json.toString());
} catch (JSONException e) {
RCLog.e(e);
}
}
public List<Pair<String, Pair<String, String>>> getServerList() {
String json = getString(KEY_HOSTNAME_LIST, null);
if (json == null) {
return Collections.emptyList();
}
try {
JSONObject jsonObj = new JSONObject(json);
List<Pair<String, Pair<String, String>>> serverList = new ArrayList<>();
for (Iterator<String> iter = jsonObj.keys(); iter.hasNext();) {
String hostname = iter.next();
JSONObject serverInfoJson = jsonObj.getJSONObject(hostname);
serverList.add(new Pair<>(hostname, new Pair<>(
"http://" + hostname + "/" + serverInfoJson.getString("hostname"),
serverInfoJson.getString("sitename"))));
}
return serverList;
} catch (JSONException e) {
RCLog.e(e);
}
return Collections.emptyList();
}
public String getSelectedRoomId() {
return getString(KEY_SELECTED_ROOM_ID, null);
return getString(getSelectedServerHostname() + KEY_SELECTED_ROOM_ID, null);
}
public void setSelectedRoomId(String roomId) {
setString(KEY_SELECTED_ROOM_ID, roomId);
setString(getSelectedServerHostname() + KEY_SELECTED_ROOM_ID, roomId);
}
public String getOrCreatePushId() {
......@@ -58,7 +112,7 @@ public class RocketChatCache {
}
public Flowable<Optional<String>> getSelectedRoomIdPublisher() {
return getValuePublisher(KEY_SELECTED_ROOM_ID);
return getValuePublisher(getSelectedServerHostname() + KEY_SELECTED_ROOM_ID);
}
private SharedPreferences getSharedPreferences() {
......@@ -69,7 +123,7 @@ public class RocketChatCache {
return getSharedPreferences().edit();
}
private String getString(String key, String defaultValue) {
public String getString(String key, String defaultValue) {
return getSharedPreferences().getString(key, defaultValue);
}
......
package chat.rocket.android.activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
......@@ -13,9 +14,16 @@ import icepick.Icepick;
abstract class AbstractFragmentActivity extends RxAppCompatActivity {
public static final String EXTRA_FINISH_ON_BACK_PRESS = "EXTRA_FINISH_ON_BACK_PRESS";
private boolean finishOnBackPress;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (intent != null) {
finishOnBackPress = intent.getBooleanExtra(EXTRA_FINISH_ON_BACK_PRESS, false);
}
Icepick.restoreInstanceState(this, savedInstanceState);
}
......@@ -30,8 +38,13 @@ abstract class AbstractFragmentActivity extends RxAppCompatActivity {
@Override
public final void onBackPressed() {
if (!onBackPress()) {
onBackPressedNotHandled();
if (finishOnBackPress) {
super.onBackPressed();
finish();
} else {
if (!onBackPress()) {
onBackPressedNotHandled();
}
}
}
......
package chat.rocket.android.activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.support.v4.widget.SlidingPaneLayout;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.facebook.drawee.view.SimpleDraweeView;
import java.util.List;
import chat.rocket.android.LaunchUtil;
import chat.rocket.android.R;
......@@ -16,9 +27,13 @@ import chat.rocket.android.fragment.sidebar.SidebarMainFragment;
import chat.rocket.android.helper.KeyboardHelper;
import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.android.widget.RoomToolbar;
import chat.rocket.android.widget.helper.FrescoHelper;
import chat.rocket.core.interactors.CanCreateRoomInteractor;
import chat.rocket.core.interactors.RoomInteractor;
import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.core.repositories.PublicSettingRepository;
import chat.rocket.core.utils.Pair;
import chat.rocket.persistence.realm.repositories.RealmPublicSettingRepository;
import chat.rocket.persistence.realm.repositories.RealmRoomRepository;
import chat.rocket.persistence.realm.repositories.RealmSessionRepository;
import chat.rocket.persistence.realm.repositories.RealmUserRepository;
......@@ -51,8 +66,16 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
@Override
protected void onResume() {
super.onResume();
if (presenter != null) {
if (hostname == null || presenter == null) {
hostname = new RocketChatCache(getApplicationContext()).getSelectedServerHostname();
if (hostname == null) {
showAddServerScreen();
} else {
onHostnameUpdated();
}
} else {
presenter.bindViewOnly(this);
presenter.loadSignedInServers(hostname);
}
}
......@@ -65,30 +88,47 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
super.onPause();
}
private void showAddServerActivity() {
Intent intent = new Intent(this, AddServerActivity.class);
intent.putExtra(AddServerActivity.EXTRA_FINISH_ON_BACK_PRESS, true);
startActivity(intent);
}
private void setupToolbar() {
pane.setPanelSlideListener(new SlidingPaneLayout.PanelSlideListener() {
@Override
public void onPanelSlide(View view, float v) {
//Ref: ActionBarDrawerToggle#setProgress
toolbar.setNavigationIconProgress(v);
}
if (pane != null) {
pane.setPanelSlideListener(new SlidingPaneLayout.PanelSlideListener() {
@Override
public void onPanelSlide(View view, float v) {
//Ref: ActionBarDrawerToggle#setProgress
toolbar.setNavigationIconProgress(v);
}
@Override
public void onPanelOpened(View view) {
toolbar.setNavigationIconVerticalMirror(true);
}
@Override
public void onPanelOpened(View view) {
toolbar.setNavigationIconVerticalMirror(true);
}
@Override
public void onPanelClosed(View view) {
toolbar.setNavigationIconVerticalMirror(false);
}
});
@Override
public void onPanelClosed(View view) {
toolbar.setNavigationIconVerticalMirror(false);
Fragment fragment = getSupportFragmentManager()
.findFragmentById(R.id.sidebar_fragment_container);
if (fragment != null && fragment instanceof SidebarMainFragment) {
SidebarMainFragment sidebarMainFragment = (SidebarMainFragment) fragment;
sidebarMainFragment.toggleUserActionContainer(false);
sidebarMainFragment.showUserActionContainer(false);
}
}
});
}
toolbar.setNavigationOnClickListener(view -> {
if (pane.isSlideable() && !pane.isOpen()) {
pane.openPane();
}
});
if (toolbar != null) {
toolbar.setNavigationOnClickListener(view -> {
if (pane.isSlideable() && !pane.isOpen()) {
pane.openPane();
}
});
}
}
private boolean closeSidebarIfNeeded() {
......@@ -120,21 +160,26 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
new RealmSessionRepository(hostname)
);
PublicSettingRepository publicSettingRepository = new RealmPublicSettingRepository(hostname);
presenter = new MainPresenter(
roomInteractor,
createRoomInteractor,
sessionInteractor,
new MethodCallHelper(this, hostname),
ConnectivityManager.getInstance(getApplicationContext()),
new RocketChatCache(this)
new RocketChatCache(this),
publicSettingRepository
);
updateSidebarMainFragment();
presenter.bindView(this);
presenter.loadSignedInServers(hostname);
}
private void updateSidebarMainFragment() {
closeSidebarIfNeeded();
getSupportFragmentManager().beginTransaction()
.replace(R.id.sidebar_fragment_container, SidebarMainFragment.create(hostname))
.commit();
......@@ -165,7 +210,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
@Override
public void showUnreadCount(long roomsCount, int mentionsCount) {
toolbar.setUnreadBudge((int) roomsCount, mentionsCount);
toolbar.setUnreadBadge((int) roomsCount, mentionsCount);
}
@Override
......@@ -200,6 +245,57 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
statusTicker.updateStatus(StatusTicker.STATUS_DISMISS, null);
}
@Override
public void showSignedInServers(List<Pair<String, Pair<String, String>>> serverList) {
final SlidingPaneLayout subPane = (SlidingPaneLayout) findViewById(R.id.sub_sliding_pane);
if (subPane != null) {
LinearLayout serverListContainer = subPane.findViewById(R.id.server_list_bar);
View addServerButton = subPane.findViewById(R.id.btn_add_server);
addServerButton.setOnClickListener(view -> showAddServerActivity());
for (Pair<String, Pair<String, String>> server : serverList) {
String serverHostname = server.first;
Pair<String, String> serverInfoPair = server.second;
String logoUrl = serverInfoPair.first;
String siteName = serverInfoPair.second;
if (serverListContainer.findViewWithTag(serverHostname) == null) {
int serverCount = serverListContainer.getChildCount();
View serverRow = LayoutInflater.from(this).inflate(R.layout.server_row, serverListContainer, false);
SimpleDraweeView serverButton = serverRow.findViewById(R.id.drawee_server_button);
TextView hostnameLabel = serverRow.findViewById(R.id.text_view_server_label);
TextView siteNameLabel = serverRow.findViewById(R.id.text_view_site_name_label);
ImageView dotView = serverRow.findViewById(R.id.selected_server_dot);
serverButton.setTag(serverHostname);
hostnameLabel.setText(serverHostname);
siteNameLabel.setText(siteName);
// Currently selected server
if (serverHostname.equalsIgnoreCase(hostname)) {
serverRow.setSelected(true);
dotView.setVisibility(View.VISIBLE);
} else {
dotView.setVisibility(View.GONE);
}
serverRow.setOnClickListener(view -> changeServerIfNeeded(serverHostname));
FrescoHelper.INSTANCE.loadImage(serverButton, logoUrl, ContextCompat.getDrawable(this, R.mipmap.ic_launcher));
serverListContainer.addView(serverRow, serverCount - 1);
}
}
}
}
private void changeServerIfNeeded(String serverHostname) {
if (!hostname.equalsIgnoreCase(serverHostname)) {
RocketChatCache rocketChatCache = new RocketChatCache(getApplicationContext());
rocketChatCache.setSelectedServerHostname(serverHostname);
recreate();
}
}
//TODO: consider this class to define in layouthelper for more complicated operation.
private static class StatusTicker {
public static final int STATUS_DISMISS = 0;
......
package chat.rocket.android.activity;
import java.util.List;
import chat.rocket.android.shared.BaseContract;
import chat.rocket.core.utils.Pair;
public interface MainContract {
......@@ -21,6 +24,8 @@ public interface MainContract {
void showConnecting();
void showConnectionOk();
void showSignedInServers(List<Pair<String, Pair<String, String>>> serverList);
}
interface Presenter extends BaseContract.Presenter<View> {
......@@ -30,5 +35,7 @@ public interface MainContract {
void onRetryLogin();
void bindViewOnly(View view);
void loadSignedInServers(String hostname);
}
}
package chat.rocket.android.activity;
import android.support.annotation.NonNull;
import android.support.v4.util.Pair;
import com.hadisatrio.optional.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.List;
import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.RocketChatCache;
import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.LogIfError;
import chat.rocket.android.helper.Logger;
import chat.rocket.android.log.RCLog;
import chat.rocket.android.service.ConnectivityManagerApi;
import chat.rocket.android.service.ServerConnectivity;
import chat.rocket.android.shared.BasePresenter;
import chat.rocket.core.PublicSettingsConstants;
import chat.rocket.core.interactors.CanCreateRoomInteractor;
import chat.rocket.core.interactors.RoomInteractor;
import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.core.models.PublicSetting;
import chat.rocket.core.models.Session;
import chat.rocket.core.models.User;
import chat.rocket.core.repositories.PublicSettingRepository;
import chat.rocket.core.utils.Pair;
import hu.akarnokd.rxjava.interop.RxJavaInterop;
import io.reactivex.Flowable;
import io.reactivex.android.schedulers.AndroidSchedulers;
......@@ -30,19 +41,21 @@ public class MainPresenter extends BasePresenter<MainContract.View>
private final MethodCallHelper methodCallHelper;
private final ConnectivityManagerApi connectivityManagerApi;
private final RocketChatCache rocketChatCache;
private final PublicSettingRepository publicSettingRepository;
public MainPresenter(RoomInteractor roomInteractor,
CanCreateRoomInteractor canCreateRoomInteractor,
SessionInteractor sessionInteractor,
MethodCallHelper methodCallHelper,
ConnectivityManagerApi connectivityManagerApi,
RocketChatCache rocketChatCache) {
RocketChatCache rocketChatCache, PublicSettingRepository publicSettingRepository) {
this.roomInteractor = roomInteractor;
this.canCreateRoomInteractor = canCreateRoomInteractor;
this.sessionInteractor = sessionInteractor;
this.methodCallHelper = methodCallHelper;
this.connectivityManagerApi = connectivityManagerApi;
this.rocketChatCache = rocketChatCache;
this.publicSettingRepository = publicSettingRepository;
}
@Override
......@@ -53,6 +66,22 @@ public class MainPresenter extends BasePresenter<MainContract.View>
setUserOnline();
}
@Override
public void loadSignedInServers(@NonNull String hostname) {
final Disposable disposable = publicSettingRepository.getById(PublicSettingsConstants.Assets.LOGO)
.zipWith(publicSettingRepository.getById(PublicSettingsConstants.General.SITE_NAME), Pair::new)
.map(this::getLogoAndSiteNamePair)
.map(settings -> getServerList(hostname, settings))
.subscribeOn(AndroidSchedulers.from(BackgroundLooper.get()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
view::showSignedInServers,
RCLog::e
);
addSubscription(disposable);
}
@Override
public void bindView(@NonNull MainContract.View view) {
super.bindView(view);
......@@ -104,6 +133,27 @@ public class MainPresenter extends BasePresenter<MainContract.View>
addSubscription(subscription);
}
private Pair<String, String> getLogoAndSiteNamePair(Pair<Optional<PublicSetting>, Optional<PublicSetting>> settingsPair) {
String logoUrl = "";
String siteName = "";
if (settingsPair.first.isPresent()) {
logoUrl = settingsPair.first.get().getValue();
}
if (settingsPair.second.isPresent()) {
siteName = settingsPair.second.get().getValue();
}
return new Pair<>(logoUrl, siteName);
}
private List<Pair<String, Pair<String, String>>> getServerList(String hostname, Pair<String, String> serverInfoPair) throws JSONException {
JSONObject jsonObject = new JSONObject(serverInfoPair.first);
String logoUrl = (jsonObject.has("url")) ?
jsonObject.optString("url") : jsonObject.optString("defaultUrl");
String siteName = serverInfoPair.second;
rocketChatCache.addHostname(hostname.toLowerCase(), logoUrl, siteName);
return rocketChatCache.getServerList();
}
private void openRoom() {
String hostname = rocketChatCache.getSelectedServerHostname();
String roomId = rocketChatCache.getSelectedRoomId();
......
......@@ -52,7 +52,7 @@ public class MethodCallHelper {
*/
public MethodCallHelper(Context context, String hostname) {
this.context = context.getApplicationContext();
this.realmHelper = RealmStore.get(hostname);
this.realmHelper = RealmStore.getOrCreate(hostname);
ddpClientRef = null;
}
......
......@@ -72,7 +72,7 @@ public class InputHostnameFragment extends AbstractFragment implements InputHost
private String getHostname() {
final TextView editor = (TextView) rootView.findViewById(R.id.editor_hostname);
return TextUtils.or(TextUtils.or(editor.getText(), editor.getHint()), "").toString();
return TextUtils.or(TextUtils.or(editor.getText(), editor.getHint()), "").toString().toLowerCase();
}
private void showError(String errString) {
......
......@@ -219,11 +219,16 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
);
}
private void showUserActionContainer(boolean show) {
public void showUserActionContainer(boolean show) {
rootView.findViewById(R.id.user_action_outer_container)
.setVisibility(show ? View.VISIBLE : View.GONE);
}
public void toggleUserActionContainer(boolean checked) {
CompoundButton toggleUserAction = rootView.findViewById(R.id.toggle_user_action);
toggleUserAction.setChecked(checked);
}
@Override
public void showScreen() {
rootView.setVisibility(View.VISIBLE);
......
......@@ -6,7 +6,7 @@ import chat.rocket.android.R
import chat.rocket.android.helper.DateTime
import chat.rocket.android.widget.AbsoluteUrl
import chat.rocket.android.widget.RocketChatAvatar
import chat.rocket.android.widget.helper.UserAvatarHelper
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
......@@ -21,12 +21,12 @@ class MessageRenderer(val message: Message, val autoLoadImage: Boolean) {
fun showAvatar(rocketChatAvatarWidget: RocketChatAvatar, hostname: String) {
val username: String? = message.user?.username
if (username != null) {
val placeholderDrawable = UserAvatarHelper.getTextDrawable(username, rocketChatAvatarWidget.context)
val placeholderDrawable = AvatarHelper.getTextDrawable(username, rocketChatAvatarWidget.context)
if (message.avatar != null) {
// Load user's avatar image from Oauth provider URI.
rocketChatAvatarWidget.loadImage(message.avatar, placeholderDrawable)
} else {
rocketChatAvatarWidget.loadImage(UserAvatarHelper.getUri(hostname, username), placeholderDrawable)
rocketChatAvatarWidget.loadImage(AvatarHelper.getUri(hostname, username), placeholderDrawable)
}
} else {
rocketChatAvatarWidget.visibility = View.GONE
......
......@@ -4,7 +4,7 @@ import android.view.View
import android.widget.ImageView
import android.widget.TextView
import chat.rocket.android.widget.RocketChatAvatar
import chat.rocket.android.widget.helper.UserAvatarHelper
import chat.rocket.android.widget.helper.AvatarHelper
import chat.rocket.core.models.User
class UserRenderer(val user: User) {
......@@ -15,7 +15,7 @@ class UserRenderer(val user: User) {
fun showAvatar(rocketChatAvatarWidget: RocketChatAvatar, hostname: String) {
val username: String? = user.username
if (username != null) {
rocketChatAvatarWidget.loadImage(UserAvatarHelper.getUri(hostname, username), UserAvatarHelper.getTextDrawable(username, rocketChatAvatarWidget.context))
rocketChatAvatarWidget.loadImage(AvatarHelper.getUri(hostname, username), AvatarHelper.getTextDrawable(username, rocketChatAvatarWidget.context))
} else {
rocketChatAvatarWidget.visibility = View.GONE
}
......
......@@ -44,7 +44,7 @@
android:layout_marginTop="@dimen/margin_8"
android:hint="@string/fragment_input_hostname_server_hint"
android:imeOptions="actionSend"
android:inputType="textWebEditText"
android:inputType="textUri"
android:maxLines="1"
app:layout_constraintTop_toBottomOf="@+id/hostnameTextView"
app:layout_constraintBottom_toTopOf="@+id/btn_connect"
......
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/sub_sliding_pane"
android:layout_width="280dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:theme="@style/AppTheme.Dark">
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/sub_sliding_pane"
android:layout_width="280dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:theme="@style/AppTheme.Dark">
<android.support.v4.widget.NestedScrollView
android:layout_width="96dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="?attr/colorPrimaryDark">
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="?attr/colorPrimaryDark">
<LinearLayout
android:id="@+id/server_list_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.constraint.ConstraintLayout
android:id="@+id/btn_add_server"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_height="80dp"
android:background="?selectableItemBackground"
android:descendantFocusability="afterDescendants">
<ImageButton
<io.github.yusukeiwaki.android.widget.FontAwesomeTextView
android:id="@+id/fa_add_server"
style="@style/Base.Widget.AppCompat.Button.Borderless"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="@dimen/margin_8"
android:src="@mipmap/ic_launcher" />
android:background="@null"
android:clickable="false"
android:duplicateParentState="true"
android:text="@string/fa_plus"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<io.github.yusukeiwaki.android.widget.FontAwesomeButton
style="@style/Base.Widget.AppCompat.Button.Borderless"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="@dimen/margin_8"
android:text="@string/fa_plus" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:duplicateParentState="true"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="1"
android:text="@string/add_new_team"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="@android:color/white"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/fa_add_server"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
<FrameLayout
android:id="@+id/sidebar_fragment_container"
android:layout_width="280dp"
android:layout_height="match_parent" />
android:id="@+id/sidebar_fragment_container"
android:layout_width="280dp"
android:layout_height="match_parent" />
</android.support.v4.widget.SlidingPaneLayout>
\ No newline at end of file
......@@ -68,4 +68,5 @@
<string name="edit_message">Edit message</string>
<string name="message_options_no_message_info">Ooops. Something\'s up!</string>
<string name="message_options_no_permissions_info">You have no permissions</string>
<string name="add_new_team">Add new Team</string>
</resources>
package chat.rocket.android;
import android.content.Context
import chat.rocket.core.utils.Pair
import org.hamcrest.CoreMatchers.equalTo
import org.json.JSONObject
import org.junit.Assert.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.*
import org.mockito.junit.MockitoJUnitRunner
@RunWith(MockitoJUnitRunner::class)
class RocketChatCacheTest {
lateinit var cache: RocketChatCache
@Before
fun setup() {
val mockedContext = mock(Context::class.java)
val mockAppContext = mock(Context::class.java)
`when`(mockedContext.applicationContext).thenReturn(mockAppContext)
cache = spy(RocketChatCache(mockedContext))
}
@Test
fun getServerList_ShouldReturnHostnameList() {
val hostnameList = JSONObject()
.put("http://demo.rocket.chat", "images/logo/logo.png")
.put("http://192.168.0.6:3000", "images/icon.svg")
.toString()
doReturn(hostnameList).`when`(cache).getString("KEY_HOSTNAME_LIST", null)
val expectedServerList = mutableListOf(
Pair("http://192.168.0.6:3000", "http://192.168.0.6:3000/images/icon.svg"),
Pair("http://demo.rocket.chat", "http://demo.rocket.chat/images/logo/logo.png"))
val serverList = cache.serverList
assertThat(serverList, equalTo(expectedServerList))
}
}
\ No newline at end of file
......@@ -137,7 +137,7 @@ public class RoomToolbar extends Toolbar {
userStatusImage.setVisibility(VISIBLE);
}
public void setUnreadBudge(int numUnreadChannels, int numMentionsSum) {
public void setUnreadBadge(int numUnreadChannels, int numMentionsSum) {
if (getNavigationIcon() == null) {
return;
}
......
......@@ -7,7 +7,7 @@ import chat.rocket.android.widget.AbsoluteUrl
import com.amulyakhare.textdrawable.TextDrawable
import java.net.URLEncoder
object UserAvatarHelper {
object AvatarHelper {
/**
* Returns the user avatar URI.
......
......@@ -4,13 +4,11 @@ import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import chat.rocket.android.widget.AbsoluteUrl;
import chat.rocket.android.widget.R;
import chat.rocket.android.widget.RocketChatAvatar;
import chat.rocket.android.widget.helper.UserAvatarHelper;
import chat.rocket.android.widget.helper.AvatarHelper;
import chat.rocket.android.widget.message.autocomplete.AutocompleteViewHolder;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class UserViewHolder extends AutocompleteViewHolder<UserItem> {
private final TextView titleTextView;
......@@ -45,8 +43,8 @@ public class UserViewHolder extends AutocompleteViewHolder<UserItem> {
}
if (avatar != null) {
String absoluteUri = UserAvatarHelper.INSTANCE.getAbsoluteUri(userItem.getAbsoluteUrl(), suggestion);
Drawable placeholderDrawable = UserAvatarHelper.INSTANCE.getTextDrawable(suggestion, itemView.getContext());
String absoluteUri = AvatarHelper.INSTANCE.getAbsoluteUri(userItem.getAbsoluteUrl(), suggestion);
Drawable placeholderDrawable = AvatarHelper.INSTANCE.getTextDrawable(suggestion, itemView.getContext());
avatar.loadImage(absoluteUri, placeholderDrawable);
}
......
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true"
android:color="@android:color/white" />
<item android:color="@color/color_timestamp"/>
</selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/serverstatus_selected"/>
<stroke
android:width="1dp"
android:color="@color/serverstatus_outline"/>
<size
android:width="16dp"
android:height="16dp"/>
</shape>
<?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:fresco="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="80dp"
tools:background="#044b76">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/drawee_server_button"
style="@style/Base.Widget.AppCompat.Button.Borderless"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
fresco:actualImageScaleType="fitXY" />
<TextView
android:id="@+id/text_view_site_name_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:ellipsize="end"
android:gravity="bottom"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="@android:color/white"
app:layout_constraintEnd_toEndOf="@+id/text_view_server_label"
app:layout_constraintStart_toStartOf="@+id/text_view_server_label"
app:layout_constraintTop_toTopOf="@+id/drawee_server_button"
tools:text="Rocket.Chat" />
<TextView
android:id="@+id/text_view_server_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:ellipsize="end"
android:gravity="top"
android:maxLines="1"
android:textAllCaps="false"
android:textColor="@color/color_embed_hostname"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/drawee_server_button"
app:layout_constraintTop_toBottomOf="@+id/text_view_site_name_label"
tools:text="demo.rocket.chat" />
<ImageView
android:id="@+id/selected_server_dot"
android:layout_width="8dp"
android:layout_height="8dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="16dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/text_view_site_name_label"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/serverstatus_selected"
tools:visibility="visible" />
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
......@@ -14,4 +14,6 @@
<color name="color_user_status_busy">#FFF44336</color> <!-- Red 500-->
<color name="color_user_status_away">#FFFFC107</color> <!-- Amber 500-->
<color name="color_user_status_offline">#FF607D8B</color> <!-- Blue Grey 500-->
<color name="serverstatus_selected">#FFFFFFFF</color>
<color name="serverstatus_outline">#c2c2c2</color>
</resources>
\ No newline at end of file
import chat.rocket.android.widget.helper.AvatarHelper
import org.junit.Test
import kotlin.test.assertEquals
class AvatarHelperTest {
@Test
fun getUsernameInitialsTest() {
assertEquals("?", AvatarHelper.getUsernameInitials(""))
assertEquals("?", AvatarHelper.getUsernameInitials("?"))
assertEquals("F", AvatarHelper.getUsernameInitials("f"))
assertEquals("B", AvatarHelper.getUsernameInitials("B"))
assertEquals("FO", AvatarHelper.getUsernameInitials("Fo"))
assertEquals("FO", AvatarHelper.getUsernameInitials("FO"))
assertEquals("FO", AvatarHelper.getUsernameInitials("fOo"))
assertEquals("FO", AvatarHelper.getUsernameInitials("FOO"))
assertEquals("FO", AvatarHelper.getUsernameInitials("F.O"))
assertEquals("FO", AvatarHelper.getUsernameInitials("F.o"))
assertEquals("FB", AvatarHelper.getUsernameInitials("Foo.bar"))
assertEquals("FB", AvatarHelper.getUsernameInitials("Foobar.bar"))
assertEquals("FZ", AvatarHelper.getUsernameInitials("Foobar.bar.zab"))
assertEquals("..", AvatarHelper.getUsernameInitials(".."))
assertEquals("..", AvatarHelper.getUsernameInitials("..."))
assertEquals(".F", AvatarHelper.getUsernameInitials(".Foo."))
assertEquals("FO", AvatarHelper.getUsernameInitials("Foo.."))
}
}
\ No newline at end of file
import chat.rocket.android.widget.helper.UserAvatarHelper
import org.junit.Test
import kotlin.test.assertEquals
class UserAvatarHelperTest {
@Test
fun getUsernameInitialsTest() {
assertEquals("?", UserAvatarHelper.getUsernameInitials(""))
assertEquals("?", UserAvatarHelper.getUsernameInitials("?"))
assertEquals("F", UserAvatarHelper.getUsernameInitials("f"))
assertEquals("B", UserAvatarHelper.getUsernameInitials("B"))
assertEquals("FO", UserAvatarHelper.getUsernameInitials("Fo"))
assertEquals("FO", UserAvatarHelper.getUsernameInitials("FO"))
assertEquals("FO", UserAvatarHelper.getUsernameInitials("fOo"))
assertEquals("FO", UserAvatarHelper.getUsernameInitials("FOO"))
assertEquals("FO", UserAvatarHelper.getUsernameInitials("F.O"))
assertEquals("FO", UserAvatarHelper.getUsernameInitials("F.o"))
assertEquals("FB", UserAvatarHelper.getUsernameInitials("Foo.bar"))
assertEquals("FB", UserAvatarHelper.getUsernameInitials("Foobar.bar"))
assertEquals("FZ", UserAvatarHelper.getUsernameInitials("Foobar.bar.zab"))
assertEquals("..", UserAvatarHelper.getUsernameInitials(".."))
assertEquals("..", UserAvatarHelper.getUsernameInitials("..."))
assertEquals(".F", UserAvatarHelper.getUsernameInitials(".Foo."))
assertEquals("FO", UserAvatarHelper.getUsernameInitials("Foo.."))
}
}
\ No newline at end of file
......@@ -67,7 +67,7 @@ class EditMessageInteractorTest {
val allowEdit = allowEditPublicSettings(true)
val allowEditTimeout = allowEditTimeLimitPublicSetting()
`when`(userRepository.current).thenReturn(Flowable.just(Optional.of(user)))
`when`(userRepository.getCurrent()).thenReturn(Flowable.just(Optional.of(user)))
`when`(publicSettingRepository.getById(PublicSettingsConstants.Message.ALLOW_EDITING))
.thenReturn(Single.just(Optional.of(allowEdit)))
`when`(publicSettingRepository.getById(PublicSettingsConstants.Message.ALLOW_EDITING_BLOCK_TIMEOUT))
......@@ -93,7 +93,7 @@ class EditMessageInteractorTest {
val allowEdit = allowEditPublicSettings(false)
val allowEditTimeout = allowEditTimeLimitPublicSetting()
`when`(userRepository.current).thenReturn(Flowable.just(Optional.of(user)))
`when`(userRepository.getCurrent()).thenReturn(Flowable.just(Optional.of(user)))
`when`(publicSettingRepository.getById(PublicSettingsConstants.Message.ALLOW_EDITING))
.thenReturn(Single.just(Optional.of(allowEdit)))
`when`(publicSettingRepository.getById(PublicSettingsConstants.Message.ALLOW_EDITING_BLOCK_TIMEOUT))
......@@ -123,7 +123,7 @@ class EditMessageInteractorTest {
val allowEdit = allowEditPublicSettings(true)
val allowEditTimeout = allowEditTimeLimitPublicSetting(1)
`when`(userRepository.current).thenReturn(Flowable.just(Optional.of(user)))
`when`(userRepository.getCurrent()).thenReturn(Flowable.just(Optional.of(user)))
`when`(publicSettingRepository.getById(PublicSettingsConstants.Message.ALLOW_EDITING))
.thenReturn(Single.just(Optional.of(allowEdit)))
`when`(publicSettingRepository.getById(PublicSettingsConstants.Message.ALLOW_EDITING_BLOCK_TIMEOUT))
......@@ -155,7 +155,7 @@ class EditMessageInteractorTest {
val anotherUser = mock(User::class.java)
`when`(anotherUser.id).thenReturn("another id")
`when`(userRepository.current).thenReturn(Flowable.just(Optional.of(anotherUser)))
`when`(userRepository.getCurrent()).thenReturn(Flowable.just(Optional.of(anotherUser)))
`when`(publicSettingRepository.getById(PublicSettingsConstants.Message.ALLOW_EDITING))
.thenReturn(Single.just(Optional.of(allowEdit)))
`when`(publicSettingRepository.getById(PublicSettingsConstants.Message.ALLOW_EDITING_BLOCK_TIMEOUT))
......@@ -184,7 +184,7 @@ class EditMessageInteractorTest {
val allowEdit = allowEditPublicSettings(true)
val allowEditTimeout = allowEditTimeLimitPublicSetting()
`when`(userRepository.current).thenReturn(Flowable.just(Optional.of(user)))
`when`(userRepository.getCurrent()).thenReturn(Flowable.just(Optional.of(user)))
`when`(publicSettingRepository.getById(PublicSettingsConstants.Message.ALLOW_EDITING))
.thenReturn(Single.just(Optional.of(allowEdit)))
`when`(publicSettingRepository.getById(PublicSettingsConstants.Message.ALLOW_EDITING_BLOCK_TIMEOUT))
......@@ -213,7 +213,7 @@ class EditMessageInteractorTest {
val allowEdit = allowEditPublicSettings(true)
val allowEditTimeout = allowEditTimeLimitPublicSetting()
`when`(userRepository.current).thenReturn(Flowable.just(Optional.of(user)))
`when`(userRepository.getCurrent()).thenReturn(Flowable.just(Optional.of(user)))
`when`(publicSettingRepository.getById(PublicSettingsConstants.Message.ALLOW_EDITING))
.thenReturn(Single.just(Optional.of(allowEdit)))
`when`(publicSettingRepository.getById(PublicSettingsConstants.Message.ALLOW_EDITING_BLOCK_TIMEOUT))
......@@ -242,7 +242,7 @@ class EditMessageInteractorTest {
val allowEdit = allowEditPublicSettings(false)
val allowEditTimeout = allowEditTimeLimitPublicSetting()
`when`(userRepository.current).thenReturn(Flowable.just(Optional.of(user)))
`when`(userRepository.getCurrent()).thenReturn(Flowable.just(Optional.of(user)))
`when`(publicSettingRepository.getById(PublicSettingsConstants.Message.ALLOW_EDITING))
.thenReturn(Single.just(Optional.of(allowEdit)))
`when`(publicSettingRepository.getById(PublicSettingsConstants.Message.ALLOW_EDITING_BLOCK_TIMEOUT))
......
......@@ -52,7 +52,7 @@ class PermissionInteractorTest {
@Test
fun isAllowedReturnsFalseWhenWithoutCurrentUser() {
`when`(userRepository.current)
`when`(userRepository.getCurrent())
.thenReturn(Flowable.just(Optional.absent()))
val testObserver = TestObserver<Boolean>()
......@@ -67,7 +67,7 @@ class PermissionInteractorTest {
fun isAllowedReturnsFalseWhenWithoutRoomRoleAndPermission() {
val permissionId = "permission"
`when`(userRepository.current)
`when`(userRepository.getCurrent())
.thenReturn(Flowable.just(Optional.of(user)))
`when`(roomRoleRepository.getFor(any(Room::class.java), any(User::class.java)))
......@@ -88,7 +88,7 @@ class PermissionInteractorTest {
fun isAllowedReturnsFalseWhenWithoutRoomRole() {
val permissionId = "permission"
`when`(userRepository.current)
`when`(userRepository.getCurrent())
.thenReturn(Flowable.just(Optional.of(user)))
`when`(roomRoleRepository.getFor(any(Room::class.java), any(User::class.java)))
......@@ -109,7 +109,7 @@ class PermissionInteractorTest {
fun isAllowedReturnsFalseWhenWithoutPermission() {
val permissionId = "permission"
`when`(userRepository.current)
`when`(userRepository.getCurrent())
.thenReturn(Flowable.just(Optional.of(user)))
`when`(roomRoleRepository.getFor(any(Room::class.java), any(User::class.java)))
......@@ -131,7 +131,7 @@ class PermissionInteractorTest {
val permissionId = "permission"
`when`(userRepository.current)
`when`(userRepository.getCurrent())
.thenReturn(Flowable.just(Optional.of(user)))
`when`(roomRoleRepository.getFor(any(Room::class.java), any(User::class.java)))
......@@ -153,7 +153,7 @@ class PermissionInteractorTest {
val permissionId = "permission"
`when`(userRepository.current)
`when`(userRepository.getCurrent())
.thenReturn(Flowable.just(Optional.of(user)))
`when`(roomRole.roles).thenReturn(getSomeRoles())
......@@ -179,7 +179,7 @@ class PermissionInteractorTest {
val permissionId = "permission"
`when`(userRepository.current)
`when`(userRepository.getCurrent())
.thenReturn(Flowable.just(Optional.of(user)))
`when`(roomRole.roles).thenReturn(getMoreRoles())
......
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