Commit 189a3c69 authored by Rafael Kellermann Streit's avatar Rafael Kellermann Streit Committed by GitHub

Merge pull request #492 from RocketChat/develop

[RELEASE] Merge 1.0.20 release into master
parents 483ae847 9e1504be
...@@ -39,7 +39,7 @@ dependencies { ...@@ -39,7 +39,7 @@ dependencies {
compile "com.android.support:support-annotations:$rootProject.ext.supportLibraryVersion" compile "com.android.support:support-annotations:$rootProject.ext.supportLibraryVersion"
compile 'com.squareup.okhttp3:okhttp:3.8.0' compile "com.squareup.okhttp3:okhttp:$rootProject.ext.okHttpVersion"
compile 'io.reactivex.rxjava2:rxjava:2.1.0' compile 'io.reactivex.rxjava2:rxjava:2.1.0'
......
...@@ -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'
...@@ -45,8 +46,8 @@ android { ...@@ -45,8 +46,8 @@ android {
applicationId "chat.rocket.android" applicationId "chat.rocket.android"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 36 versionCode 40
versionName "1.0.18" versionName "1.0.20"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
multiDexEnabled true multiDexEnabled true
...@@ -102,6 +103,11 @@ android { ...@@ -102,6 +103,11 @@ android {
release { release {
manifest.srcFile 'src/release/AndroidManifest.xml' manifest.srcFile 'src/release/AndroidManifest.xml'
} }
test {
test.java.srcDirs += 'src/test/kotlin'
androidTest.java.srcDirs += 'src/androidTest/kotlin'
}
} }
} }
...@@ -135,13 +141,15 @@ dependencies { ...@@ -135,13 +141,15 @@ 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"
compile "com.google.android.gms:play-services-gcm:$playLibVersion" compile "com.google.android.gms:play-services-gcm:$playLibVersion"
compile 'com.squareup.okhttp3:okhttp:3.8.0' compile "com.squareup.okhttp3:okhttp:$rootProject.ext.okHttpVersion"
debugCompile "com.facebook.stetho:stetho:$stethoVersion" debugCompile "com.facebook.stetho:stetho:$stethoVersion"
debugCompile "com.facebook.stetho:stetho-okhttp3:$stethoOkhttp3Version" debugCompile "com.facebook.stetho:stetho-okhttp3:$stethoOkhttp3Version"
...@@ -169,11 +177,18 @@ dependencies { ...@@ -169,11 +177,18 @@ dependencies {
transitive = true; transitive = true;
} }
debugCompile "com.tspoon.traceur:traceur:1.0.1"
provided 'com.parse.bolts:bolts-tasks:1.4.0' provided 'com.parse.bolts:bolts-tasks:1.4.0'
provided 'io.reactivex.rxjava2:rxjava:2.1.0' provided 'io.reactivex.rxjava2:rxjava:2.1.0'
provided 'io.reactivex:rxjava:1.3.0' provided 'io.reactivex:rxjava:1.3.0'
provided "com.github.akarnokd:rxjava2-interop:0.10.2" provided "com.github.akarnokd:rxjava2-interop:0.10.2"
provided 'com.hadisatrio:Optional:v1.0.1' 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' apply plugin: 'com.google.gms.google-services'
...@@ -2,6 +2,7 @@ package chat.rocket.android; ...@@ -2,6 +2,7 @@ package chat.rocket.android;
import android.os.StrictMode; import android.os.StrictMode;
import com.facebook.stetho.Stetho; import com.facebook.stetho.Stetho;
import com.tspoon.traceur.Traceur;
import com.uphyca.stetho_realm.RealmInspectorModulesProvider; import com.uphyca.stetho_realm.RealmInspectorModulesProvider;
public class RocketChatApplicationDebug extends RocketChatApplication { public class RocketChatApplicationDebug extends RocketChatApplication {
...@@ -11,6 +12,11 @@ public class RocketChatApplicationDebug extends RocketChatApplication { ...@@ -11,6 +12,11 @@ public class RocketChatApplicationDebug extends RocketChatApplication {
super.onCreate(); super.onCreate();
enableStrictMode(); enableStrictMode();
enableStetho(); enableStetho();
enableTraceur();
}
private void enableTraceur() {
Traceur.enableLogging();
} }
private void enableStrictMode() { private void enableStrictMode() {
......
...@@ -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()
...@@ -21,6 +28,8 @@ object OkHttpHelper { ...@@ -21,6 +28,8 @@ object OkHttpHelper {
if(httpClientForDownloadFile == null) { if(httpClientForDownloadFile == null) {
httpClientForDownloadFile = OkHttpClient.Builder() httpClientForDownloadFile = OkHttpClient.Builder()
.addNetworkInterceptor(StethoInterceptor()) .addNetworkInterceptor(StethoInterceptor())
.followRedirects(true)
.followSslRedirects(true)
.addInterceptor(CookieInterceptor(DefaultCookieProvider(RocketChatCache(context)))) .addInterceptor(CookieInterceptor(DefaultCookieProvider(RocketChatCache(context))))
.build() .build()
} }
...@@ -39,6 +48,7 @@ object OkHttpHelper { ...@@ -39,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
......
...@@ -33,10 +33,16 @@ ...@@ -33,10 +33,16 @@
</activity> </activity>
<activity <activity
android:name=".activity.AddServerActivity" android:name=".activity.room.RoomActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
android:windowSoftInputMode="adjustResize"/> android:windowSoftInputMode="adjustResize"/>
<activity
android:name=".activity.AddServerActivity"
android:configChanges="orientation|screenSize"
android:windowSoftInputMode="adjustResize"
android:launchMode="singleTop"/>
<activity <activity
android:name=".activity.LoginActivity" android:name=".activity.LoginActivity"
android:configChanges="orientation|screenSize" android:configChanges="orientation|screenSize"
......
...@@ -17,7 +17,7 @@ public class LaunchUtil { ...@@ -17,7 +17,7 @@ public class LaunchUtil {
*/ */
public static void showMainActivity(Context context) { public static void showMainActivity(Context context) {
Intent intent = new Intent(context, MainActivity.class); Intent intent = new Intent(context, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(intent); context.startActivity(intent);
} }
...@@ -26,7 +26,7 @@ public class LaunchUtil { ...@@ -26,7 +26,7 @@ public class LaunchUtil {
*/ */
public static void showAddServerActivity(Context context) { public static void showAddServerActivity(Context context) {
Intent intent = new Intent(context, AddServerActivity.class); Intent intent = new Intent(context, AddServerActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
context.startActivity(intent); context.startActivity(intent);
} }
...@@ -35,7 +35,7 @@ public class LaunchUtil { ...@@ -35,7 +35,7 @@ public class LaunchUtil {
*/ */
public static void showLoginActivity(Context context, String hostname) { public static void showLoginActivity(Context context, String hostname) {
Intent intent = new Intent(context, LoginActivity.class); Intent intent = new Intent(context, LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP); intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(LoginActivity.KEY_HOSTNAME, hostname); intent.putExtra(LoginActivity.KEY_HOSTNAME, hostname);
context.startActivity(intent); context.startActivity(intent);
} }
......
package chat.rocket.android; package chat.rocket.android;
import android.os.Build;
import android.support.multidex.MultiDexApplication; import android.support.multidex.MultiDexApplication;
import android.support.v7.app.AppCompatDelegate;
import chat.rocket.android.helper.OkHttpHelper; import chat.rocket.android.helper.OkHttpHelper;
import com.crashlytics.android.Crashlytics; import com.crashlytics.android.Crashlytics;
...@@ -17,6 +20,12 @@ import chat.rocket.persistence.realm.RocketChatPersistenceRealm; ...@@ -17,6 +20,12 @@ import chat.rocket.persistence.realm.RocketChatPersistenceRealm;
*/ */
public class RocketChatApplication extends MultiDexApplication { public class RocketChatApplication extends MultiDexApplication {
private static RocketChatApplication instance;
public static RocketChatApplication getInstance() {
return instance;
}
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
...@@ -30,5 +39,11 @@ public class RocketChatApplication extends MultiDexApplication { ...@@ -30,5 +39,11 @@ public class RocketChatApplication extends MultiDexApplication {
} }
RocketChatWidgets.initialize(this, OkHttpHelper.INSTANCE.getClientForDownloadFile(this)); RocketChatWidgets.initialize(this, OkHttpHelper.INSTANCE.getClientForDownloadFile(this));
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true);
}
instance = this;
} }
} }
\ No newline at end of file
...@@ -5,18 +5,32 @@ import android.content.SharedPreferences; ...@@ -5,18 +5,32 @@ import android.content.SharedPreferences;
import com.hadisatrio.optional.Optional; 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 java.util.UUID;
import chat.rocket.android.helper.Logger;
import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.log.RCLog;
import chat.rocket.core.utils.Pair;
import io.reactivex.BackpressureStrategy; import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.annotations.NonNull;
import io.reactivex.annotations.Nullable;
/** /**
* sharedpreference-based cache. * sharedpreference-based cache.
*/ */
public class RocketChatCache { public class RocketChatCache {
private static final String KEY_SELECTED_SERVER_HOSTNAME = "selectedServerHostname"; private static final String KEY_SELECTED_SERVER_HOSTNAME = "KEY_SELECTED_SERVER_HOSTNAME";
private static final String KEY_SELECTED_ROOM_ID = "selectedRoomId"; private static final String KEY_SELECTED_ROOM_ID = "KEY_SELECTED_ROOM_ID";
private static final String KEY_PUSH_ID = "pushId"; private static final String KEY_PUSH_ID = "KEY_PUSH_ID";
private static final String KEY_HOSTNAME_LIST = "KEY_HOSTNAME_LIST";
private Context context; private Context context;
...@@ -29,15 +43,115 @@ public class RocketChatCache { ...@@ -29,15 +43,115 @@ public class RocketChatCache {
} }
public void setSelectedServerHostname(String hostname) { public void setSelectedServerHostname(String hostname) {
setString(KEY_SELECTED_SERVER_HOSTNAME, hostname); String newHostname = null;
if (hostname != null) {
newHostname = hostname.toLowerCase();
}
setString(KEY_SELECTED_SERVER_HOSTNAME, newHostname);
}
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("avatar", 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("avatar"),
serverInfoJson.getString("sitename"))));
}
return serverList;
} catch (JSONException e) {
RCLog.e(e);
}
return Collections.emptyList();
}
public void removeHostname(String hostname) {
String json = getString(KEY_HOSTNAME_LIST, null);
if (TextUtils.isEmpty(json)) {
return;
}
try {
JSONObject jsonObj = new JSONObject(json);
jsonObj.remove(hostname);
String result = jsonObj.length() == 0 ? null : jsonObj.toString();
setString(KEY_HOSTNAME_LIST, result);
} catch (JSONException e) {
RCLog.e(e);
}
}
@Nullable
public String getFirstLoggedHostnameIfAny() {
String json = getString(KEY_HOSTNAME_LIST, null);
if (json != null) {
try {
JSONObject jsonObj = new JSONObject(json);
if (jsonObj.length() > 0 && jsonObj.keys().hasNext()) {
// Returns the first hostname on the list.
return jsonObj.keys().next();
}
} catch (JSONException e) {
RCLog.e(e);
}
}
return null;
} }
public String getSelectedRoomId() { public String getSelectedRoomId() {
return getString(KEY_SELECTED_ROOM_ID, null); try {
JSONObject jsonObject = getSelectedRoomIdJsonObject();
return jsonObject.optString(getSelectedServerHostname(), null);
} catch (JSONException e) {
RCLog.e(e);
Logger.report(e);
}
return null;
} }
public void setSelectedRoomId(String roomId) { public void setSelectedRoomId(String roomId) {
setString(KEY_SELECTED_ROOM_ID, roomId); try {
JSONObject jsonObject = getSelectedRoomIdJsonObject();
jsonObject.put(getSelectedServerHostname(), roomId);
setString(KEY_SELECTED_ROOM_ID, jsonObject.toString());
} catch (JSONException e) {
RCLog.e(e);
Logger.report(e);
}
}
private JSONObject getSelectedRoomIdJsonObject() throws JSONException {
String json = getString(KEY_SELECTED_ROOM_ID, null);
if (json == null) {
return new JSONObject();
}
return new JSONObject(json);
} }
public String getOrCreatePushId() { public String getOrCreatePushId() {
...@@ -69,7 +183,7 @@ public class RocketChatCache { ...@@ -69,7 +183,7 @@ public class RocketChatCache {
return getSharedPreferences().edit(); return getSharedPreferences().edit();
} }
private String getString(String key, String defaultValue) { public String getString(String key, String defaultValue) {
return getSharedPreferences().getString(key, defaultValue); return getSharedPreferences().getString(key, defaultValue);
} }
...@@ -93,4 +207,17 @@ public class RocketChatCache { ...@@ -93,4 +207,17 @@ public class RocketChatCache {
getSharedPreferences().registerOnSharedPreferenceChangeListener(listener); getSharedPreferences().registerOnSharedPreferenceChangeListener(listener);
}, BackpressureStrategy.LATEST); }, BackpressureStrategy.LATEST);
} }
public void removeSelectedRoomId(String currentHostname) {
try {
JSONObject selectedRoomIdJsonObject = getSelectedRoomIdJsonObject();
selectedRoomIdJsonObject.remove(currentHostname);
String result = selectedRoomIdJsonObject.length() == 0 ?
null : selectedRoomIdJsonObject.toString();
setString(KEY_SELECTED_ROOM_ID, result);
} catch (JSONException e) {
Logger.report(e);
RCLog.e(e);
}
}
} }
...@@ -6,6 +6,9 @@ import android.support.annotation.Nullable; ...@@ -6,6 +6,9 @@ import android.support.annotation.Nullable;
import com.hadisatrio.optional.Optional; import com.hadisatrio.optional.Optional;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.List; import java.util.List;
import chat.rocket.android.LaunchUtil; import chat.rocket.android.LaunchUtil;
...@@ -61,6 +64,8 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity { ...@@ -61,6 +64,8 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
if (intent.hasExtra(PushConstants.ROOM_ID)) { if (intent.hasExtra(PushConstants.ROOM_ID)) {
rocketChatCache.setSelectedRoomId(intent.getStringExtra(PushConstants.ROOM_ID)); rocketChatCache.setSelectedRoomId(intent.getStringExtra(PushConstants.ROOM_ID));
} }
} else {
updateHostnameIfNeeded(rocketChatCache.getSelectedServerHostname());
} }
if (intent.hasExtra(PushConstants.NOT_ID)) { if (intent.hasExtra(PushConstants.NOT_ID)) {
...@@ -194,7 +199,8 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity { ...@@ -194,7 +199,8 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
compositeDisposable.add( compositeDisposable.add(
rocketChatCache.getSelectedRoomIdPublisher() rocketChatCache.getSelectedRoomIdPublisher()
.map(Optional::get) .map(Optional::get)
.distinctUntilChanged() .map(this::convertStringToJsonObject)
.map(jsonObject -> jsonObject.optString(rocketChatCache.getSelectedServerHostname(), null))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.subscribe( .subscribe(
...@@ -203,4 +209,11 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity { ...@@ -203,4 +209,11 @@ abstract class AbstractAuthedActivity extends AbstractFragmentActivity {
) )
); );
} }
private JSONObject convertStringToJsonObject(String json) throws JSONException {
if (json == null) {
return new JSONObject();
}
return new JSONObject(json);
}
} }
package chat.rocket.android.activity; package chat.rocket.android.activity;
import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.IdRes; import android.support.annotation.IdRes;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
...@@ -13,9 +14,16 @@ import icepick.Icepick; ...@@ -13,9 +14,16 @@ import icepick.Icepick;
abstract class AbstractFragmentActivity extends RxAppCompatActivity { abstract class AbstractFragmentActivity extends RxAppCompatActivity {
public static final String EXTRA_FINISH_ON_BACK_PRESS = "EXTRA_FINISH_ON_BACK_PRESS";
private boolean finishOnBackPress;
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (intent != null) {
finishOnBackPress = intent.getBooleanExtra(EXTRA_FINISH_ON_BACK_PRESS, false);
}
Icepick.restoreInstanceState(this, savedInstanceState); Icepick.restoreInstanceState(this, savedInstanceState);
} }
...@@ -30,10 +38,15 @@ abstract class AbstractFragmentActivity extends RxAppCompatActivity { ...@@ -30,10 +38,15 @@ abstract class AbstractFragmentActivity extends RxAppCompatActivity {
@Override @Override
public final void onBackPressed() { public final void onBackPressed() {
if (finishOnBackPress) {
super.onBackPressed();
finish();
} else {
if (!onBackPress()) { if (!onBackPress()) {
onBackPressedNotHandled(); onBackPressedNotHandled();
} }
} }
}
protected boolean onBackPress() { protected boolean onBackPress() {
FragmentManager fragmentManager = getSupportFragmentManager(); FragmentManager fragmentManager = getSupportFragmentManager();
...@@ -62,10 +75,28 @@ abstract class AbstractFragmentActivity extends RxAppCompatActivity { ...@@ -62,10 +75,28 @@ abstract class AbstractFragmentActivity extends RxAppCompatActivity {
.commit(); .commit();
} }
protected void showFragmentWithTagWithBackStack(Fragment fragment, String tag) {
getSupportFragmentManager().beginTransaction()
.replace(getLayoutContainerForFragment(), fragment, tag)
.addToBackStack(null)
.commit();
}
protected void showFragmentWithTag(Fragment fragment, String tag) {
getSupportFragmentManager().beginTransaction()
.replace(getLayoutContainerForFragment(), fragment, tag)
.commit();
}
protected void showFragmentWithBackStack(Fragment fragment) { protected void showFragmentWithBackStack(Fragment fragment) {
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.replace(getLayoutContainerForFragment(), fragment) .replace(getLayoutContainerForFragment(), fragment)
.addToBackStack(null) .addToBackStack(null)
.commit(); .commit();
} }
@Nullable
protected Fragment findFragmentByTag(String tag) {
return getSupportFragmentManager().findFragmentByTag(tag);
}
} }
package chat.rocket.android.activity; package chat.rocket.android.activity;
import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar; 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.support.v4.widget.SlidingPaneLayout;
import android.view.LayoutInflater;
import android.view.View; 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.LaunchUtil;
import chat.rocket.android.R; import chat.rocket.android.R;
...@@ -16,9 +27,13 @@ import chat.rocket.android.fragment.sidebar.SidebarMainFragment; ...@@ -16,9 +27,13 @@ import chat.rocket.android.fragment.sidebar.SidebarMainFragment;
import chat.rocket.android.helper.KeyboardHelper; import chat.rocket.android.helper.KeyboardHelper;
import chat.rocket.android.service.ConnectivityManager; import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.android.widget.RoomToolbar; import chat.rocket.android.widget.RoomToolbar;
import chat.rocket.android.widget.helper.FrescoHelper;
import chat.rocket.core.interactors.CanCreateRoomInteractor; import chat.rocket.core.interactors.CanCreateRoomInteractor;
import chat.rocket.core.interactors.RoomInteractor; import chat.rocket.core.interactors.RoomInteractor;
import chat.rocket.core.interactors.SessionInteractor; 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.RealmRoomRepository;
import chat.rocket.persistence.realm.repositories.RealmSessionRepository; import chat.rocket.persistence.realm.repositories.RealmSessionRepository;
import chat.rocket.persistence.realm.repositories.RealmUserRepository; import chat.rocket.persistence.realm.repositories.RealmUserRepository;
...@@ -33,7 +48,6 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -33,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;
} }
...@@ -51,8 +65,16 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -51,8 +65,16 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
@Override @Override
protected void onResume() { protected void onResume() {
super.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.bindViewOnly(this);
presenter.loadSignedInServers(hostname);
} }
} }
...@@ -65,7 +87,16 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -65,7 +87,16 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
super.onPause(); super.onPause();
} }
private void showAddServerActivity() {
closeSidebarIfNeeded();
Intent intent = new Intent(this, AddServerActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(AddServerActivity.EXTRA_FINISH_ON_BACK_PRESS, true);
startActivity(intent);
}
private void setupToolbar() { private void setupToolbar() {
if (pane != null) {
pane.setPanelSlideListener(new SlidingPaneLayout.PanelSlideListener() { pane.setPanelSlideListener(new SlidingPaneLayout.PanelSlideListener() {
@Override @Override
public void onPanelSlide(View view, float v) { public void onPanelSlide(View view, float v) {
...@@ -81,15 +112,25 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -81,15 +112,25 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
@Override @Override
public void onPanelClosed(View view) { public void onPanelClosed(View view) {
toolbar.setNavigationIconVerticalMirror(false); 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);
}
} }
}); });
}
if (toolbar != null) {
toolbar.setNavigationOnClickListener(view -> { toolbar.setNavigationOnClickListener(view -> {
if (pane.isSlideable() && !pane.isOpen()) { if (pane.isSlideable() && !pane.isOpen()) {
pane.openPane(); pane.openPane();
} }
}); });
} }
}
private boolean closeSidebarIfNeeded() { private boolean closeSidebarIfNeeded() {
// REMARK: Tablet UI doesn't have SlidingPane! // REMARK: Tablet UI doesn't have SlidingPane!
...@@ -120,24 +161,39 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -120,24 +161,39 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
new RealmSessionRepository(hostname) new RealmSessionRepository(hostname)
); );
PublicSettingRepository publicSettingRepository = new RealmPublicSettingRepository(hostname);
RocketChatCache rocketChatCache = new RocketChatCache(this);
presenter = new MainPresenter( presenter = new MainPresenter(
roomInteractor, roomInteractor,
createRoomInteractor, createRoomInteractor,
sessionInteractor, sessionInteractor,
new MethodCallHelper(this, hostname), new MethodCallHelper(this, hostname),
ConnectivityManager.getInstance(getApplicationContext()), ConnectivityManager.getInstance(getApplicationContext()),
new RocketChatCache(this) rocketChatCache,
publicSettingRepository
); );
updateSidebarMainFragment(); updateSidebarMainFragment();
presenter.bindView(this); presenter.bindView(this);
presenter.loadSignedInServers(hostname);
roomId = rocketChatCache.getSelectedRoomId();
} }
private void updateSidebarMainFragment() { private void updateSidebarMainFragment() {
closeSidebarIfNeeded();
String selectedServerHostname = new RocketChatCache(this).getSelectedServerHostname();
Fragment sidebarFragment = findFragmentByTag(selectedServerHostname);
if (sidebarFragment == null) {
sidebarFragment = SidebarMainFragment.create(selectedServerHostname);
}
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.replace(R.id.sidebar_fragment_container, SidebarMainFragment.create(hostname)) .replace(R.id.sidebar_fragment_container, sidebarFragment, selectedServerHostname)
.commit(); .commit();
getSupportFragmentManager().executePendingTransactions();
} }
@Override @Override
...@@ -165,7 +221,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -165,7 +221,7 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
@Override @Override
public void showUnreadCount(long roomsCount, int mentionsCount) { public void showUnreadCount(long roomsCount, int mentionsCount) {
toolbar.setUnreadBudge((int) roomsCount, mentionsCount); toolbar.setUnreadBadge((int) roomsCount, mentionsCount);
} }
@Override @Override
...@@ -195,16 +251,94 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract ...@@ -195,16 +251,94 @@ public class MainActivity extends AbstractAuthedActivity implements MainContract
R.string.server_config_activity_authenticating, Snackbar.LENGTH_INDEFINITE)); R.string.server_config_activity_authenticating, Snackbar.LENGTH_INDEFINITE));
} }
public void showLogoutMessage() {
statusTicker.updateStatus(StatusTicker.STATUS_LOGGING_OUT,
Snackbar.make(findViewById(getLayoutContainerForFragment()),
"Logging Out...", Snackbar.LENGTH_INDEFINITE));
}
@Override @Override
public void showConnectionOk() { public void showConnectionOk() {
statusTicker.updateStatus(StatusTicker.STATUS_DISMISS, null); 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());
serverListContainer.removeAllViews();
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;
View serverView = serverListContainer.findViewWithTag(serverHostname);
if (serverView == null) {
View newServerView = LayoutInflater.from(this).inflate(R.layout.server_row, serverListContainer, false);
SimpleDraweeView serverButton = newServerView.findViewById(R.id.drawee_server_button);
TextView hostnameLabel = newServerView.findViewById(R.id.text_view_server_label);
TextView siteNameLabel = newServerView.findViewById(R.id.text_view_site_name_label);
ImageView dotView = newServerView.findViewById(R.id.selected_server_dot);
newServerView.setTag(serverHostname);
hostnameLabel.setText(serverHostname);
siteNameLabel.setText(siteName);
// Currently selected server
if (hostname.equalsIgnoreCase(serverHostname)) {
newServerView.setSelected(true);
dotView.setVisibility(View.VISIBLE);
} else {
newServerView.setSelected(false);
dotView.setVisibility(View.GONE);
}
newServerView.setOnClickListener(view -> changeServerIfNeeded(serverHostname));
FrescoHelper.INSTANCE.loadImage(serverButton, logoUrl, ContextCompat.getDrawable(this, R.mipmap.ic_launcher));
serverListContainer.addView(newServerView);
}
}
serverListContainer.addView(addServerButton);
}
}
private void changeServerIfNeeded(String serverHostname) {
if (!hostname.equalsIgnoreCase(serverHostname)) {
RocketChatCache rocketChatCache = new RocketChatCache(getApplicationContext());
rocketChatCache.setSelectedServerHostname(serverHostname);
}
}
@DebugLog
public void hideLogoutMessage() {
statusTicker.updateStatus(StatusTicker.STATUS_DISMISS, null);
}
@DebugLog
public void onLogout() {
if (new RocketChatCache(getApplicationContext()).getSelectedServerHostname() == null) {
LaunchUtil.showMainActivity(this);
} else {
onHostnameUpdated();
}
}
@DebugLog
public void beforeLogoutCleanUp() {
presenter.beforeLogoutCleanUp();
}
//TODO: consider this class to define in layouthelper for more complicated operation. //TODO: consider this class to define in layouthelper for more complicated operation.
private static class StatusTicker { private static class StatusTicker {
public static final int STATUS_DISMISS = 0; public static final int STATUS_DISMISS = 0;
public static final int STATUS_CONNECTION_ERROR = 1; public static final int STATUS_CONNECTION_ERROR = 1;
public static final int STATUS_TOKEN_LOGIN = 2; public static final int STATUS_TOKEN_LOGIN = 2;
public static final int STATUS_LOGGING_OUT = 3;
private int status; private int status;
private Snackbar snackbar; private Snackbar snackbar;
......
package chat.rocket.android.activity; package chat.rocket.android.activity;
import java.util.List;
import chat.rocket.android.shared.BaseContract; import chat.rocket.android.shared.BaseContract;
import chat.rocket.core.utils.Pair;
public interface MainContract { public interface MainContract {
...@@ -21,6 +24,8 @@ public interface MainContract { ...@@ -21,6 +24,8 @@ public interface MainContract {
void showConnecting(); void showConnecting();
void showConnectionOk(); void showConnectionOk();
void showSignedInServers(List<Pair<String, Pair<String, String>>> serverList);
} }
interface Presenter extends BaseContract.Presenter<View> { interface Presenter extends BaseContract.Presenter<View> {
...@@ -30,5 +35,9 @@ public interface MainContract { ...@@ -30,5 +35,9 @@ public interface MainContract {
void onRetryLogin(); void onRetryLogin();
void bindViewOnly(View view); void bindViewOnly(View view);
void loadSignedInServers(String hostname);
void beforeLogoutCleanUp();
} }
} }
package chat.rocket.android.activity; package chat.rocket.android.activity;
import android.support.annotation.NonNull; 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.BackgroundLooper;
import chat.rocket.android.RocketChatCache; import chat.rocket.android.RocketChatCache;
import chat.rocket.android.api.MethodCallHelper; import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.LogIfError; import chat.rocket.android.helper.LogIfError;
import chat.rocket.android.helper.Logger; import chat.rocket.android.helper.Logger;
import chat.rocket.android.log.RCLog;
import chat.rocket.android.service.ConnectivityManagerApi; import chat.rocket.android.service.ConnectivityManagerApi;
import chat.rocket.android.service.ServerConnectivity; import chat.rocket.android.service.ServerConnectivity;
import chat.rocket.android.shared.BasePresenter; import chat.rocket.android.shared.BasePresenter;
import chat.rocket.core.PublicSettingsConstants;
import chat.rocket.core.interactors.CanCreateRoomInteractor; import chat.rocket.core.interactors.CanCreateRoomInteractor;
import chat.rocket.core.interactors.RoomInteractor; import chat.rocket.core.interactors.RoomInteractor;
import chat.rocket.core.interactors.SessionInteractor; import chat.rocket.core.interactors.SessionInteractor;
import chat.rocket.core.models.PublicSetting;
import chat.rocket.core.models.Session; import chat.rocket.core.models.Session;
import chat.rocket.core.models.User; 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 hu.akarnokd.rxjava.interop.RxJavaInterop;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
...@@ -30,19 +41,21 @@ public class MainPresenter extends BasePresenter<MainContract.View> ...@@ -30,19 +41,21 @@ public class MainPresenter extends BasePresenter<MainContract.View>
private final MethodCallHelper methodCallHelper; private final MethodCallHelper methodCallHelper;
private final ConnectivityManagerApi connectivityManagerApi; private final ConnectivityManagerApi connectivityManagerApi;
private final RocketChatCache rocketChatCache; private final RocketChatCache rocketChatCache;
private final PublicSettingRepository publicSettingRepository;
public MainPresenter(RoomInteractor roomInteractor, public MainPresenter(RoomInteractor roomInteractor,
CanCreateRoomInteractor canCreateRoomInteractor, CanCreateRoomInteractor canCreateRoomInteractor,
SessionInteractor sessionInteractor, SessionInteractor sessionInteractor,
MethodCallHelper methodCallHelper, MethodCallHelper methodCallHelper,
ConnectivityManagerApi connectivityManagerApi, ConnectivityManagerApi connectivityManagerApi,
RocketChatCache rocketChatCache) { RocketChatCache rocketChatCache, PublicSettingRepository publicSettingRepository) {
this.roomInteractor = roomInteractor; this.roomInteractor = roomInteractor;
this.canCreateRoomInteractor = canCreateRoomInteractor; this.canCreateRoomInteractor = canCreateRoomInteractor;
this.sessionInteractor = sessionInteractor; this.sessionInteractor = sessionInteractor;
this.methodCallHelper = methodCallHelper; this.methodCallHelper = methodCallHelper;
this.connectivityManagerApi = connectivityManagerApi; this.connectivityManagerApi = connectivityManagerApi;
this.rocketChatCache = rocketChatCache; this.rocketChatCache = rocketChatCache;
this.publicSettingRepository = publicSettingRepository;
} }
@Override @Override
...@@ -53,6 +66,22 @@ public class MainPresenter extends BasePresenter<MainContract.View> ...@@ -53,6 +66,22 @@ public class MainPresenter extends BasePresenter<MainContract.View>
setUserOnline(); 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 @Override
public void bindView(@NonNull MainContract.View view) { public void bindView(@NonNull MainContract.View view) {
super.bindView(view); super.bindView(view);
...@@ -104,6 +133,32 @@ public class MainPresenter extends BasePresenter<MainContract.View> ...@@ -104,6 +133,32 @@ public class MainPresenter extends BasePresenter<MainContract.View>
addSubscription(subscription); addSubscription(subscription);
} }
@Override
public void beforeLogoutCleanUp() {
clearSubscriptions();
}
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() { private void openRoom() {
String hostname = rocketChatCache.getSelectedServerHostname(); String hostname = rocketChatCache.getSelectedServerHostname();
String roomId = rocketChatCache.getSelectedRoomId(); String roomId = rocketChatCache.getSelectedRoomId();
......
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
...@@ -2,7 +2,7 @@ package chat.rocket.android.api; ...@@ -2,7 +2,7 @@ package chat.rocket.android.api;
import android.content.Context; import android.content.Context;
import android.util.Patterns; import android.util.Patterns;
import chat.rocket.persistence.realm.models.ddp.RealmSpotlight;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
...@@ -17,7 +17,6 @@ import chat.rocket.android.service.ConnectivityManager; ...@@ -17,7 +17,6 @@ import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.android.service.DDPClientRef; import chat.rocket.android.service.DDPClientRef;
import chat.rocket.android_ddp.DDPClientCallback; import chat.rocket.android_ddp.DDPClientCallback;
import chat.rocket.core.SyncState; import chat.rocket.core.SyncState;
import chat.rocket.core.models.ServerInfo;
import chat.rocket.persistence.realm.RealmHelper; import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.RealmStore; import chat.rocket.persistence.realm.RealmStore;
import chat.rocket.persistence.realm.models.ddp.RealmMessage; import chat.rocket.persistence.realm.models.ddp.RealmMessage;
...@@ -25,6 +24,7 @@ import chat.rocket.persistence.realm.models.ddp.RealmPermission; ...@@ -25,6 +24,7 @@ import chat.rocket.persistence.realm.models.ddp.RealmPermission;
import chat.rocket.persistence.realm.models.ddp.RealmPublicSetting; import chat.rocket.persistence.realm.models.ddp.RealmPublicSetting;
import chat.rocket.persistence.realm.models.ddp.RealmRoom; import chat.rocket.persistence.realm.models.ddp.RealmRoom;
import chat.rocket.persistence.realm.models.ddp.RealmRoomRole; import chat.rocket.persistence.realm.models.ddp.RealmRoomRole;
import chat.rocket.persistence.realm.models.ddp.RealmSpotlight;
import chat.rocket.persistence.realm.models.ddp.RealmSpotlightRoom; import chat.rocket.persistence.realm.models.ddp.RealmSpotlightRoom;
import chat.rocket.persistence.realm.models.ddp.RealmSpotlightUser; import chat.rocket.persistence.realm.models.ddp.RealmSpotlightUser;
import chat.rocket.persistence.realm.models.internal.MethodCall; import chat.rocket.persistence.realm.models.internal.MethodCall;
...@@ -52,7 +52,7 @@ public class MethodCallHelper { ...@@ -52,7 +52,7 @@ public class MethodCallHelper {
*/ */
public MethodCallHelper(Context context, String hostname) { public MethodCallHelper(Context context, String hostname) {
this.context = context.getApplicationContext(); this.context = context.getApplicationContext();
this.realmHelper = RealmStore.get(hostname); this.realmHelper = RealmStore.getOrCreate(hostname);
ddpClientRef = null; ddpClientRef = null;
} }
...@@ -260,18 +260,12 @@ public class MethodCallHelper { ...@@ -260,18 +260,12 @@ public class MethodCallHelper {
* Logout. * Logout.
*/ */
public Task<Void> logout() { public Task<Void> logout() {
return call("logout", TIMEOUT_MS).onSuccessTask(task -> return call("logout", TIMEOUT_MS).onSuccessTask(task -> {
realmHelper.executeTransaction(realm -> { if (task.isFaulted()) {
realm.delete(RealmSession.class); return Task.forError(task.getError());
//check whether the server list is empty
if (!ConnectivityManager.getInstance(context).getServerList().isEmpty()){
//for each server in serverList -> remove the server
for (ServerInfo server: ConnectivityManager.getInstance(context).getServerList()) {
ConnectivityManager.getInstance(context.getApplicationContext()).removeServer(server.getHostname());
}
} }
return null; return null;
})); });
} }
/** /**
......
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
...@@ -72,7 +72,7 @@ public class InputHostnameFragment extends AbstractFragment implements InputHost ...@@ -72,7 +72,7 @@ public class InputHostnameFragment extends AbstractFragment implements InputHost
private String getHostname() { private String getHostname() {
final TextView editor = (TextView) rootView.findViewById(R.id.editor_hostname); 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) { private void showError(String errString) {
......
package chat.rocket.android.fragment.add_server; package chat.rocket.android.fragment.add_server;
import chat.rocket.android.BackgroundLooper; import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.helper.Logger;
import chat.rocket.android.helper.OkHttpHelper; import chat.rocket.android.helper.OkHttpHelper;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
...@@ -46,7 +47,10 @@ public class InputHostnamePresenter extends BasePresenter<InputHostnameContract. ...@@ -46,7 +47,10 @@ public class InputHostnamePresenter extends BasePresenter<InputHostnameContract.
view.showInvalidServerError(); view.showInvalidServerError();
} }
}, },
throwable -> view.showConnectionError()); throwable -> {
Logger.report(throwable);
view.showConnectionError();
});
addSubscription(subscription); addSubscription(subscription);
} }
......
...@@ -22,6 +22,7 @@ abstract class AbstractChatRoomFragment extends AbstractFragment { ...@@ -22,6 +22,7 @@ abstract class AbstractChatRoomFragment extends AbstractFragment {
} }
protected void setToolbarTitle(CharSequence title) { protected void setToolbarTitle(CharSequence title) {
roomToolbar.hideChannelIcons();
roomToolbar.setTitle(title); roomToolbar.setTitle(title);
} }
......
...@@ -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
...@@ -22,6 +22,10 @@ public interface RoomContract { ...@@ -22,6 +22,10 @@ public interface RoomContract {
void onMessageSendSuccessfully(); void onMessageSendSuccessfully();
void disableMessageInput();
void enableMessageInput();
void showUnreadCount(int count); void showUnreadCount(int count);
void showMessages(List<Message> messages); void showMessages(List<Message> messages);
......
...@@ -119,6 +119,7 @@ public class RoomPresenter extends BasePresenter<RoomContract.View> ...@@ -119,6 +119,7 @@ public class RoomPresenter extends BasePresenter<RoomContract.View>
@Override @Override
public void sendMessage(String messageText) { public void sendMessage(String messageText) {
view.disableMessageInput();
final Disposable subscription = getRoomUserPair() final Disposable subscription = getRoomUserPair()
.flatMap(pair -> messageInteractor.send(pair.first, pair.second, messageText)) .flatMap(pair -> messageInteractor.send(pair.first, pair.second, messageText))
.subscribeOn(AndroidSchedulers.from(BackgroundLooper.get())) .subscribeOn(AndroidSchedulers.from(BackgroundLooper.get()))
...@@ -128,8 +129,12 @@ public class RoomPresenter extends BasePresenter<RoomContract.View> ...@@ -128,8 +129,12 @@ public class RoomPresenter extends BasePresenter<RoomContract.View>
if (success) { if (success) {
view.onMessageSendSuccessfully(); view.onMessageSendSuccessfully();
} }
view.enableMessageInput();
}, },
Logger::report throwable -> {
view.enableMessageInput();
Logger.report(throwable);
}
); );
addSubscription(subscription); addSubscription(subscription);
...@@ -148,6 +153,7 @@ public class RoomPresenter extends BasePresenter<RoomContract.View> ...@@ -148,6 +153,7 @@ public class RoomPresenter extends BasePresenter<RoomContract.View>
@Override @Override
public void updateMessage(Message message, String content) { public void updateMessage(Message message, String content) {
view.disableMessageInput();
final Disposable subscription = getCurrentUser() final Disposable subscription = getCurrentUser()
.flatMap(user -> messageInteractor.update(message, user, content)) .flatMap(user -> messageInteractor.update(message, user, content))
.subscribeOn(AndroidSchedulers.from(BackgroundLooper.get())) .subscribeOn(AndroidSchedulers.from(BackgroundLooper.get()))
...@@ -157,8 +163,12 @@ public class RoomPresenter extends BasePresenter<RoomContract.View> ...@@ -157,8 +163,12 @@ public class RoomPresenter extends BasePresenter<RoomContract.View>
if (success) { if (success) {
view.onMessageSendSuccessfully(); view.onMessageSendSuccessfully();
} }
view.enableMessageInput();
}, },
Logger::report throwable -> {
view.enableMessageInput();
Logger.report(throwable);
}
); );
addSubscription(subscription); addSubscription(subscription);
......
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
...@@ -2,6 +2,7 @@ package chat.rocket.android.fragment.sidebar; ...@@ -2,6 +2,7 @@ package chat.rocket.android.fragment.sidebar;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import bolts.Continuation;
import chat.rocket.core.models.RoomSidebar; import chat.rocket.core.models.RoomSidebar;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import java.util.List; import java.util.List;
...@@ -22,6 +23,8 @@ public interface SidebarMainContract { ...@@ -22,6 +23,8 @@ public interface SidebarMainContract {
void filterRoomSidebarList(CharSequence term); void filterRoomSidebarList(CharSequence term);
void show(User user); void show(User user);
void onLogoutCleanUp();
} }
interface Presenter extends BaseContract.Presenter<View> { interface Presenter extends BaseContract.Presenter<View> {
...@@ -42,6 +45,8 @@ public interface SidebarMainContract { ...@@ -42,6 +45,8 @@ public interface SidebarMainContract {
void onUserOffline(); void onUserOffline();
void onLogout(); void onLogout(Continuation<Void, Object> continuation);
void beforeLogoutCleanUp();
} }
} }
\ No newline at end of file
package chat.rocket.android.fragment.sidebar; package chat.rocket.android.fragment.sidebar;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
...@@ -8,13 +9,23 @@ import android.support.v4.app.DialogFragment; ...@@ -8,13 +9,23 @@ import android.support.v4.app.DialogFragment;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView; import android.support.v7.widget.SearchView;
import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton; import android.widget.CompoundButton;
import android.widget.TextView; import android.widget.TextView;
import com.jakewharton.rxbinding2.support.v7.widget.RxSearchView;
import com.jakewharton.rxbinding2.widget.RxCompoundButton;
import java.util.ArrayList;
import java.util.List;
import bolts.Task;
import chat.rocket.android.BuildConfig; import chat.rocket.android.BuildConfig;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.RocketChatCache; import chat.rocket.android.RocketChatCache;
import chat.rocket.android.activity.MainActivity;
import chat.rocket.android.api.MethodCallHelper; import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.fragment.AbstractFragment; import chat.rocket.android.fragment.AbstractFragment;
import chat.rocket.android.fragment.sidebar.dialog.AddChannelDialogFragment; import chat.rocket.android.fragment.sidebar.dialog.AddChannelDialogFragment;
...@@ -39,15 +50,9 @@ import chat.rocket.persistence.realm.repositories.RealmServerInfoRepository; ...@@ -39,15 +50,9 @@ import chat.rocket.persistence.realm.repositories.RealmServerInfoRepository;
import chat.rocket.persistence.realm.repositories.RealmSessionRepository; import chat.rocket.persistence.realm.repositories.RealmSessionRepository;
import chat.rocket.persistence.realm.repositories.RealmSpotlightRepository; import chat.rocket.persistence.realm.repositories.RealmSpotlightRepository;
import chat.rocket.persistence.realm.repositories.RealmUserRepository; import chat.rocket.persistence.realm.repositories.RealmUserRepository;
import com.jakewharton.rxbinding2.support.v7.widget.RxSearchView;
import com.jakewharton.rxbinding2.widget.RxCompoundButton;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable; import io.reactivex.disposables.Disposable;
import java.util.ArrayList;
import java.util.List;
public class SidebarMainFragment extends AbstractFragment implements SidebarMainContract.View { public class SidebarMainFragment extends AbstractFragment implements SidebarMainContract.View {
private SidebarMainContract.Presenter presenter; private SidebarMainContract.Presenter presenter;
private RoomListAdapter adapter; private RoomListAdapter adapter;
...@@ -87,26 +92,42 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain ...@@ -87,26 +92,42 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
new SessionInteractor(new RealmSessionRepository(hostname)) new SessionInteractor(new RealmSessionRepository(hostname))
); );
RocketChatCache rocketChatCache = new RocketChatCache(getContext().getApplicationContext());
presenter = new SidebarMainPresenter( presenter = new SidebarMainPresenter(
hostname, hostname,
new RoomInteractor(new RealmRoomRepository(hostname)), new RoomInteractor(new RealmRoomRepository(hostname)),
userRepository, userRepository,
new RocketChatCache(getContext()), rocketChatCache,
absoluteUrlHelper, absoluteUrlHelper,
new MethodCallHelper(getContext(), hostname), new MethodCallHelper(getContext(), hostname),
new RealmSpotlightRepository(hostname) new RealmSpotlightRepository(hostname)
); );
} }
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
presenter.bindView(this);
return view;
}
@Override
public void onDestroyView() {
presenter.release();
super.onDestroyView();
}
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
presenter.bindView(this);
} }
@Override @Override
public void onPause() { public void onPause() {
presenter.release();
super.onPause(); super.onPause();
} }
...@@ -219,11 +240,16 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain ...@@ -219,11 +240,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) rootView.findViewById(R.id.user_action_outer_container)
.setVisibility(show ? View.VISIBLE : View.GONE); .setVisibility(show ? View.VISIBLE : View.GONE);
} }
public void toggleUserActionContainer(boolean checked) {
CompoundButton toggleUserAction = rootView.findViewById(R.id.toggle_user_action);
toggleUserAction.setChecked(checked);
}
@Override @Override
public void showScreen() { public void showScreen() {
rootView.setVisibility(View.VISIBLE); rootView.setVisibility(View.VISIBLE);
...@@ -295,12 +321,32 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain ...@@ -295,12 +321,32 @@ public class SidebarMainFragment extends AbstractFragment implements SidebarMain
adapter.setRoomListHeaders(roomListHeaders); adapter.setRoomListHeaders(roomListHeaders);
} }
@Override
public void onLogoutCleanUp() {
Activity activity = getActivity();
if (activity != null && activity instanceof MainActivity) {
((MainActivity) activity).hideLogoutMessage();
((MainActivity) activity).onLogout();
presenter.onLogout(task -> {
if (task.isFaulted()) {
return Task.forError(task.getError());
}
return null;
});
}
}
private void setupLogoutButton() { private void setupLogoutButton() {
rootView.findViewById(R.id.btn_logout).setOnClickListener(view -> { rootView.findViewById(R.id.btn_logout).setOnClickListener(view -> {
presenter.onLogout();
closeUserActionContainer(); closeUserActionContainer();
// destroy Activity on logout to be able to recreate most of the environment // Clear relative data and set new hostname if any.
this.getActivity().finish(); presenter.beforeLogoutCleanUp();
final Activity activity = getActivity();
if (activity != null && activity instanceof MainActivity) {
((MainActivity) activity).showLogoutMessage();
// Clear subscriptions on MainPresenter.
((MainActivity) activity).beforeLogoutCleanUp();
}
}); });
} }
......
...@@ -6,13 +6,18 @@ import android.support.v4.util.Pair; ...@@ -6,13 +6,18 @@ import android.support.v4.util.Pair;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import bolts.Continuation;
import bolts.Task;
import chat.rocket.android.BackgroundLooper; import chat.rocket.android.BackgroundLooper;
import chat.rocket.android.RocketChatApplication;
import chat.rocket.android.RocketChatCache; import chat.rocket.android.RocketChatCache;
import chat.rocket.android.api.MethodCallHelper; import chat.rocket.android.api.MethodCallHelper;
import chat.rocket.android.helper.AbsoluteUrlHelper; import chat.rocket.android.helper.AbsoluteUrlHelper;
import chat.rocket.android.helper.LogIfError; import chat.rocket.android.helper.LogIfError;
import chat.rocket.android.helper.Logger; import chat.rocket.android.helper.Logger;
import chat.rocket.android.helper.TextUtils; import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.service.ConnectivityManager;
import chat.rocket.android.service.ConnectivityManagerApi;
import chat.rocket.android.shared.BasePresenter; import chat.rocket.android.shared.BasePresenter;
import chat.rocket.core.interactors.RoomInteractor; import chat.rocket.core.interactors.RoomInteractor;
import chat.rocket.core.models.Room; import chat.rocket.core.models.Room;
...@@ -21,6 +26,8 @@ import chat.rocket.core.models.Spotlight; ...@@ -21,6 +26,8 @@ import chat.rocket.core.models.Spotlight;
import chat.rocket.core.models.User; import chat.rocket.core.models.User;
import chat.rocket.core.repositories.SpotlightRepository; import chat.rocket.core.repositories.SpotlightRepository;
import chat.rocket.core.repositories.UserRepository; import chat.rocket.core.repositories.UserRepository;
import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.persistence.realm.RealmStore;
import chat.rocket.persistence.realm.repositories.RealmSpotlightRepository; import chat.rocket.persistence.realm.repositories.RealmSpotlightRepository;
import io.reactivex.Flowable; import io.reactivex.Flowable;
import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.android.schedulers.AndroidSchedulers;
...@@ -131,8 +138,31 @@ public class SidebarMainPresenter extends BasePresenter<SidebarMainContract.View ...@@ -131,8 +138,31 @@ public class SidebarMainPresenter extends BasePresenter<SidebarMainContract.View
} }
@Override @Override
public void onLogout() { public void onLogout(Continuation<Void, Object> continuation) {
methodCallHelper.logout().continueWith(new LogIfError()); methodCallHelper.logout().continueWith(task -> {
if (task.isFaulted()) {
Logger.report(task.getError());
return Task.forError(task.getError());
}
return task.onSuccess(continuation);
});
}
@Override
public void beforeLogoutCleanUp() {
clearSubscriptions();
String currentHostname = rocketChatCache.getSelectedServerHostname();
RealmHelper realmHelper = RealmStore.getOrCreate(currentHostname);
realmHelper.executeTransaction(realm -> {
realm.deleteAll();
ConnectivityManagerApi connectivityManagerApi = ConnectivityManager.getInstance(RocketChatApplication.getInstance());
connectivityManagerApi.removeServer(currentHostname);
rocketChatCache.removeHostname(currentHostname);
rocketChatCache.removeSelectedRoomId(currentHostname);
rocketChatCache.setSelectedServerHostname(rocketChatCache.getFirstLoggedHostnameIfAny());
view.onLogoutCleanUp();
return null;
});
} }
@Override @Override
......
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)?.mutate()
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;
......
package chat.rocket.android.layouthelper.extra_action.upload;
import android.content.Intent;
import chat.rocket.android.R;
public class FileUploadActionItem extends AbstractUploadActionItem {
@Override
public int getItemId() {
return 13;
}
@Override
protected Intent getIntentForPickFile() {
Intent intent = new Intent();
intent.setType("*/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
return Intent.createChooser(intent, "Select File to Upload");
}
@Override
public int getIcon() {
return R.drawable.ic_insert_drive_file_white_24dp;
}
@Override
public int getTitle() {
return R.string.doc_upload_message_spec_title;
}
}
...@@ -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();
......
...@@ -6,7 +6,7 @@ import chat.rocket.android.R ...@@ -6,7 +6,7 @@ import chat.rocket.android.R
import chat.rocket.android.helper.DateTime import chat.rocket.android.helper.DateTime
import chat.rocket.android.widget.AbsoluteUrl import chat.rocket.android.widget.AbsoluteUrl
import chat.rocket.android.widget.RocketChatAvatar 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.RocketChatMessageAttachmentsLayout
import chat.rocket.android.widget.message.RocketChatMessageLayout import chat.rocket.android.widget.message.RocketChatMessageLayout
import chat.rocket.android.widget.message.RocketChatMessageUrlsLayout import chat.rocket.android.widget.message.RocketChatMessageUrlsLayout
...@@ -21,12 +21,12 @@ class MessageRenderer(val message: Message, val autoLoadImage: Boolean) { ...@@ -21,12 +21,12 @@ class MessageRenderer(val message: Message, val autoLoadImage: Boolean) {
fun showAvatar(rocketChatAvatarWidget: RocketChatAvatar, hostname: String) { fun showAvatar(rocketChatAvatarWidget: RocketChatAvatar, hostname: String) {
val username: String? = message.user?.username val username: String? = message.user?.username
if (username != null) { if (username != null) {
val placeholderDrawable = UserAvatarHelper.getTextDrawable(username, rocketChatAvatarWidget.context) val placeholderDrawable = AvatarHelper.getTextDrawable(username, rocketChatAvatarWidget.context)
if (message.avatar != null) { if (message.avatar != null) {
// Load user's avatar image from Oauth provider URI. // Load user's avatar image from Oauth provider URI.
rocketChatAvatarWidget.loadImage(message.avatar, placeholderDrawable) rocketChatAvatarWidget.loadImage(message.avatar, placeholderDrawable)
} else { } else {
rocketChatAvatarWidget.loadImage(UserAvatarHelper.getUri(hostname, username), placeholderDrawable) rocketChatAvatarWidget.loadImage(AvatarHelper.getUri(hostname, username), placeholderDrawable)
} }
} else { } else {
rocketChatAvatarWidget.visibility = View.GONE rocketChatAvatarWidget.visibility = View.GONE
......
...@@ -4,7 +4,7 @@ import android.view.View ...@@ -4,7 +4,7 @@ import android.view.View
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import chat.rocket.android.widget.RocketChatAvatar 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 import chat.rocket.core.models.User
class UserRenderer(val user: User) { class UserRenderer(val user: User) {
...@@ -15,7 +15,7 @@ class UserRenderer(val user: User) { ...@@ -15,7 +15,7 @@ class UserRenderer(val user: User) {
fun showAvatar(rocketChatAvatarWidget: RocketChatAvatar, hostname: String) { fun showAvatar(rocketChatAvatarWidget: RocketChatAvatar, hostname: String) {
val username: String? = user.username val username: String? = user.username
if (username != null) { 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 { } else {
rocketChatAvatarWidget.visibility = View.GONE rocketChatAvatarWidget.visibility = View.GONE
} }
......
...@@ -7,10 +7,11 @@ import android.os.IBinder; ...@@ -7,10 +7,11 @@ import android.os.IBinder;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import chat.rocket.android.helper.RxHelper; import chat.rocket.android.helper.RxHelper;
import chat.rocket.android.log.RCLog; import chat.rocket.android.log.RCLog;
import chat.rocket.core.models.ServerInfo; import chat.rocket.core.models.ServerInfo;
...@@ -25,7 +26,7 @@ import rx.subjects.PublishSubject; ...@@ -25,7 +26,7 @@ import rx.subjects.PublishSubject;
*/ */
/*package*/ class RealmBasedConnectivityManager /*package*/ class RealmBasedConnectivityManager
implements ConnectivityManagerApi, ConnectivityManagerInternal { implements ConnectivityManagerApi, ConnectivityManagerInternal {
private final HashMap<String, Integer> serverConnectivityList = new HashMap<>(); private final ConcurrentHashMap<String, Integer> serverConnectivityList = new ConcurrentHashMap<>();
private final PublishSubject<ServerConnectivity> connectivitySubject = PublishSubject.create(); private final PublishSubject<ServerConnectivity> connectivitySubject = PublishSubject.create();
private Context appContext; private Context appContext;
private final ServiceConnection serviceConnection = new ServiceConnection() { private final ServiceConnection serviceConnection = new ServiceConnection() {
......
...@@ -12,7 +12,6 @@ import java.util.HashMap; ...@@ -12,7 +12,6 @@ import java.util.HashMap;
import java.util.concurrent.Semaphore; import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import chat.rocket.android.activity.MainActivity;
import chat.rocket.persistence.realm.RealmStore; import chat.rocket.persistence.realm.RealmStore;
import hugo.weaving.DebugLog; import hugo.weaving.DebugLog;
import rx.Observable; import rx.Observable;
...@@ -93,12 +92,6 @@ public class RocketChatService extends Service implements ConnectivityServiceInt ...@@ -93,12 +92,6 @@ public class RocketChatService extends Service implements ConnectivityServiceInt
webSocketThreads.remove(hostname); webSocketThreads.remove(hostname);
// remove RealmConfiguration key from HashMap // remove RealmConfiguration key from HashMap
RealmStore.sStore.remove(hostname); RealmStore.sStore.remove(hostname);
// clear "cache" SharedPreference
this.getSharedPreferences("cache", 0).edit().clear().apply();
// start a fresh new MainActivity
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(intent);
}); });
} else { } else {
return Observable.timer(1, TimeUnit.SECONDS).toSingle() return Observable.timer(1, TimeUnit.SECONDS).toSingle()
......
...@@ -255,6 +255,10 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -255,6 +255,10 @@ public class RocketChatWebSocketThread extends HandlerThread {
return prepareDDPClient() return prepareDDPClient()
.flatMap(_val -> Single.fromEmitter(emitter -> { .flatMap(_val -> Single.fromEmitter(emitter -> {
ServerInfo info = connectivityManager.getServerInfoForHost(hostname); ServerInfo info = connectivityManager.getServerInfoForHost(hostname);
if (info == null) {
emitter.onSuccess(false);
return;
}
RCLog.d("DDPClient#connect"); RCLog.d("DDPClient#connect");
ddpClient.connect(info.getSession(), info.isSecure()) ddpClient.connect(info.getSession(), info.isSecure())
.onSuccessTask(task -> { .onSuccessTask(task -> {
......
package chat.rocket.android.service.observer; package chat.rocket.android.service.observer;
import android.content.Context;
import com.google.android.gms.gcm.GoogleCloudMessaging; import com.google.android.gms.gcm.GoogleCloudMessaging;
import com.google.android.gms.iid.InstanceID; import com.google.android.gms.iid.InstanceID;
import android.content.Context;
import io.realm.Realm;
import io.realm.RealmResults;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import bolts.Task; import bolts.Task;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.RocketChatCache; import chat.rocket.android.RocketChatCache;
import chat.rocket.android.api.RaixPushHelper; import chat.rocket.android.api.RaixPushHelper;
import chat.rocket.android.helper.LogIfError; import chat.rocket.android.helper.LogIfError;
import chat.rocket.android.service.DDPClientRef;
import chat.rocket.core.SyncState; import chat.rocket.core.SyncState;
import chat.rocket.persistence.realm.models.ddp.RealmPublicSetting; import chat.rocket.persistence.realm.RealmHelper;
import chat.rocket.core.PublicSettingsConstants;
import chat.rocket.persistence.realm.models.ddp.RealmUser; import chat.rocket.persistence.realm.models.ddp.RealmUser;
import chat.rocket.persistence.realm.models.internal.GcmPushRegistration; import chat.rocket.persistence.realm.models.internal.GcmPushRegistration;
import chat.rocket.persistence.realm.RealmHelper; import io.realm.Realm;
import chat.rocket.android.service.DDPClientRef; import io.realm.RealmResults;
/** /**
* call raix:push-update if needed. * call raix:push-update if needed.
...@@ -83,13 +82,6 @@ public class GcmPushRegistrationObserver extends AbstractModelObserver<GcmPushRe ...@@ -83,13 +82,6 @@ public class GcmPushRegistrationObserver extends AbstractModelObserver<GcmPushRe
} }
private String getSenderId() { private String getSenderId() {
final String senderId = RealmPublicSetting
.getString(realmHelper, PublicSettingsConstants.Push.GCM_PROJECT_NUMBER, "").trim();
if (senderId.length() != 0) {
return senderId;
}
return context.getString(R.string.gcm_sender_id); return context.getString(R.string.gcm_sender_id);
} }
......
<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
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6L6,2zM13,9L13,3.5L18.5,9L13,9z"/>
</vector>
...@@ -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"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="@+id/room_user_titlebar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:paddingEnd="@dimen/margin_16"
android:paddingStart="@dimen/margin_16">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical"
android:text="@string/users_of_room_title"
android:textAppearance="@style/TextAppearance.AppCompat.Title" />
<TextView
android:id="@+id/room_user_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
</FrameLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" />
</LinearLayout>
<chat.rocket.android.widget.WaitingView
android:id="@+id/waiting"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</FrameLayout>
\ No newline at end of file
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
android:layout_marginTop="@dimen/margin_8" android:layout_marginTop="@dimen/margin_8"
android:hint="@string/fragment_input_hostname_server_hint" android:hint="@string/fragment_input_hostname_server_hint"
android:imeOptions="actionSend" android:imeOptions="actionSend"
android:inputType="textWebEditText" android:inputType="textUri"
android:maxLines="1" android:maxLines="1"
app:layout_constraintTop_toBottomOf="@+id/hostnameTextView" app:layout_constraintTop_toBottomOf="@+id/hostnameTextView"
app:layout_constraintBottom_toTopOf="@+id/btn_connect" app:layout_constraintBottom_toTopOf="@+id/btn_connect"
......
<?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_marginLeft="@dimen/margin_16"
android:layout_marginStart="@dimen/margin_16"
android:layout_marginRight="@dimen/margin_16"
android:layout_marginEnd="@dimen/margin_16"
android:layout_centerInParent="true"
android:gravity="center"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
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"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android" <android.support.v4.widget.SlidingPaneLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/sub_sliding_pane" android:id="@+id/sub_sliding_pane"
android:layout_width="280dp" android:layout_width="280dp"
android:layout_height="match_parent" android:layout_height="match_parent"
...@@ -7,30 +8,59 @@ ...@@ -7,30 +8,59 @@
android:theme="@style/AppTheme.Dark"> android:theme="@style/AppTheme.Dark">
<android.support.v4.widget.NestedScrollView <android.support.v4.widget.NestedScrollView
android:layout_width="96dp" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="start" android:layout_gravity="start"
android:background="?attr/colorPrimaryDark"> android:background="?attr/colorPrimaryDark">
<LinearLayout <LinearLayout
android:id="@+id/server_list_bar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<ImageButton <android.support.constraint.ConstraintLayout
style="@style/Base.Widget.AppCompat.Button.Borderless" android:id="@+id/btn_add_server"
android:layout_width="80dp" android:layout_width="match_parent"
android:layout_height="80dp" android:layout_height="80dp"
android:layout_margin="@dimen/margin_8" android:background="?selectableItemBackground"
android:src="@mipmap/ic_launcher" /> android:descendantFocusability="afterDescendants">
<io.github.yusukeiwaki.android.widget.FontAwesomeButton <io.github.yusukeiwaki.android.widget.FontAwesomeTextView
android:id="@+id/fa_add_server"
style="@style/Base.Widget.AppCompat.Button.Borderless" style="@style/Base.Widget.AppCompat.Button.Borderless"
android:layout_width="80dp" android:layout_width="80dp"
android:layout_height="80dp" android:layout_height="80dp"
android:layout_margin="@dimen/margin_8" android:layout_margin="@dimen/margin_8"
android:text="@string/fa_plus" /> 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" />
<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> </LinearLayout>
</android.support.v4.widget.NestedScrollView> </android.support.v4.widget.NestedScrollView>
<FrameLayout <FrameLayout
......
<?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,12 +13,17 @@ ...@@ -13,12 +13,17 @@
<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.\n%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>
<plurals name="fmt_room_user_count">
<item quantity="one">Total: %,d user</item>
<item quantity="other">Total: %,d users</item>
</plurals>
<string name="sending">Sending…</string> <string name="sending">Sending…</string>
<string name="not_synced">Not synced</string> <string name="not_synced">Not synced</string>
<string name="failed_to_sync">Failed to sync</string> <string name="failed_to_sync">Failed to sync</string>
...@@ -36,6 +41,7 @@ ...@@ -36,6 +41,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>
...@@ -57,6 +63,7 @@ ...@@ -57,6 +63,7 @@
<string name="video_upload_message_spec_title">Attach video</string> <string name="video_upload_message_spec_title">Attach video</string>
<string name="input_hostname_invalid_server_message">Invalid server version</string> <string name="input_hostname_invalid_server_message">Invalid server version</string>
<string name="make_sure_your_server_version_is_up_to_date">Make sure your Rocket.Chat server version is up to date</string>
<string name="connection_error_try_later">There\'s a connection error. Please try later.</string> <string name="connection_error_try_later">There\'s a connection error. Please try later.</string>
<string name="version_info_text">Version: %s</string> <string name="version_info_text">Version: %s</string>
...@@ -68,4 +75,11 @@ ...@@ -68,4 +75,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>
</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;
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
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,8 @@ ext { ...@@ -22,7 +22,8 @@ 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"
} }
task clean(type: Delete) { task clean(type: Delete) {
......
...@@ -29,11 +29,17 @@ public class RealmLoginServiceConfigurationRepository extends RealmRepository ...@@ -29,11 +29,17 @@ public class RealmLoginServiceConfigurationRepository extends RealmRepository
public Single<Optional<LoginServiceConfiguration>> getByName(String serviceName) { public Single<Optional<LoginServiceConfiguration>> getByName(String serviceName) {
return Single.defer(() -> Flowable.using( return Single.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable( pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmMeteorLoginServiceConfiguration.class) pair.first.where(RealmMeteorLoginServiceConfiguration.class)
.equalTo(RealmMeteorLoginServiceConfiguration.SERVICE, serviceName) .equalTo(RealmMeteorLoginServiceConfiguration.SERVICE, serviceName)
.findAll() .findAll()
.<RealmResults<RealmMeteorLoginServiceConfiguration>>asObservable()), .<RealmResults<RealmMeteorLoginServiceConfiguration>>asObservable());
},
pair -> close(pair.first, pair.second) pair -> close(pair.first, pair.second)
) )
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper())) .unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
...@@ -46,10 +52,16 @@ public class RealmLoginServiceConfigurationRepository extends RealmRepository ...@@ -46,10 +52,16 @@ public class RealmLoginServiceConfigurationRepository extends RealmRepository
public Flowable<List<LoginServiceConfiguration>> getAll() { public Flowable<List<LoginServiceConfiguration>> getAll() {
return Flowable.defer(() -> Flowable.using( return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop
.toV2Flowable(pair.first.where(RealmMeteorLoginServiceConfiguration.class) .toV2Flowable(pair.first.where(RealmMeteorLoginServiceConfiguration.class)
.findAll() .findAll()
.asObservable()), .asObservable());
},
pair -> close(pair.first, pair.second) pair -> close(pair.first, pair.second)
) )
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper())) .unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
...@@ -36,11 +36,17 @@ public class RealmMessageRepository extends RealmRepository implements MessageRe ...@@ -36,11 +36,17 @@ public class RealmMessageRepository extends RealmRepository implements MessageRe
public Single<Optional<Message>> getById(String messageId) { public Single<Optional<Message>> getById(String messageId) {
return Single.defer(() -> Flowable.using( return Single.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable( pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmMessage.class) pair.first.where(RealmMessage.class)
.equalTo(RealmMessage.ID, messageId) .equalTo(RealmMessage.ID, messageId)
.findAll() .findAll()
.<RealmResults<RealmMessage>>asObservable()), .<RealmResults<RealmMessage>>asObservable());
},
pair -> close(pair.first, pair.second) pair -> close(pair.first, pair.second)
) )
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper())) .unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
...@@ -133,11 +139,17 @@ public class RealmMessageRepository extends RealmRepository implements MessageRe ...@@ -133,11 +139,17 @@ public class RealmMessageRepository extends RealmRepository implements MessageRe
public Flowable<List<Message>> getAllFrom(Room room) { public Flowable<List<Message>> getAllFrom(Room room) {
return Flowable.defer(() -> Flowable.using( return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(pair.first.where(RealmMessage.class) pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(pair.first.where(RealmMessage.class)
.equalTo(RealmMessage.ROOM_ID, room.getRoomId()) .equalTo(RealmMessage.ROOM_ID, room.getRoomId())
.isNotNull(RealmMessage.USER) .isNotNull(RealmMessage.USER)
.findAllSorted(RealmMessage.TIMESTAMP, Sort.DESCENDING) .findAllSorted(RealmMessage.TIMESTAMP, Sort.DESCENDING)
.asObservable()), .asObservable());
},
pair -> close(pair.first, pair.second) pair -> close(pair.first, pair.second)
) )
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper())) .unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
...@@ -150,12 +162,18 @@ public class RealmMessageRepository extends RealmRepository implements MessageRe ...@@ -150,12 +162,18 @@ public class RealmMessageRepository extends RealmRepository implements MessageRe
public Single<Integer> unreadCountFor(Room room, User user) { public Single<Integer> unreadCountFor(Room room, User user) {
return Single.defer(() -> Flowable.using( return Single.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable(pair.first.where(RealmMessage.class) pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(pair.first.where(RealmMessage.class)
.equalTo(RealmMessage.ROOM_ID, room.getId()) .equalTo(RealmMessage.ROOM_ID, room.getId())
.greaterThanOrEqualTo(RealmMessage.TIMESTAMP, room.getLastSeen()) .greaterThanOrEqualTo(RealmMessage.TIMESTAMP, room.getLastSeen())
.notEqualTo(RealmMessage.USER_ID, user.getId()) .notEqualTo(RealmMessage.USER_ID, user.getId())
.findAll() .findAll()
.asObservable()), .asObservable());
},
pair -> close(pair.first, pair.second) pair -> close(pair.first, pair.second)
) )
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper())) .unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
...@@ -26,11 +26,16 @@ public class RealmPermissionRepository extends RealmRepository implements Permis ...@@ -26,11 +26,16 @@ public class RealmPermissionRepository extends RealmRepository implements Permis
public Single<Optional<Permission>> getById(String id) { public Single<Optional<Permission>> getById(String id) {
return Single.defer(() -> Flowable.using( return Single.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable( pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmPermission.class) pair.first.where(RealmPermission.class)
.equalTo(RealmPermission.Columns.ID, id) .equalTo(RealmPermission.Columns.ID, id)
.findAll() .findAll()
.<RealmResults<RealmPermission>>asObservable()), .<RealmResults<RealmPermission>>asObservable());
},
pair -> close(pair.first, pair.second) pair -> close(pair.first, pair.second)
) )
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper())) .unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
...@@ -27,11 +27,16 @@ public class RealmPublicSettingRepository extends RealmRepository ...@@ -27,11 +27,16 @@ public class RealmPublicSettingRepository extends RealmRepository
public Single<Optional<PublicSetting>> getById(String id) { public Single<Optional<PublicSetting>> getById(String id) {
return Single.defer(() -> Flowable.using( return Single.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable( pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmPublicSetting.class) pair.first.where(RealmPublicSetting.class)
.equalTo(RealmPublicSetting.ID, id) .equalTo(RealmPublicSetting.ID, id)
.findAll() .findAll()
.<RealmResults<RealmPublicSetting>>asObservable()), .<RealmResults<RealmPublicSetting>>asObservable());
},
pair -> close(pair.first, pair.second) pair -> close(pair.first, pair.second)
) )
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper())) .unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
...@@ -34,10 +34,16 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito ...@@ -34,10 +34,16 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito
public Flowable<List<Room>> getAll() { public Flowable<List<Room>> getAll() {
return Flowable.defer(() -> Flowable.using( return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable( pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmRoom.class) pair.first.where(RealmRoom.class)
.findAll() .findAll()
.asObservable()), .asObservable());
},
pair -> close(pair.first, pair.second) pair -> close(pair.first, pair.second)
) )
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper())) .unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
...@@ -51,6 +57,10 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito ...@@ -51,6 +57,10 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito
return Flowable.defer(() -> Flowable.using( return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> { pair -> {
if (pair.first == null) {
return Flowable.empty();
}
RealmRoom realmRoom = pair.first.where(RealmRoom.class) RealmRoom realmRoom = pair.first.where(RealmRoom.class)
.equalTo(RealmRoom.ROOM_ID, roomId) .equalTo(RealmRoom.ROOM_ID, roomId)
.findFirst(); .findFirst();
...@@ -84,6 +94,9 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito ...@@ -84,6 +94,9 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito
return Flowable.defer(() -> Flowable.using( return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> { pair -> {
if (pair.first == null) {
return Flowable.empty();
}
LoadMessageProcedure messageProcedure = pair.first.where(LoadMessageProcedure.class) LoadMessageProcedure messageProcedure = pair.first.where(LoadMessageProcedure.class)
.equalTo(LoadMessageProcedure.ID, roomId) .equalTo(LoadMessageProcedure.ID, roomId)
...@@ -147,7 +160,11 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito ...@@ -147,7 +160,11 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito
public Flowable<List<Room>> getSortedLikeName(String name, SortDirection direction, int limit) { public Flowable<List<Room>> getSortedLikeName(String name, SortDirection direction, int limit) {
return Flowable.defer(() -> Flowable.using( return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable( pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmRoom.class) pair.first.where(RealmRoom.class)
.like(RealmRoom.NAME, "*" + name + "*", Case.INSENSITIVE) .like(RealmRoom.NAME, "*" + name + "*", Case.INSENSITIVE)
.beginGroup() .beginGroup()
...@@ -157,7 +174,8 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito ...@@ -157,7 +174,8 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito
.endGroup() .endGroup()
.findAllSorted(RealmRoom.NAME, .findAllSorted(RealmRoom.NAME,
direction.equals(SortDirection.ASC) ? Sort.ASCENDING : Sort.DESCENDING) direction.equals(SortDirection.ASC) ? Sort.ASCENDING : Sort.DESCENDING)
.asObservable()), .asObservable());
},
pair -> close(pair.first, pair.second) pair -> close(pair.first, pair.second)
) )
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper())) .unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
...@@ -170,7 +188,11 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito ...@@ -170,7 +188,11 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito
public Flowable<List<Room>> getLatestSeen(int limit) { public Flowable<List<Room>> getLatestSeen(int limit) {
return Flowable.defer(() -> Flowable.using( return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable( pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmRoom.class) pair.first.where(RealmRoom.class)
.beginGroup() .beginGroup()
.equalTo(RealmRoom.TYPE, RealmRoom.TYPE_CHANNEL) .equalTo(RealmRoom.TYPE, RealmRoom.TYPE_CHANNEL)
...@@ -178,7 +200,8 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito ...@@ -178,7 +200,8 @@ public class RealmRoomRepository extends RealmRepository implements RoomReposito
.equalTo(RealmRoom.TYPE, RealmRoom.TYPE_PRIVATE) .equalTo(RealmRoom.TYPE, RealmRoom.TYPE_PRIVATE)
.endGroup() .endGroup()
.findAllSorted(RealmRoom.LAST_SEEN, Sort.ASCENDING) .findAllSorted(RealmRoom.LAST_SEEN, Sort.ASCENDING)
.asObservable()), .asObservable());
},
pair -> close(pair.first, pair.second) pair -> close(pair.first, pair.second)
) )
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper())) .unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
...@@ -29,12 +29,17 @@ public class RealmRoomRoleRepository extends RealmRepository implements RoomRole ...@@ -29,12 +29,17 @@ public class RealmRoomRoleRepository extends RealmRepository implements RoomRole
public Single<Optional<RoomRole>> getFor(Room room, User user) { public Single<Optional<RoomRole>> getFor(Room room, User user) {
return Single.defer(() -> Flowable.using( return Single.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable( pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmRoomRole.class) pair.first.where(RealmRoomRole.class)
.equalTo(RealmRoomRole.Columns.ROOM_ID, room.getId()) .equalTo(RealmRoomRole.Columns.ROOM_ID, room.getId())
.equalTo(RealmRoomRole.Columns.USER + "." + RealmUser.ID, user.getId()) .equalTo(RealmRoomRole.Columns.USER + "." + RealmUser.ID, user.getId())
.findAll() .findAll()
.<RealmResults<RealmRoomRole>>asObservable()), .<RealmResults<RealmRoomRole>>asObservable());
},
pair -> close(pair.first, pair.second) pair -> close(pair.first, pair.second)
) )
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper())) .unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
...@@ -26,11 +26,17 @@ public class RealmSessionRepository extends RealmRepository implements SessionRe ...@@ -26,11 +26,17 @@ public class RealmSessionRepository extends RealmRepository implements SessionRe
public Flowable<Optional<Session>> getById(int id) { public Flowable<Optional<Session>> getById(int id) {
return Flowable.defer(() -> Flowable.using( return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable( pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmSession.class) pair.first.where(RealmSession.class)
.equalTo(RealmSession.ID, id) .equalTo(RealmSession.ID, id)
.findAll() .findAll()
.<RealmSession>asObservable()), .<RealmSession>asObservable());
},
pair -> close(pair.first, pair.second) pair -> close(pair.first, pair.second)
) )
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper())) .unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
...@@ -20,7 +20,12 @@ class RealmSpotlightRepository(private val hostname: String) : RealmRepository() ...@@ -20,7 +20,12 @@ class RealmSpotlightRepository(private val hostname: String) : RealmRepository()
override fun getSuggestionsFor(term: String, limit: Int): Flowable<List<Spotlight>> { override fun getSuggestionsFor(term: String, limit: Int): Flowable<List<Spotlight>> {
return Flowable.defer { Flowable.using<RealmResults<RealmSpotlight>, Pair<Realm, Looper>>({ return Flowable.defer { Flowable.using<RealmResults<RealmSpotlight>, Pair<Realm, Looper>>({
Pair(RealmStore.getRealm(hostname), Looper.myLooper()) Pair(RealmStore.getRealm(hostname), Looper.myLooper())
}, { pair -> RxJavaInterop.toV2Flowable<RealmResults<RealmSpotlight>>(pair.first.where(RealmSpotlight::class.java) }, { pair ->
if (pair.first == null) {
return@using Flowable.empty()
}
return@using RxJavaInterop.toV2Flowable<RealmResults<RealmSpotlight>>(pair.first.where(RealmSpotlight::class.java)
.findAllSorted(Columns.TYPE, Sort.DESCENDING) .findAllSorted(Columns.TYPE, Sort.DESCENDING)
.asObservable()) .asObservable())
}) { pair -> close(pair.first, pair.second) } }) { pair -> close(pair.first, pair.second) }
......
...@@ -29,7 +29,12 @@ public class RealmSpotlightRoomRepository extends RealmRepository implements Spo ...@@ -29,7 +29,12 @@ public class RealmSpotlightRoomRepository extends RealmRepository implements Spo
public Flowable<List<SpotlightRoom>> getSuggestionsFor(String name, SortDirection direction, int limit) { public Flowable<List<SpotlightRoom>> getSuggestionsFor(String name, SortDirection direction, int limit) {
return Flowable.defer(() -> Flowable.using( return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable( pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmSpotlightRoom.class) pair.first.where(RealmSpotlightRoom.class)
.like(RealmSpotlightRoom.Columns.NAME, "*" + name + "*", Case.INSENSITIVE) .like(RealmSpotlightRoom.Columns.NAME, "*" + name + "*", Case.INSENSITIVE)
.beginGroup() .beginGroup()
...@@ -38,7 +43,8 @@ public class RealmSpotlightRoomRepository extends RealmRepository implements Spo ...@@ -38,7 +43,8 @@ public class RealmSpotlightRoomRepository extends RealmRepository implements Spo
.equalTo(RealmSpotlightRoom.Columns.TYPE, RealmRoom.TYPE_PRIVATE) .equalTo(RealmSpotlightRoom.Columns.TYPE, RealmRoom.TYPE_PRIVATE)
.endGroup() .endGroup()
.findAllSorted(RealmSpotlightRoom.Columns.NAME, direction.equals(SortDirection.ASC) ? Sort.ASCENDING : Sort.DESCENDING) .findAllSorted(RealmSpotlightRoom.Columns.NAME, direction.equals(SortDirection.ASC) ? Sort.ASCENDING : Sort.DESCENDING)
.asObservable()), .asObservable());
},
pair -> close(pair.first, pair.second) pair -> close(pair.first, pair.second)
) )
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper())) .unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
...@@ -28,7 +28,12 @@ public class RealmSpotlightUserRepository extends RealmRepository implements Spo ...@@ -28,7 +28,12 @@ public class RealmSpotlightUserRepository extends RealmRepository implements Spo
public Flowable<List<SpotlightUser>> getSuggestionsFor(String name, SortDirection direction, int limit) { public Flowable<List<SpotlightUser>> getSuggestionsFor(String name, SortDirection direction, int limit) {
return Flowable.defer(() -> Flowable.using( return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable( pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmSpotlightUser.class) pair.first.where(RealmSpotlightUser.class)
.beginGroup() .beginGroup()
.like(RealmSpotlightUser.Columns.USERNAME, "*" + name + "*", Case.INSENSITIVE) .like(RealmSpotlightUser.Columns.USERNAME, "*" + name + "*", Case.INSENSITIVE)
...@@ -41,7 +46,8 @@ public class RealmSpotlightUserRepository extends RealmRepository implements Spo ...@@ -41,7 +46,8 @@ public class RealmSpotlightUserRepository extends RealmRepository implements Spo
.endGroup() .endGroup()
.findAllSorted(RealmSpotlightUser.Columns.USERNAME, .findAllSorted(RealmSpotlightUser.Columns.USERNAME,
direction.equals(SortDirection.ASC) ? Sort.ASCENDING : Sort.DESCENDING) direction.equals(SortDirection.ASC) ? Sort.ASCENDING : Sort.DESCENDING)
.asObservable()), .asObservable());
},
pair -> close(pair.first, pair.second) pair -> close(pair.first, pair.second)
) )
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper())) .unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
......
...@@ -31,10 +31,16 @@ public class RealmUserRepository extends RealmRepository implements UserReposito ...@@ -31,10 +31,16 @@ public class RealmUserRepository extends RealmRepository implements UserReposito
public Flowable<List<User>> getAll() { public Flowable<List<User>> getAll() {
return Flowable.defer(() -> Flowable.using( return Flowable.defer(() -> Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable( pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmUser.class) pair.first.where(RealmUser.class)
.findAll() .findAll()
.asObservable()), .asObservable());
},
pair -> close(pair.first, pair.second)) pair -> close(pair.first, pair.second))
.unsubscribeOn(AndroidSchedulers.from(Looper.myLooper())) .unsubscribeOn(AndroidSchedulers.from(Looper.myLooper()))
.filter(roomSubscriptions -> roomSubscriptions != null && roomSubscriptions.isLoaded() .filter(roomSubscriptions -> roomSubscriptions != null && roomSubscriptions.isLoaded()
...@@ -60,11 +66,17 @@ public class RealmUserRepository extends RealmRepository implements UserReposito ...@@ -60,11 +66,17 @@ public class RealmUserRepository extends RealmRepository implements UserReposito
private Flowable<RealmResults<RealmUser>> realmGetCurrent() { private Flowable<RealmResults<RealmUser>> realmGetCurrent() {
return Flowable.using( return Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable( pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmUser.class) pair.first.where(RealmUser.class)
.isNotEmpty(RealmUser.EMAILS) .isNotEmpty(RealmUser.EMAILS)
.findAll() .findAll()
.<RealmResults<RealmUser>>asObservable()), .<RealmResults<RealmUser>>asObservable());
},
pair -> close(pair.first, pair.second)); pair -> close(pair.first, pair.second));
} }
...@@ -90,6 +102,10 @@ public class RealmUserRepository extends RealmRepository implements UserReposito ...@@ -90,6 +102,10 @@ public class RealmUserRepository extends RealmRepository implements UserReposito
} }
private Flowable<Optional<RealmUser>> realmQueryUsername(Realm realm, String username) { private Flowable<Optional<RealmUser>> realmQueryUsername(Realm realm, String username) {
if (realm == null) {
return Flowable.empty();
}
RealmUser realmUser = realm.where(RealmUser.class) RealmUser realmUser = realm.where(RealmUser.class)
.equalTo(RealmUser.USERNAME, username) .equalTo(RealmUser.USERNAME, username)
.findFirst(); .findFirst();
...@@ -118,11 +134,17 @@ public class RealmUserRepository extends RealmRepository implements UserReposito ...@@ -118,11 +134,17 @@ public class RealmUserRepository extends RealmRepository implements UserReposito
private Flowable<RealmResults<RealmUser>> realmGetSortedLikeName(String name) { private Flowable<RealmResults<RealmUser>> realmGetSortedLikeName(String name) {
return Flowable.using( return Flowable.using(
() -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()), () -> new Pair<>(RealmStore.getRealm(hostname), Looper.myLooper()),
pair -> RxJavaInterop.toV2Flowable( pair -> {
if (pair.first == null) {
return Flowable.empty();
}
return RxJavaInterop.toV2Flowable(
pair.first.where(RealmUser.class) pair.first.where(RealmUser.class)
.like(RealmUser.USERNAME, "*" + name + "*", Case.INSENSITIVE) .like(RealmUser.USERNAME, "*" + name + "*", Case.INSENSITIVE)
.findAllSorted(RealmUser.USERNAME, Sort.DESCENDING) .findAllSorted(RealmUser.USERNAME, Sort.DESCENDING)
.asObservable()), .asObservable());
},
pair -> close(pair.first, pair.second)); pair -> close(pair.first, pair.second));
} }
......
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
buildscript { buildscript {
repositories { repositories {
...@@ -62,7 +63,7 @@ dependencies { ...@@ -62,7 +63,7 @@ dependencies {
compile 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' compile 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
compile 'com.squareup.okhttp3:okhttp:3.8.0' compile "com.squareup.okhttp3:okhttp:$rootProject.ext.okHttpVersion"
compile 'com.github.yusukeiwaki.android-widget:widget-fontawesome:0.0.1' compile 'com.github.yusukeiwaki.android-widget:widget-fontawesome:0.0.1'
......
...@@ -65,7 +65,7 @@ public class RoomToolbar extends Toolbar { ...@@ -65,7 +65,7 @@ public class RoomToolbar extends Toolbar {
privateChannelDrawable = VectorDrawableCompat.create(getResources(), R.drawable.ic_lock_white_24dp, null); privateChannelDrawable = VectorDrawableCompat.create(getResources(), R.drawable.ic_lock_white_24dp, null);
publicChannelDrawable = VectorDrawableCompat.create(getResources(), R.drawable.ic_hashtag_white_24dp, null); publicChannelDrawable = VectorDrawableCompat.create(getResources(), R.drawable.ic_hashtag_white_24dp, null);
livechatChannelDrawable = VectorDrawableCompat.create(getResources(), R.drawable.ic_livechat_white_24dp, null); livechatChannelDrawable = VectorDrawableCompat.create(getResources(), R.drawable.ic_livechat_white_24dp, null);
userStatusDrawable = VectorDrawableCompat.create(getResources(), R.drawable.ic_user_status_black_24dp, null); userStatusDrawable = VectorDrawableCompat.create(getResources(), R.drawable.ic_user_status_black_24dp, null).mutate();
} }
private void setNavigationIcon(Context context) { private void setNavigationIcon(Context context) {
...@@ -92,6 +92,11 @@ public class RoomToolbar extends Toolbar { ...@@ -92,6 +92,11 @@ public class RoomToolbar extends Toolbar {
toolbarText.setText(title); toolbarText.setText(title);
} }
public void hideChannelIcons() {
roomTypeImage.setVisibility(GONE);
userStatusImage.setVisibility(GONE);
}
public void showPrivateChannelIcon() { public void showPrivateChannelIcon() {
roomTypeImage.setImageDrawable(privateChannelDrawable); roomTypeImage.setImageDrawable(privateChannelDrawable);
userStatusImage.setVisibility(GONE); userStatusImage.setVisibility(GONE);
...@@ -137,7 +142,7 @@ public class RoomToolbar extends Toolbar { ...@@ -137,7 +142,7 @@ public class RoomToolbar extends Toolbar {
userStatusImage.setVisibility(VISIBLE); userStatusImage.setVisibility(VISIBLE);
} }
public void setUnreadBudge(int numUnreadChannels, int numMentionsSum) { public void setUnreadBadge(int numUnreadChannels, int numMentionsSum) {
if (getNavigationIcon() == null) { if (getNavigationIcon() == null) {
return; return;
} }
......
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
object UserAvatarHelper { /**
* Created by Filipe de Lima Brito (filipedelimabrito@gmail.com) on 8/02/17.
*/
object AvatarHelper {
/** /**
* Returns the user avatar URI. * Returns the user avatar URI.
...@@ -45,6 +49,7 @@ object UserAvatarHelper { ...@@ -45,6 +49,7 @@ object UserAvatarHelper {
* @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 UserAvatarHelper { ...@@ -57,6 +62,19 @@ object UserAvatarHelper {
.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 UserAvatarHelper { ...@@ -70,15 +88,15 @@ object UserAvatarHelper {
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;
import android.view.View;
/**
* A {@linkplain View.OnClickListener click listener} that debounces multiple clicks posted in the
* same frame. A click on one button disables all buttons for that frame.
*
* Shamelessly copied from butterknife -> https://github.com/JakeWharton/butterknife/blob/master/butterknife/src/main/java/butterknife/internal/DebouncingOnClickListener.java
*/
public abstract class DebouncingOnClickListener implements View.OnClickListener {
static boolean enabled = true;
private static final Runnable ENABLE_AGAIN = new Runnable() {
@Override public void run() {
enabled = true;
}
};
@Override public final void onClick(View v) {
if (enabled) {
enabled = false;
v.post(ENABLE_AGAIN);
doClick(v);
}
}
public abstract void doClick(View v);
}
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.
* *
* @param drawable The drawable to wrap. * @param drawable The drawable to wrap.
* @see tintDrawable * @see tintDrawable
*/ */
fun wrapDrawable(drawable: Drawable) { fun wrapDrawable(drawable: Drawable?) {
if (drawable != null) {
DrawableCompat.wrap(drawable) DrawableCompat.wrap(drawable)
} }
}
/** /**
* REMARK: You MUST always wrap the drawable before tint it. * REMARK: You MUST always wrap the drawable before tint it.
...@@ -25,7 +59,9 @@ object DrawableHelper { ...@@ -25,7 +59,9 @@ object DrawableHelper {
* @param resId The resource id color to tint the drawable. * @param resId The resource id color to tint the drawable.
* @see wrapDrawable * @see wrapDrawable
*/ */
fun tintDrawable(drawable: Drawable, context: Context, resId: Int) { fun tintDrawable(drawable: Drawable?, context: Context, resId: Int) {
if (drawable != null) {
DrawableCompat.setTint(drawable, ContextCompat.getColor(context, resId)) DrawableCompat.setTint(drawable, ContextCompat.getColor(context, resId))
} }
}
} }
\ No newline at end of file
...@@ -2,35 +2,26 @@ package chat.rocket.android.widget.internal ...@@ -2,35 +2,26 @@ package chat.rocket.android.widget.internal
import android.annotation.TargetApi import android.annotation.TargetApi
import android.content.Context import android.content.Context
import android.content.res.TypedArray
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Build import android.os.Build
import android.support.annotation.ColorRes import android.support.annotation.ColorRes
import android.support.annotation.StringRes
import android.support.graphics.drawable.VectorDrawableCompat import android.support.graphics.drawable.VectorDrawableCompat
import android.util.AttributeSet import android.util.AttributeSet
import android.view.View import android.view.View
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import chat.rocket.android.widget.R import chat.rocket.android.widget.R
import chat.rocket.android.widget.helper.DrawableHelper import chat.rocket.android.widget.helper.DrawableHelper
import kotlinx.android.synthetic.main.room_list_item.view.*
/** /**
* Room list-item view used in sidebar. * Room list-item view used in sidebar.
*/ */
class RoomListItemView : FrameLayout { class RoomListItemView : FrameLayout {
lateinit private var roomId: String lateinit private var roomId: String
lateinit private var roomTypeImage: ImageView private val privateChannelDrawable: Drawable? = VectorDrawableCompat.create(resources, R.drawable.ic_lock_white_24dp, null)
lateinit private var userStatusImage: ImageView private val publicChannelDrawable: Drawable? = VectorDrawableCompat.create(resources, R.drawable.ic_hashtag_white_24dp, null)
lateinit private var roomNameText: TextView private val liveChatChannelDrawable: Drawable? = VectorDrawableCompat.create(resources, R.drawable.ic_livechat_white_24dp, null)
lateinit private var alertCountText: TextView private val userStatusDrawable: Drawable? = VectorDrawableCompat.create(resources, R.drawable.ic_user_status_black_24dp, null)?.mutate()
lateinit private var privateChannelDrawable: Drawable
lateinit private var publicChannelDrawable: Drawable
lateinit private var livechatChannelDrawable: Drawable
lateinit private var userStatusDrawable: Drawable
constructor(context: Context) : super(context) { constructor(context: Context) : super(context) {
initialize(context) initialize(context)
...@@ -50,7 +41,7 @@ class RoomListItemView : FrameLayout { ...@@ -50,7 +41,7 @@ class RoomListItemView : FrameLayout {
} }
private fun initialize(context: Context) { private fun initialize(context: Context) {
layoutParams = LinearLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT) layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT)
val array = context val array = context
.theme .theme
...@@ -60,16 +51,6 @@ class RoomListItemView : FrameLayout { ...@@ -60,16 +51,6 @@ class RoomListItemView : FrameLayout {
array.recycle() array.recycle()
View.inflate(context, R.layout.room_list_item, this) View.inflate(context, R.layout.room_list_item, this)
roomTypeImage = findViewById(R.id.image_room_type)
userStatusImage = findViewById(R.id.image_user_status)
roomNameText = findViewById(R.id.text_room_name)
alertCountText = findViewById(R.id.text_alert_count)
privateChannelDrawable = VectorDrawableCompat.create(resources, R.drawable.ic_lock_white_24dp, null)!!
publicChannelDrawable = VectorDrawableCompat.create(resources, R.drawable.ic_hashtag_white_24dp, null)!!
livechatChannelDrawable = VectorDrawableCompat.create(resources, R.drawable.ic_livechat_white_24dp, null)!!
userStatusDrawable = VectorDrawableCompat.create(resources, R.drawable.ic_user_status_black_24dp, null)!!
} }
fun setRoomId(roomId: String) { fun setRoomId(roomId: String) {
...@@ -78,10 +59,10 @@ class RoomListItemView : FrameLayout { ...@@ -78,10 +59,10 @@ class RoomListItemView : FrameLayout {
fun setUnreadCount(count: Int) { fun setUnreadCount(count: Int) {
if (count > 0) { if (count > 0) {
alertCountText.text = count.toString() alertCount.text = count.toString()
alertCountText.visibility = View.VISIBLE alertCount.visibility = View.VISIBLE
} else { } else {
alertCountText.visibility = View.GONE alertCount.visibility = View.GONE
} }
} }
...@@ -90,25 +71,25 @@ class RoomListItemView : FrameLayout { ...@@ -90,25 +71,25 @@ class RoomListItemView : FrameLayout {
} }
fun setRoomName(roomName: String) { fun setRoomName(roomName: String) {
roomNameText.text = roomName name.text = roomName
} }
fun showPrivateChannelIcon() { fun showPrivateChannelIcon() {
roomTypeImage.setImageDrawable(privateChannelDrawable) type.setImageDrawable(privateChannelDrawable)
userStatusImage.visibility = View.GONE userStatus.visibility = View.GONE
roomTypeImage.visibility = View.VISIBLE type.visibility = View.VISIBLE
} }
fun showPublicChannelIcon() { fun showPublicChannelIcon() {
roomTypeImage.setImageDrawable(publicChannelDrawable) type.setImageDrawable(publicChannelDrawable)
userStatusImage.visibility = View.GONE userStatus.visibility = View.GONE
roomTypeImage.visibility = View.VISIBLE type.visibility = View.VISIBLE
} }
fun showLivechatChannelIcon() { fun showLivechatChannelIcon() {
roomTypeImage.setImageDrawable(livechatChannelDrawable) type.setImageDrawable(liveChatChannelDrawable)
userStatusImage.visibility = View.GONE userStatus.visibility = View.GONE
roomTypeImage.visibility = View.VISIBLE type.visibility = View.VISIBLE
} }
fun showOnlineUserStatusIcon() { fun showOnlineUserStatusIcon() {
...@@ -130,8 +111,8 @@ class RoomListItemView : FrameLayout { ...@@ -130,8 +111,8 @@ class RoomListItemView : FrameLayout {
private fun prepareDrawableAndShow(@ColorRes resId: Int) { private fun prepareDrawableAndShow(@ColorRes resId: Int) {
DrawableHelper.wrapDrawable(userStatusDrawable) DrawableHelper.wrapDrawable(userStatusDrawable)
DrawableHelper.tintDrawable(userStatusDrawable, context, resId) DrawableHelper.tintDrawable(userStatusDrawable, context, resId)
userStatusImage.setImageDrawable(userStatusDrawable) userStatus.setImageDrawable(userStatusDrawable)
roomTypeImage.visibility = View.GONE type.visibility = View.GONE
userStatusImage.visibility = View.VISIBLE userStatus.visibility = View.VISIBLE
} }
} }
\ No newline at end of file
...@@ -18,6 +18,7 @@ import android.widget.ImageButton; ...@@ -18,6 +18,7 @@ import android.widget.ImageButton;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import chat.rocket.android.widget.R; import chat.rocket.android.widget.R;
import chat.rocket.android.widget.helper.DebouncingOnClickListener;
public class MessageFormLayout extends LinearLayout { public class MessageFormLayout extends LinearLayout {
...@@ -57,18 +58,18 @@ public class MessageFormLayout extends LinearLayout { ...@@ -57,18 +58,18 @@ public class MessageFormLayout extends LinearLayout {
attachButton = composer.findViewById(R.id.button_attach); attachButton = composer.findViewById(R.id.button_attach);
attachButton.setOnClickListener(new OnClickListener() { attachButton.setOnClickListener(new DebouncingOnClickListener() {
@Override @Override
public void onClick(View view) { public void doClick(View view) {
onExtraActionSelectionClick(); onExtraActionSelectionClick();
} }
}); });
sendButton = composer.findViewById(R.id.button_send); sendButton = composer.findViewById(R.id.button_send);
sendButton.setOnClickListener(new OnClickListener() { sendButton.setOnClickListener(new DebouncingOnClickListener() {
@Override @Override
public void onClick(View view) { public void doClick(View v) {
String messageText = getText(); String messageText = getText();
if (messageText.length() > 0 && submitTextListener != null) { if (messageText.length() > 0 && submitTextListener != null) {
submitTextListener.onSubmitText(messageText); submitTextListener.onSubmitText(messageText);
...@@ -163,8 +164,8 @@ public class MessageFormLayout extends LinearLayout { ...@@ -163,8 +164,8 @@ public class MessageFormLayout extends LinearLayout {
} }
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
getEditor().setEnabled(enabled); sendButton.setEnabled(enabled);
composer.findViewById(R.id.button_send).setEnabled(enabled); attachButton.setEnabled(enabled);
} }
public void setEditTextCommitContentListener( public void setEditTextCommitContentListener(
......
<?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
This diff is collapsed.
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