Commit d4d4cd46 authored by Yusuke Iwaki's avatar Yusuke Iwaki Committed by GitHub

Merge pull request #42 from RocketChat/fix_style_and_rules

Fix style and rules
parents d6e44491 3b4396da
...@@ -27,6 +27,10 @@ android { ...@@ -27,6 +27,10 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
lintOptions {
//avoiding okio error: https://github.com/square/okhttp/issues/896
lintConfig file("lint.xml")
}
} }
repositories { repositories {
......
<lint>
<issue id="InvalidPackage">
<ignore regexp="okio.*jar"/>
</issue>
</lint>
\ No newline at end of file
...@@ -5,23 +5,26 @@ ...@@ -5,23 +5,26 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application <application
android:name=".RocketChatApplication"
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme">
android:name=".RocketChatApplication">
<activity android:name=".activity.MainActivity" <activity
android:name=".activity.MainActivity"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LAUNCHER"/> <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".activity.ServerConfigActivity" <activity
android:name=".activity.ServerConfigActivity"
android:windowSoftInputMode="adjustResize"/> android:windowSoftInputMode="adjustResize"/>
<service android:name=".service.RocketChatService"/> <service android:name=".service.RocketChatService"/>
......
...@@ -2,14 +2,19 @@ package chat.rocket.android; ...@@ -2,14 +2,19 @@ package chat.rocket.android;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import chat.rocket.android.activity.ServerConfigActivity; import chat.rocket.android.activity.ServerConfigActivity;
/**
* utility class for launching Activity.
*/
public class LaunchUtil { public class LaunchUtil {
public static void showServerConfigActivity(Context context, String id) { /**
* launch ServerConfigActivity with proper flags.
*/
public static void showServerConfigActivity(Context context, String serverCondigId) {
Intent intent = new Intent(context, ServerConfigActivity.class); Intent intent = new Intent(context, ServerConfigActivity.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.putExtra("id", id); intent.putExtra("id", serverCondigId);
context.startActivity(intent); context.startActivity(intent);
} }
} }
package chat.rocket.android; package chat.rocket.android;
import android.app.Application; import android.app.Application;
import com.facebook.stetho.Stetho; import com.facebook.stetho.Stetho;
import com.uphyca.stetho_realm.RealmInspectorModulesProvider; import com.uphyca.stetho_realm.RealmInspectorModulesProvider;
import io.realm.Realm; import io.realm.Realm;
import io.realm.RealmConfiguration; import io.realm.RealmConfiguration;
import timber.log.Timber; import timber.log.Timber;
/**
* Customized Application-class for Rocket.Chat
*/
public class RocketChatApplication extends Application { public class RocketChatApplication extends Application {
@Override @Override public void onCreate() {
public void onCreate() {
super.onCreate(); super.onCreate();
Timber.plant(new Timber.DebugTree()); Timber.plant(new Timber.DebugTree());
Realm.init(this); Realm.init(this);
Realm.setDefaultConfiguration(new RealmConfiguration.Builder() Realm.setDefaultConfiguration(
.deleteRealmIfMigrationNeeded() new RealmConfiguration.Builder().deleteRealmIfMigrationNeeded().build());
.build());
Stetho.initialize( Stetho.initialize(Stetho.newInitializerBuilder(this)
Stetho.newInitializerBuilder(this)
.enableDumpapp(Stetho.defaultDumperPluginsProvider(this)) .enableDumpapp(Stetho.defaultDumperPluginsProvider(this))
.enableWebKitInspector(RealmInspectorModulesProvider.builder(this).build()) .enableWebKitInspector(RealmInspectorModulesProvider.builder(this).build())
.build()); .build());
......
package chat.rocket.android.activity; package chat.rocket.android.activity;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import java.util.List;
import java.util.UUID;
import chat.rocket.android.helper.LogcatIfError; import chat.rocket.android.helper.LogcatIfError;
import chat.rocket.android.model.ServerConfig; import chat.rocket.android.model.ServerConfig;
import chat.rocket.android.service.RocketChatService; import chat.rocket.android.service.RocketChatService;
import io.realm.Realm; import io.realm.Realm;
import io.realm.RealmResults; import io.realm.RealmResults;
import java.util.List;
import java.util.UUID;
import jp.co.crowdworks.realm_java_helpers.RealmListObserver; import jp.co.crowdworks.realm_java_helpers.RealmListObserver;
import jp.co.crowdworks.realm_java_helpers_bolts.RealmHelperBolts; import jp.co.crowdworks.realm_java_helpers_bolts.RealmHelperBolts;
abstract class AbstractAuthedActivity extends AppCompatActivity { abstract class AbstractAuthedActivity extends AppCompatActivity {
private RealmListObserver<ServerConfig> serverConfigEmptinessObserver =
private RealmListObserver<ServerConfig> mInsertEmptyRecordIfNoConfigurationExists = new RealmListObserver<ServerConfig>() { new RealmListObserver<ServerConfig>() {
@Override @Override protected RealmResults<ServerConfig> queryItems(Realm realm) {
protected RealmResults<ServerConfig> queryItems(Realm realm) {
return realm.where(ServerConfig.class).findAll(); return realm.where(ServerConfig.class).findAll();
} }
@Override @Override protected void onCollectionChanged(List<ServerConfig> list) {
protected void onCollectionChanged(List<ServerConfig> list) {
if (list.isEmpty()) { if (list.isEmpty()) {
final String newId = UUID.randomUUID().toString(); final String newId = UUID.randomUUID().toString();
RealmHelperBolts RealmHelperBolts.executeTransaction(
.executeTransaction(realm -> realm.createObject(ServerConfig.class, newId)) realm -> realm.createObject(ServerConfig.class, newId))
.continueWith(new LogcatIfError()); .continueWith(new LogcatIfError());
} }
} }
}; };
private RealmListObserver<ServerConfig> mShowConfigActivityIfNeeded = new RealmListObserver<ServerConfig>() { private RealmListObserver<ServerConfig> loginRequiredServerConfigObserver =
new RealmListObserver<ServerConfig>() {
@Override @Override protected RealmResults<ServerConfig> queryItems(Realm realm) {
protected RealmResults<ServerConfig> queryItems(Realm realm) {
return ServerConfig.queryLoginRequiredConnections(realm).findAll(); return ServerConfig.queryLoginRequiredConnections(realm).findAll();
} }
@Override @Override protected void onCollectionChanged(List<ServerConfig> list) {
protected void onCollectionChanged(List<ServerConfig> list) {
ServerConfigActivity.launchFor(AbstractAuthedActivity.this, list); ServerConfigActivity.launchFor(AbstractAuthedActivity.this, list);
} }
}; };
@Override @Override protected void onResume() {
protected void onResume() {
super.onResume(); super.onResume();
RocketChatService.keepalive(this); RocketChatService.keepalive(this);
mInsertEmptyRecordIfNoConfigurationExists.sub(); serverConfigEmptinessObserver.sub();
mShowConfigActivityIfNeeded.sub(); loginRequiredServerConfigObserver.sub();
} }
@Override @Override protected void onPause() {
protected void onPause() { loginRequiredServerConfigObserver.unsub();
mShowConfigActivityIfNeeded.unsub(); serverConfigEmptinessObserver.unsub();
mInsertEmptyRecordIfNoConfigurationExists.unsub();
super.onPause(); super.onPause();
} }
} }
...@@ -3,32 +3,33 @@ package chat.rocket.android.activity; ...@@ -3,32 +3,33 @@ package chat.rocket.android.activity;
import android.support.annotation.IdRes; import android.support.annotation.IdRes;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import chat.rocket.android.helper.OnBackPressListener; import chat.rocket.android.helper.OnBackPressListener;
abstract class AbstractFragmentActivity extends AppCompatActivity { abstract class AbstractFragmentActivity extends AppCompatActivity {
protected abstract @IdRes int getLayoutContainerForFragment(); protected abstract @IdRes int getLayoutContainerForFragment();
@Override @Override public void onBackPressed() {
public void onBackPressed(){ Fragment fragment =
Fragment f = getSupportFragmentManager().findFragmentById(getLayoutContainerForFragment()); getSupportFragmentManager().findFragmentById(getLayoutContainerForFragment());
if(f instanceof OnBackPressListener &&
((OnBackPressListener) f).onBackPressed()){ if (fragment instanceof OnBackPressListener
&& ((OnBackPressListener) fragment).onBackPressed()) {
//consumed. do nothing. //consumed. do nothing.
} else {
super.onBackPressed();
} }
else super.onBackPressed();
} }
protected void showFragment(Fragment f) { protected void showFragment(Fragment fragment) {
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.replace(getLayoutContainerForFragment(), f) .replace(getLayoutContainerForFragment(), fragment)
.commit(); .commit();
} }
protected void showFragmentWithBackStack(Fragment f) { protected void showFragmentWithBackStack(Fragment fragment) {
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.replace(getLayoutContainerForFragment(), f) .replace(getLayoutContainerForFragment(), fragment)
.addToBackStack(null) .addToBackStack(null)
.commit(); .commit();
} }
......
...@@ -2,21 +2,22 @@ package chat.rocket.android.activity; ...@@ -2,21 +2,22 @@ package chat.rocket.android.activity;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.helper.LogcatIfError; import chat.rocket.android.helper.LogcatIfError;
import chat.rocket.android.model.ServerConfig; import chat.rocket.android.model.ServerConfig;
import jp.co.crowdworks.realm_java_helpers_bolts.RealmHelperBolts; import jp.co.crowdworks.realm_java_helpers_bolts.RealmHelperBolts;
/**
* Entry-point for Rocket.Chat.Android application.
*/
public class MainActivity extends AbstractAuthedActivity { public class MainActivity extends AbstractAuthedActivity {
@Override @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
if (savedInstanceState==null) { if (savedInstanceState == null) {
RealmHelperBolts.executeTransaction(realm -> { RealmHelperBolts.executeTransaction(realm -> {
for(ServerConfig config: ServerConfig.queryActiveConnections(realm).findAll()) { for (ServerConfig config : ServerConfig.queryActiveConnections(realm).findAll()) {
config.setTokenVerified(false); config.setTokenVerified(false);
} }
return null; return null;
......
...@@ -5,71 +5,68 @@ import android.content.Intent; ...@@ -5,71 +5,68 @@ 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.v4.app.Fragment; import android.support.v4.app.Fragment;
import java.util.List;
import chat.rocket.android.LaunchUtil; import chat.rocket.android.LaunchUtil;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.fragment.server_config.ConnectingToHostFragment; import chat.rocket.android.fragment.server_config.ConnectingToHostFragment;
import chat.rocket.android.fragment.server_config.InputHostnameFragment; import chat.rocket.android.fragment.server_config.InputHostnameFragment;
import chat.rocket.android.helper.TextUtils; import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.model.ServerAuthProvider; import chat.rocket.android.model.MeteorLoginServiceConfiguration;
import chat.rocket.android.model.ServerConfig; import chat.rocket.android.model.ServerConfig;
import chat.rocket.android.service.RocketChatService; import chat.rocket.android.service.RocketChatService;
import io.realm.Realm; import io.realm.Realm;
import io.realm.RealmList; import io.realm.RealmList;
import io.realm.RealmQuery; import io.realm.RealmQuery;
import java.util.List;
import jp.co.crowdworks.realm_java_helpers.RealmObjectObserver; import jp.co.crowdworks.realm_java_helpers.RealmObjectObserver;
/**
* Activity for Login, Sign-up, and Connecting...
*/
public class ServerConfigActivity extends AbstractFragmentActivity { public class ServerConfigActivity extends AbstractFragmentActivity {
@Override private String serverConfigId;
protected int getLayoutContainerForFragment() { private RealmObjectObserver<ServerConfig> serverConfigObserver =
return R.id.content; new RealmObjectObserver<ServerConfig>() {
} @Override protected RealmQuery<ServerConfig> query(Realm realm) {
return realm.where(ServerConfig.class).equalTo("id", serverConfigId);
private String mServerConfigId;
private RealmObjectObserver<ServerConfig> mServerConfigObserver = new RealmObjectObserver<ServerConfig>() {
@Override
protected RealmQuery<ServerConfig> query(Realm realm) {
return realm.where(ServerConfig.class).equalTo("id", mServerConfigId);
} }
@Override @Override protected void onChange(ServerConfig config) {
protected void onChange(ServerConfig config) {
onRenderServerConfig(config); onRenderServerConfig(config);
} }
}; };
/**
* Start the ServerConfigActivity with considering the priority of ServerConfig in the list.
*/
public static boolean launchFor(Context context, List<ServerConfig> configList) { public static boolean launchFor(Context context, List<ServerConfig> configList) {
for (ServerConfig config: configList) { for (ServerConfig config : configList) {
if (TextUtils.isEmpty(config.getHostname())) { if (TextUtils.isEmpty(config.getHostname())) {
return launchFor(context, config); return launchFor(context, config);
} } else if (!TextUtils.isEmpty(config.getConnectionError())) {
else if (!TextUtils.isEmpty(config.getConnectionError())) {
return launchFor(context, config); return launchFor(context, config);
} }
} }
for (ServerConfig config: configList) { for (ServerConfig config : configList) {
if (config.getProviders().isEmpty()) { if (config.getAuthProviders().isEmpty()) {
return launchFor(context, config); return launchFor(context, config);
} }
} }
for (ServerConfig config: configList) { for (ServerConfig config : configList) {
if (TextUtils.isEmpty(config.getSelectedProviderName())) { if (TextUtils.isEmpty(config.getSelectedProviderName())) {
return launchFor(context, config); return launchFor(context, config);
} }
} }
for (ServerConfig config: configList) { for (ServerConfig config : configList) {
if (TextUtils.isEmpty(config.getToken())) { if (TextUtils.isEmpty(config.getToken())) {
return launchFor(context, config); return launchFor(context, config);
} }
} }
for (ServerConfig config: configList) { for (ServerConfig config : configList) {
if (!config.isTokenVerified()) { if (!config.isTokenVerified()) {
return launchFor(context, config); return launchFor(context, config);
} }
...@@ -83,19 +80,21 @@ public class ServerConfigActivity extends AbstractFragmentActivity { ...@@ -83,19 +80,21 @@ public class ServerConfigActivity extends AbstractFragmentActivity {
return true; return true;
} }
@Override protected int getLayoutContainerForFragment() {
return R.id.content;
}
@Override @Override protected void onCreate(@Nullable Bundle savedInstanceState) {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
Intent intent = getIntent(); Intent intent = getIntent();
if (intent==null || intent.getExtras()==null) { if (intent == null || intent.getExtras() == null) {
finish(); finish();
return; return;
} }
mServerConfigId = intent.getStringExtra("id"); serverConfigId = intent.getStringExtra("id");
if (TextUtils.isEmpty(mServerConfigId)) { if (TextUtils.isEmpty(serverConfigId)) {
finish(); finish();
return; return;
} }
...@@ -103,21 +102,19 @@ public class ServerConfigActivity extends AbstractFragmentActivity { ...@@ -103,21 +102,19 @@ public class ServerConfigActivity extends AbstractFragmentActivity {
setContentView(R.layout.simple_screen); setContentView(R.layout.simple_screen);
} }
@Override @Override protected void onResume() {
protected void onResume() {
super.onResume(); super.onResume();
RocketChatService.keepalive(this); RocketChatService.keepalive(this);
mServerConfigObserver.sub(); serverConfigObserver.sub();
} }
@Override @Override protected void onPause() {
protected void onPause() { serverConfigObserver.unsub();
mServerConfigObserver.unsub();
super.onPause(); super.onPause();
} }
private void onRenderServerConfig(ServerConfig config) { private void onRenderServerConfig(ServerConfig config) {
if (config==null) { if (config == null) {
finish(); finish();
return; return;
} }
...@@ -138,7 +135,7 @@ public class ServerConfigActivity extends AbstractFragmentActivity { ...@@ -138,7 +135,7 @@ public class ServerConfigActivity extends AbstractFragmentActivity {
return; return;
} }
RealmList<ServerAuthProvider> providers = config.getProviders(); RealmList<MeteorLoginServiceConfiguration> providers = config.getAuthProviders();
if (!providers.isEmpty()) { if (!providers.isEmpty()) {
return; return;
...@@ -154,31 +151,29 @@ public class ServerConfigActivity extends AbstractFragmentActivity { ...@@ -154,31 +151,29 @@ public class ServerConfigActivity extends AbstractFragmentActivity {
showFragment(new InputHostnameFragment()); showFragment(new InputHostnameFragment());
} }
@Override @Override protected void showFragment(Fragment fragment) {
protected void showFragment(Fragment f) { injectServerConfigIdArgTo(fragment);
injectIdArgTo(f); super.showFragment(fragment);
super.showFragment(f);
} }
@Override @Override protected void showFragmentWithBackStack(Fragment fragment) {
protected void showFragmentWithBackStack(Fragment f) { injectServerConfigIdArgTo(fragment);
injectIdArgTo(f); super.showFragmentWithBackStack(fragment);
super.showFragmentWithBackStack(f);
} }
private void injectIdArgTo(Fragment f) { private void injectServerConfigIdArgTo(Fragment fragment) {
Bundle args = f.getArguments(); Bundle args = fragment.getArguments();
if(args==null) args = new Bundle(); if (args == null) {
args.putString("id", mServerConfigId); args = new Bundle();
f.setArguments(args); }
args.putString("id", serverConfigId);
fragment.setArguments(args);
} }
@Override @Override public void onBackPressed() {
public void onBackPressed() {
if (ServerConfig.hasActiveConnection()) { if (ServerConfig.hasActiveConnection()) {
super.onBackPressed(); super.onBackPressed();
} } else {
else {
moveTaskToBack(true); moveTaskToBack(true);
} }
} }
......
...@@ -8,23 +8,29 @@ import android.view.LayoutInflater; ...@@ -8,23 +8,29 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
/**
* Fragment base class for this Application.
*/
public abstract class AbstractFragment extends Fragment { public abstract class AbstractFragment extends Fragment {
protected View mRootView; protected View rootView;
protected abstract @LayoutRes int getLayout(); protected abstract @LayoutRes int getLayout();
protected abstract void onSetupView(); protected abstract void onSetupView();
@Nullable @Nullable
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @Override
mRootView = inflater.inflate(getLayout(), container,false); public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
rootView = inflater.inflate(getLayout(), container, false);
onSetupView(); onSetupView();
return mRootView; return rootView;
} }
protected void finish() { protected void finish() {
if(getFragmentManager().getBackStackEntryCount()==0){ if (getFragmentManager().getBackStackEntryCount() == 0) {
getActivity().finish(); getActivity().finish();
} } else {
else {
getFragmentManager().popBackStack(); getFragmentManager().popBackStack();
} }
} }
......
...@@ -2,24 +2,23 @@ package chat.rocket.android.fragment.server_config; ...@@ -2,24 +2,23 @@ package chat.rocket.android.fragment.server_config;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import chat.rocket.android.fragment.AbstractFragment; import chat.rocket.android.fragment.AbstractFragment;
import chat.rocket.android.helper.TextUtils; import chat.rocket.android.helper.TextUtils;
abstract class AbstractServerConfigFragment extends AbstractFragment { abstract class AbstractServerConfigFragment extends AbstractFragment {
protected String mServerConfigId; protected String serverConfigId;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) { @Override public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
Bundle args = getArguments(); Bundle args = getArguments();
if (args==null) { if (args == null) {
finish(); finish();
return; return;
} }
mServerConfigId = args.getString("id"); serverConfigId = args.getString("id");
if (TextUtils.isEmpty(mServerConfigId)) { if (TextUtils.isEmpty(serverConfigId)) {
finish(); finish();
return; return;
} }
......
...@@ -2,14 +2,15 @@ package chat.rocket.android.fragment.server_config; ...@@ -2,14 +2,15 @@ package chat.rocket.android.fragment.server_config;
import chat.rocket.android.R; import chat.rocket.android.R;
/**
* Just showing "connecting..." screen.
*/
public class ConnectingToHostFragment extends AbstractServerConfigFragment { public class ConnectingToHostFragment extends AbstractServerConfigFragment {
@Override @Override protected int getLayout() {
protected int getLayout() {
return R.layout.fragment_wait_for_connection; return R.layout.fragment_wait_for_connection;
} }
@Override @Override protected void onSetupView() {
protected void onSetupView() {
} }
} }
...@@ -2,12 +2,8 @@ package chat.rocket.android.fragment.server_config; ...@@ -2,12 +2,8 @@ package chat.rocket.android.fragment.server_config;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import android.view.View;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import org.json.JSONObject;
import chat.rocket.android.R; import chat.rocket.android.R;
import chat.rocket.android.helper.LogcatIfError; import chat.rocket.android.helper.LogcatIfError;
import chat.rocket.android.helper.TextUtils; import chat.rocket.android.helper.TextUtils;
...@@ -16,73 +12,75 @@ import io.realm.Realm; ...@@ -16,73 +12,75 @@ import io.realm.Realm;
import io.realm.RealmQuery; import io.realm.RealmQuery;
import jp.co.crowdworks.realm_java_helpers.RealmObjectObserver; import jp.co.crowdworks.realm_java_helpers.RealmObjectObserver;
import jp.co.crowdworks.realm_java_helpers_bolts.RealmHelperBolts; import jp.co.crowdworks.realm_java_helpers_bolts.RealmHelperBolts;
import org.json.JSONObject;
/**
* Input server host.
*/
public class InputHostnameFragment extends AbstractServerConfigFragment { public class InputHostnameFragment extends AbstractServerConfigFragment {
public InputHostnameFragment(){} private Handler errorShowingHandler = new Handler() {
@Override public void handleMessage(Message msg) {
@Override Toast.makeText(rootView.getContext(), (String) msg.obj, Toast.LENGTH_SHORT).show();
protected int getLayout() {
return R.layout.fragment_input_hostname;
} }
};
RealmObjectObserver<ServerConfig> mObserver = new RealmObjectObserver<ServerConfig>() { RealmObjectObserver<ServerConfig> serverConfigObserver = new RealmObjectObserver<ServerConfig>() {
@Override @Override protected RealmQuery<ServerConfig> query(Realm realm) {
protected RealmQuery<ServerConfig> query(Realm realm) { return realm.where(ServerConfig.class).equalTo("id", serverConfigId);
return realm.where(ServerConfig.class).equalTo("id", mServerConfigId);
} }
@Override @Override protected void onChange(ServerConfig config) {
protected void onChange(ServerConfig config) {
onRenderServerConfig(config); onRenderServerConfig(config);
} }
}; };
@Override public InputHostnameFragment() {
protected void onSetupView() { }
final TextView editor = (TextView) mRootView.findViewById(R.id.editor_hostname);
final View btnConnect = mRootView.findViewById(R.id.btn_connect);
btnConnect.setOnClickListener(v -> { @Override protected int getLayout() {
final String hostname = TextUtils.or(TextUtils.or(editor.getText(), editor.getHint()), "").toString(); return R.layout.fragment_input_hostname;
RealmHelperBolts }
.executeTransaction(realm -> realm.createOrUpdateObjectFromJson(ServerConfig.class, new JSONObject()
.put("id", mServerConfigId)
.put("hostname", hostname)
.put("connectionError", JSONObject.NULL)))
.continueWith(new LogcatIfError());
});
mObserver.sub(); @Override protected void onSetupView() {
rootView.findViewById(R.id.btn_connect).setOnClickListener(view -> handleConnect());
serverConfigObserver.sub();
} }
@Override private void handleConnect() {
public void onResume() { final TextView editor = (TextView) rootView.findViewById(R.id.editor_hostname);
super.onResume();
mObserver.keepalive(); final String hostname =
TextUtils.or(TextUtils.or(editor.getText(), editor.getHint()), "").toString();
RealmHelperBolts.executeTransaction(
realm -> realm.createOrUpdateObjectFromJson(ServerConfig.class,
new JSONObject().put("id", serverConfigId)
.put("hostname", hostname)
.put("connectionError", JSONObject.NULL))).continueWith(new LogcatIfError());
} }
@Override @Override public void onResume() {
public void onDestroyView() { super.onResume();
mObserver.unsub(); serverConfigObserver.keepalive();
super.onDestroyView();
} }
private Handler mShowError = new Handler() { @Override public void onDestroyView() {
@Override serverConfigObserver.unsub();
public void handleMessage(Message msg) { super.onDestroyView();
Toast.makeText(mRootView.getContext(), (String) msg.obj, Toast.LENGTH_SHORT).show();
} }
};
private void showError(String errString) { private void showError(String errString) {
mShowError.removeMessages(0); errorShowingHandler.removeMessages(0);
Message m = Message.obtain(mShowError, 0, errString); Message msg = Message.obtain(errorShowingHandler, 0, errString);
mShowError.sendMessageDelayed(m, 160); errorShowingHandler.sendMessageDelayed(msg, 160);
} }
private void onRenderServerConfig(ServerConfig config) { private void onRenderServerConfig(ServerConfig config) {
final TextView editor = (TextView) mRootView.findViewById(R.id.editor_hostname); final TextView editor = (TextView) rootView.findViewById(R.id.editor_hostname);
if (!TextUtils.isEmpty(config.getHostname())) editor.setText(config.getHostname());
if (!TextUtils.isEmpty(config.getHostname())) {
editor.setText(config.getHostname());
}
if (!TextUtils.isEmpty(config.getConnectionError())) { if (!TextUtils.isEmpty(config.getConnectionError())) {
clearConnectionErrorAndHostname(); clearConnectionErrorAndHostname();
showError(config.getConnectionError()); showError(config.getConnectionError());
...@@ -90,11 +88,10 @@ public class InputHostnameFragment extends AbstractServerConfigFragment { ...@@ -90,11 +88,10 @@ public class InputHostnameFragment extends AbstractServerConfigFragment {
} }
private void clearConnectionErrorAndHostname() { private void clearConnectionErrorAndHostname() {
RealmHelperBolts RealmHelperBolts.executeTransaction(
.executeTransaction(realm -> realm.createOrUpdateObjectFromJson(ServerConfig.class, new JSONObject() realm -> realm.createOrUpdateObjectFromJson(ServerConfig.class,
.put("id", mServerConfigId) new JSONObject().put("id", serverConfigId)
.put("hostname", JSONObject.NULL) .put("hostname", JSONObject.NULL)
.put("connectionError", JSONObject.NULL))) .put("connectionError", JSONObject.NULL))).continueWith(new LogcatIfError());
.continueWith(new LogcatIfError());
} }
} }
...@@ -4,9 +4,11 @@ import bolts.Continuation; ...@@ -4,9 +4,11 @@ import bolts.Continuation;
import bolts.Task; import bolts.Task;
import timber.log.Timber; import timber.log.Timber;
/**
* Bolts-Task continuation for just logging if error occurred.
*/
public class LogcatIfError implements Continuation { public class LogcatIfError implements Continuation {
@Override @Override public Object then(Task task) throws Exception {
public Object then(Task task) throws Exception {
if (task.isFaulted()) { if (task.isFaulted()) {
Timber.w(task.getError()); Timber.w(task.getError());
} }
......
package chat.rocket.android.helper; package chat.rocket.android.helper;
import com.facebook.stetho.okhttp3.StethoInterceptor; import com.facebook.stetho.okhttp3.StethoInterceptor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
/**
* Helper class for OkHttp client.
*/
public class OkHttpHelper { public class OkHttpHelper {
private static OkHttpClient sHttpClientForWS; private static OkHttpClient sHttpClientForWS;
public static OkHttpClient getClientForWebSocket() {
if (sHttpClientForWS==null) { /**
sHttpClientForWS = new OkHttpClient.Builder() * acquire OkHttpClient instance for WebSocket connection.
.readTimeout(0, TimeUnit.NANOSECONDS) */
public static OkHttpClient getClientForWebSocket() {
if (sHttpClientForWS == null) {
sHttpClientForWS = new OkHttpClient.Builder().readTimeout(0, TimeUnit.NANOSECONDS)
.addNetworkInterceptor(new StethoInterceptor()) .addNetworkInterceptor(new StethoInterceptor())
.build(); .build();
} }
......
package chat.rocket.android.helper; package chat.rocket.android.helper;
/**
* Interface that just have onBackPressed().
*/
public interface OnBackPressListener { public interface OnBackPressListener {
/**
* onBackPressed
*
* @return whether back is handled or not.
*/
boolean onBackPressed(); boolean onBackPressed();
} }
package chat.rocket.android.helper; package chat.rocket.android.helper;
/**
* Text Utility class like android.text.TextUtils.
*/
public class TextUtils { public class TextUtils {
/**
* Returns true if the string is null or 0-length.
*
* @param str the string to be examined
* @return true if str is null or zero length
*/
public static boolean isEmpty(CharSequence str) { public static boolean isEmpty(CharSequence str) {
// same definition as android.text.TextUtils#isEmpty(). // same definition as android.text.TextUtils#isEmpty().
return str == null || str.length() == 0; return str == null || str.length() == 0;
} }
public static CharSequence or(CharSequence str, CharSequence defaultValue) { /**
if (isEmpty(str)) return defaultValue; * Returns str if it is not empty; otherwise defaultValue is returned.
*/
@SuppressWarnings("PMD.ShortMethodName")
public static CharSequence or(CharSequence str,
CharSequence defaultValue) {
if (isEmpty(str)) {
return defaultValue;
}
return str; return str;
} }
} }
package chat.rocket.android.model;
import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;
/**
* subscription model for "meteor_accounts_loginServiceConfiguration".
*/
@SuppressWarnings("PMD.ShortVariable")
public class MeteorLoginServiceConfiguration
extends RealmObject {
@PrimaryKey private String id;
private String service;
private String consumerKey; //for Twitter
private String appId; //for Facebook
private String clientId; //for other auth providers
}
package chat.rocket.android.model;
import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;
public class ServerAuthProvider extends RealmObject {
@PrimaryKey
private String name; //email, twitter, github, ...
}
package chat.rocket.android.model; package chat.rocket.android.model;
import org.json.JSONObject;
import chat.rocket.android.helper.LogcatIfError; import chat.rocket.android.helper.LogcatIfError;
import hugo.weaving.DebugLog; import hugo.weaving.DebugLog;
import io.realm.Realm; import io.realm.Realm;
...@@ -11,17 +9,43 @@ import io.realm.RealmQuery; ...@@ -11,17 +9,43 @@ import io.realm.RealmQuery;
import io.realm.annotations.PrimaryKey; import io.realm.annotations.PrimaryKey;
import jp.co.crowdworks.realm_java_helpers.RealmHelper; import jp.co.crowdworks.realm_java_helpers.RealmHelper;
import jp.co.crowdworks.realm_java_helpers_bolts.RealmHelperBolts; import jp.co.crowdworks.realm_java_helpers_bolts.RealmHelperBolts;
import org.json.JSONObject;
/**
* Server configuration.
*/
@SuppressWarnings("PMD.ShortVariable")
public class ServerConfig extends RealmObject { public class ServerConfig extends RealmObject {
@PrimaryKey @PrimaryKey private String id;
private String id;
private String hostname; private String hostname;
private String connectionError; private String connectionError;
private String token; private String token;
private boolean tokenVerified; private boolean tokenVerified;
private RealmList<ServerAuthProvider> providers; private RealmList<MeteorLoginServiceConfiguration> authProviders;
private String selectedProviderName; private String selectedProviderName;
public static RealmQuery<ServerConfig> queryLoginRequiredConnections(Realm realm) {
return realm.where(ServerConfig.class).equalTo("tokenVerified", false);
}
public static RealmQuery<ServerConfig> queryActiveConnections(Realm realm) {
return realm.where(ServerConfig.class).isNotNull("token");
}
public static boolean hasActiveConnection() {
ServerConfig config =
RealmHelper.executeTransactionForRead(realm -> queryActiveConnections(realm).findFirst());
return config != null;
}
@DebugLog public static void logError(String id, Exception exception) {
RealmHelperBolts.executeTransaction(
realm -> realm.createOrUpdateObjectFromJson(ServerConfig.class,
new JSONObject().put("id", id).put("connectionError", exception.getMessage())))
.continueWith(new LogcatIfError());
}
public String getId() { public String getId() {
return id; return id;
} }
...@@ -62,12 +86,12 @@ public class ServerConfig extends RealmObject { ...@@ -62,12 +86,12 @@ public class ServerConfig extends RealmObject {
this.tokenVerified = tokenVerified; this.tokenVerified = tokenVerified;
} }
public RealmList<ServerAuthProvider> getProviders() { public RealmList<MeteorLoginServiceConfiguration> getAuthProviders() {
return providers; return authProviders;
} }
public void setProviders(RealmList<ServerAuthProvider> providers) { public void setAuthProviders(RealmList<MeteorLoginServiceConfiguration> authProviders) {
this.providers = providers; this.authProviders = authProviders;
} }
public String getSelectedProviderName() { public String getSelectedProviderName() {
...@@ -77,30 +101,4 @@ public class ServerConfig extends RealmObject { ...@@ -77,30 +101,4 @@ public class ServerConfig extends RealmObject {
public void setSelectedProviderName(String selectedProviderName) { public void setSelectedProviderName(String selectedProviderName) {
this.selectedProviderName = selectedProviderName; this.selectedProviderName = selectedProviderName;
} }
public static RealmQuery<ServerConfig> queryLoginRequiredConnections(Realm realm) {
return realm.where(ServerConfig.class)
.equalTo("tokenVerified", false);
}
public static RealmQuery<ServerConfig> queryActiveConnections(Realm realm) {
return realm.where(ServerConfig.class)
.isNotNull("token");
}
public static boolean hasActiveConnection() {
ServerConfig config = RealmHelper.executeTransactionForRead(realm ->
queryActiveConnections(realm).findFirst());
return config != null;
}
@DebugLog
public static void logError(String id, Exception e) {
RealmHelperBolts
.executeTransaction(realm -> realm.createOrUpdateObjectFromJson(ServerConfig.class, new JSONObject()
.put("id", id)
.put("connectionError", e.getMessage())))
.continueWith(new LogcatIfError());
}
} }
package chat.rocket.android.model.doc;
import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;
public class MeteorLoginServiceConfiguration extends RealmObject {
@PrimaryKey
private String id;
private String service;
private String consumerKey; //for Twitter
private String appId;//for Facebook
private String clientId;//for other auth providers
}
package chat.rocket.android.service; package chat.rocket.android.service;
/**
* interface for observer and ddp_subscription.
*/
public interface Registerable { public interface Registerable {
/**
* register.
*/
void register(); void register();
/**
* keepalive.
*/
void keepalive(); void keepalive();
/**
* unregister.
*/
void unregister(); void unregister();
} }
...@@ -5,75 +5,81 @@ import android.content.Context; ...@@ -5,75 +5,81 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.IBinder; import android.os.IBinder;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import bolts.Task; import bolts.Task;
import chat.rocket.android.model.ServerConfig; import chat.rocket.android.model.ServerConfig;
import io.realm.Realm; import io.realm.Realm;
import io.realm.RealmResults; import io.realm.RealmResults;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import jp.co.crowdworks.realm_java_helpers.RealmListObserver; import jp.co.crowdworks.realm_java_helpers.RealmListObserver;
/**
* Background service for Rocket.Chat.Application class.
*/
public class RocketChatService extends Service { public class RocketChatService extends Service {
public static void keepalive(Context context) { private HashMap<String, RocketChatWebSocketThread> webSocketThreads;
context.startService(new Intent(context, RocketChatService.class)); private RealmListObserver<ServerConfig> connectionRequiredServerConfigObserver =
} new RealmListObserver<ServerConfig>() {
@Override protected RealmResults<ServerConfig> queryItems(Realm realm) {
public static void kill(Context context) {
context.stopService(new Intent(context, RocketChatService.class));
}
private HashMap<String, RocketChatWebSocketThread> mWebSocketThreads;
private RealmListObserver<ServerConfig> mConnectionRequiredServerConfigObserver = new RealmListObserver<ServerConfig>() {
@Override
protected RealmResults<ServerConfig> queryItems(Realm realm) {
return realm.where(ServerConfig.class) return realm.where(ServerConfig.class)
.isNotNull("hostname") .isNotNull("hostname")
.isNull("connectionError") .isNull("connectionError")
.findAll(); .findAll();
} }
@Override @Override protected void onCollectionChanged(List<ServerConfig> list) {
protected void onCollectionChanged(List<ServerConfig> list) {
syncWebSocketThreadsWith(list); syncWebSocketThreadsWith(list);
} }
}; };
@Override /**
public void onCreate() { * ensure RocketChatService alive.
*/
public static void keepalive(Context context) {
context.startService(new Intent(context, RocketChatService.class));
}
/**
* force stop RocketChatService.
*/
public static void kill(Context context) {
context.stopService(new Intent(context, RocketChatService.class));
}
@Override public void onCreate() {
super.onCreate(); super.onCreate();
mWebSocketThreads = new HashMap<>(); webSocketThreads = new HashMap<>();
} }
@Override @Override public int onStartCommand(Intent intent, int flags, int startId) {
public int onStartCommand(Intent intent, int flags, int startId) { connectionRequiredServerConfigObserver.keepalive();
mConnectionRequiredServerConfigObserver.keepalive();
return START_STICKY; return START_STICKY;
} }
private void syncWebSocketThreadsWith(List<ServerConfig> configList) { private void syncWebSocketThreadsWith(List<ServerConfig> configList) {
final Iterator<Map.Entry<String, RocketChatWebSocketThread>> it = mWebSocketThreads.entrySet().iterator(); final Iterator<Map.Entry<String, RocketChatWebSocketThread>> iterator =
while(it.hasNext()) { webSocketThreads.entrySet().iterator();
Map.Entry<String, RocketChatWebSocketThread> e = it.next();
String id = e.getKey(); while (iterator.hasNext()) {
Map.Entry<String, RocketChatWebSocketThread> entry = iterator.next();
String serverConfigId = entry.getKey();
boolean found = false; boolean found = false;
for(ServerConfig config: configList) { for (ServerConfig config : configList) {
if (id.equals(config.getId())) { if (serverConfigId.equals(config.getId())) {
found = true; found = true;
break; break;
} }
} }
if (!found) { if (!found) {
RocketChatWebSocketThread.terminate(e.getValue()); RocketChatWebSocketThread.terminate(entry.getValue());
it.remove(); iterator.remove();
} }
} }
for(ServerConfig config: configList) { for (ServerConfig config : configList) {
findOrCreateWebSocketThread(config).onSuccess(task -> { findOrCreateWebSocketThread(config).onSuccess(task -> {
RocketChatWebSocketThread thread = task.getResult(); RocketChatWebSocketThread thread = task.getResult();
thread.syncStateWith(config); thread.syncStateWith(config);
...@@ -83,13 +89,13 @@ public class RocketChatService extends Service { ...@@ -83,13 +89,13 @@ public class RocketChatService extends Service {
} }
private Task<RocketChatWebSocketThread> findOrCreateWebSocketThread(final ServerConfig config) { private Task<RocketChatWebSocketThread> findOrCreateWebSocketThread(final ServerConfig config) {
final String id = config.getId(); final String serverConfigId = config.getId();
if (mWebSocketThreads.containsKey(id)) { if (webSocketThreads.containsKey(serverConfigId)) {
return Task.forResult(mWebSocketThreads.get(id)); return Task.forResult(webSocketThreads.get(serverConfigId));
} } else {
else { return RocketChatWebSocketThread.getStarted(getApplicationContext(), config)
return RocketChatWebSocketThread.getStarted(getApplicationContext(), config).onSuccessTask(task -> { .onSuccessTask(task -> {
mWebSocketThreads.put(id, task.getResult()); webSocketThreads.put(serverConfigId, task.getResult());
return task; return task;
}); });
} }
......
...@@ -3,98 +3,101 @@ package chat.rocket.android.service; ...@@ -3,98 +3,101 @@ package chat.rocket.android.service;
import android.content.Context; import android.content.Context;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Iterator;
import bolts.Task; import bolts.Task;
import bolts.TaskCompletionSource; import bolts.TaskCompletionSource;
import chat.rocket.android.helper.TextUtils; import chat.rocket.android.helper.TextUtils;
import chat.rocket.android.model.ServerConfig; import chat.rocket.android.model.ServerConfig;
import chat.rocket.android.service.ddp_subscription.LoginServiceConfigurationSubscriber; import chat.rocket.android.service.ddp_subscriber.LoginServiceConfigurationSubscriber;
import chat.rocket.android.ws.RocketChatWebSocketAPI; import chat.rocket.android.ws.RocketChatWebSocketAPI;
import chat.rocket.android_ddp.DDPClient; import chat.rocket.android_ddp.DDPClient;
import hugo.weaving.DebugLog; import hugo.weaving.DebugLog;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Iterator;
import jp.co.crowdworks.realm_java_helpers.RealmHelper; import jp.co.crowdworks.realm_java_helpers.RealmHelper;
import timber.log.Timber; import timber.log.Timber;
import static android.content.ContentValues.TAG; /**
* Thread for handling WebSocket connection.
*/
public class RocketChatWebSocketThread extends HandlerThread { public class RocketChatWebSocketThread extends HandlerThread {
private final Context mAppContext; private static final Class[] REGISTERABLE_CLASSES = {
private final String mServerConfigId; LoginServiceConfigurationSubscriber.class
private RocketChatWebSocketAPI mWebSocketAPI; };
private boolean mSocketExists; private final Context appContext;
private boolean mListenersRegistered; private final String serverConfigId;
private final ArrayList<Registerable> listeners = new ArrayList<>();
private RocketChatWebSocketThread(Context appContext, String id) { private RocketChatWebSocketAPI webSocketAPI;
super("RC_thread_"+id); private boolean socketExists;
mServerConfigId = id; private boolean listenersRegistered;
mAppContext = appContext;
} private RocketChatWebSocketThread(Context appContext, String serverConfigId) {
super("RC_thread_" + serverConfigId);
@DebugLog this.serverConfigId = serverConfigId;
public static Task<RocketChatWebSocketThread> getStarted(Context appContext, ServerConfig config) { this.appContext = appContext;
}
/**
* create new Thread.
*/
@DebugLog public static Task<RocketChatWebSocketThread> getStarted(Context appContext,
ServerConfig config) {
TaskCompletionSource<RocketChatWebSocketThread> task = new TaskCompletionSource<>(); TaskCompletionSource<RocketChatWebSocketThread> task = new TaskCompletionSource<>();
new RocketChatWebSocketThread(appContext, config.getId()){ new RocketChatWebSocketThread(appContext, config.getId()) {
@Override @Override protected void onLooperPrepared() {
protected void onLooperPrepared() {
try { try {
super.onLooperPrepared(); super.onLooperPrepared();
task.setResult(this); task.setResult(this);
} } catch (Exception exception) {
catch (Exception e) { task.setError(exception);
task.setError(e);
} }
} }
}.start(); }.start();
return task.getTask(); return task.getTask();
} }
@DebugLog /**
public static void terminate(RocketChatWebSocketThread t) { * terminate the thread.
t.quit(); */
@DebugLog public static void terminate(RocketChatWebSocketThread thread) {
thread.quit();
} }
private Task<Void> ensureConnection() { private Task<Void> ensureConnection() {
if (mWebSocketAPI == null || !mWebSocketAPI.isConnected()) { if (webSocketAPI == null || !webSocketAPI.isConnected()) {
return registerListeners(); return registerListeners();
} else {
return Task.forResult(null);
} }
else return Task.forResult(null);
} }
@DebugLog /**
public void syncStateWith(ServerConfig config) { * synchronize the state of the thread with ServerConfig.
if (config == null || TextUtils.isEmpty(config.getHostname()) || !TextUtils.isEmpty(config.getConnectionError())) { */
@DebugLog public void syncStateWith(ServerConfig config) {
if (config == null || TextUtils.isEmpty(config.getHostname()) || !TextUtils.isEmpty(
config.getConnectionError())) {
quit(); quit();
} } else {
else { ensureConnection().continueWith(task -> {
ensureConnection() new Handler(getLooper()).post(this::keepaliveListeners);
.continueWith(task -> {
new Handler(getLooper()).post(() -> {
keepaliveListeners();
});
return null; return null;
}); });
} }
} }
@Override @Override protected void onLooperPrepared() {
protected void onLooperPrepared() {
super.onLooperPrepared(); super.onLooperPrepared();
registerListeners(); registerListeners();
} }
@Override @Override public boolean quit() {
public boolean quit() {
scheduleUnregisterListeners(); scheduleUnregisterListeners();
return super.quit(); return super.quit();
} }
@Override @Override public boolean quitSafely() {
public boolean quitSafely() {
scheduleUnregisterListeners(); scheduleUnregisterListeners();
return super.quitSafely(); return super.quitSafely();
} }
...@@ -108,26 +111,23 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -108,26 +111,23 @@ public class RocketChatWebSocketThread extends HandlerThread {
} }
} }
private static final Class[] REGISTERABLE_CLASSES = {
LoginServiceConfigurationSubscriber.class
};
private final ArrayList<Registerable> mListeners = new ArrayList<>();
private void prepareWebSocket() { private void prepareWebSocket() {
ServerConfig config = RealmHelper.executeTransactionForRead(realm -> realm.where(ServerConfig.class).equalTo("id", mServerConfigId).findFirst()); ServerConfig config = RealmHelper.executeTransactionForRead(
if (mWebSocketAPI == null || !mWebSocketAPI.isConnected()) { realm -> realm.where(ServerConfig.class).equalTo("id", serverConfigId).findFirst());
mWebSocketAPI = RocketChatWebSocketAPI.create(config.getHostname());
if (webSocketAPI == null || !webSocketAPI.isConnected()) {
webSocketAPI = RocketChatWebSocketAPI.create(config.getHostname());
} }
} }
@DebugLog @DebugLog private Task<Void> registerListeners() {
private Task<Void> registerListeners(){ if (socketExists) {
if (mSocketExists) return Task.forResult(null); return Task.forResult(null);
}
mSocketExists = true; socketExists = true;
prepareWebSocket(); prepareWebSocket();
return mWebSocketAPI.connect().onSuccess(task -> { return webSocketAPI.connect().onSuccess(task -> {
registerListenersActually(); registerListenersActually();
DDPClient client = task.getResult().client; DDPClient client = task.getResult().client;
...@@ -140,13 +140,13 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -140,13 +140,13 @@ public class RocketChatWebSocketThread extends HandlerThread {
// just for debugging. // just for debugging.
client.getSubscriptionCallback().subscribe(event -> { client.getSubscriptionCallback().subscribe(event -> {
Timber.d(TAG, "Callback [DEBUG] < " + event); Timber.d("Callback [DEBUG] < " + event);
}); });
return null; return null;
}).continueWith(task -> { }).continueWith(task -> {
if (task.isFaulted()) { if (task.isFaulted()) {
ServerConfig.logError(mServerConfigId, task.getError()); ServerConfig.logError(serverConfigId, task.getError());
} }
return null; return null;
}); });
...@@ -154,48 +154,55 @@ public class RocketChatWebSocketThread extends HandlerThread { ...@@ -154,48 +154,55 @@ public class RocketChatWebSocketThread extends HandlerThread {
//@DebugLog //@DebugLog
private void registerListenersActually() { private void registerListenersActually() {
if (mListenersRegistered) return; if (listenersRegistered) {
mListenersRegistered = true; return;
}
listenersRegistered = true;
for(Class clazz: REGISTERABLE_CLASSES){ for (Class clazz : REGISTERABLE_CLASSES) {
try { try {
Constructor ctor = clazz.getConstructor(Context.class, RocketChatWebSocketAPI.class); Constructor ctor = clazz.getConstructor(Context.class, RocketChatWebSocketAPI.class);
Object obj = ctor.newInstance(mAppContext, mWebSocketAPI); Object obj = ctor.newInstance(appContext, webSocketAPI);
if(obj instanceof Registerable) { if (obj instanceof Registerable) {
Registerable l = (Registerable) obj; Registerable registerable = (Registerable) obj;
l.register(); registerable.register();
mListeners.add(l); listeners.add(registerable);
} }
} catch (Exception e) { } catch (Exception exception) {
Timber.w(e); Timber.w(exception, "Failed to register listeners!!");
} }
} }
} }
//@DebugLog //@DebugLog
private void keepaliveListeners(){ private void keepaliveListeners() {
if (!mSocketExists || !mListenersRegistered) return; if (!socketExists || !listenersRegistered) {
return;
}
for (Registerable l : mListeners) l.keepalive(); for (Registerable registerable : listeners) {
registerable.keepalive();
}
} }
//@DebugLog //@DebugLog
private void unregisterListeners(){ private void unregisterListeners() {
if (!mSocketExists || !mListenersRegistered) return; if (!socketExists || !listenersRegistered) {
return;
}
Iterator<Registerable> it = mListeners.iterator(); Iterator<Registerable> iterator = listeners.iterator();
while(it.hasNext()){ while (iterator.hasNext()) {
Registerable l = it.next(); Registerable registerable = iterator.next();
l.unregister(); registerable.unregister();
it.remove(); iterator.remove();
} }
if (mWebSocketAPI != null) { if (webSocketAPI != null) {
mWebSocketAPI.close(); webSocketAPI.close();
mWebSocketAPI = null; webSocketAPI = null;
} }
mListenersRegistered = false; listenersRegistered = false;
mSocketExists = false; socketExists = false;
} }
} }
package chat.rocket.android.service.ddp_subscriber;
import android.content.Context;
import android.text.TextUtils;
import chat.rocket.android.helper.LogcatIfError;
import chat.rocket.android.service.Registerable;
import chat.rocket.android.ws.RocketChatWebSocketAPI;
import chat.rocket.android_ddp.DDPSubscription;
import io.realm.Realm;
import io.realm.RealmObject;
import java.util.Iterator;
import jp.co.crowdworks.realm_java_helpers_bolts.RealmHelperBolts;
import org.json.JSONException;
import org.json.JSONObject;
import rx.Subscription;
import timber.log.Timber;
abstract class AbstractDDPDocEventSubscriber implements Registerable {
protected final Context context;
protected final RocketChatWebSocketAPI webSocketAPI;
private String subscriptionId;
private Subscription rxSubscription;
protected AbstractDDPDocEventSubscriber(Context context, RocketChatWebSocketAPI api) {
this.context = context;
this.webSocketAPI = api;
}
protected abstract String getSubscriptionName();
protected abstract String getSubscriptionCallbackName();
protected abstract Class<? extends RealmObject> getModelClass();
protected JSONObject customizeFieldJson(JSONObject json) {
return json;
}
@Override public void register() {
webSocketAPI.subscribe(getSubscriptionName(), null).onSuccess(task -> {
subscriptionId = task.getResult().id;
return null;
}).continueWith(task -> {
if (task.isFaulted()) {
Timber.w(task.getError(), "DDP subscription failed.");
}
return null;
});
RealmHelperBolts.executeTransaction(realm -> {
realm.delete(getModelClass());
return null;
}).onSuccess(task -> {
registerSubscriptionCallback();
return null;
}).continueWith(new LogcatIfError());
}
private void registerSubscriptionCallback() {
rxSubscription = webSocketAPI.getSubscriptionCallback()
.filter(event -> event instanceof DDPSubscription.DocEvent)
.cast(DDPSubscription.DocEvent.class)
.filter(event -> getSubscriptionCallbackName().equals(event.collection))
.subscribe(docEvent -> {
try {
if (docEvent instanceof DDPSubscription.Added.Before) {
onDocumentAdded((DDPSubscription.Added) docEvent); //ignore Before
} else if (docEvent instanceof DDPSubscription.Added) {
onDocumentAdded((DDPSubscription.Added) docEvent);
} else if (docEvent instanceof DDPSubscription.Removed) {
onDocumentRemoved((DDPSubscription.Removed) docEvent);
} else if (docEvent instanceof DDPSubscription.Changed) {
onDocumentChanged((DDPSubscription.Changed) docEvent);
} else if (docEvent instanceof DDPSubscription.MovedBefore) {
//ignore movedBefore
}
} catch (Exception exception) {
Timber.w(exception, "failed to handle subscription callback");
}
});
}
protected void onDocumentAdded(DDPSubscription.Added docEvent) {
RealmHelperBolts.executeTransaction(realm -> {
onDocumentAdded(realm, docEvent);
return null;
}).continueWith(new LogcatIfError());
}
private void onDocumentAdded(Realm realm, DDPSubscription.Added docEvent) throws JSONException {
//executed in RealmTransaction
JSONObject json = new JSONObject().put("id", docEvent.docID);
mergeJson(json, docEvent.fields);
realm.createOrUpdateObjectFromJson(getModelClass(), customizeFieldJson(json));
}
protected void onDocumentChanged(DDPSubscription.Changed docEvent) {
RealmHelperBolts.executeTransaction(realm -> {
onDocumentChanged(realm, docEvent);
return null;
}).continueWith(new LogcatIfError());
}
private void onDocumentChanged(Realm realm, DDPSubscription.Changed docEvent)
throws JSONException {
//executed in RealmTransaction
JSONObject json = new JSONObject().put("id", docEvent.docID);
for (int i = 0; i < docEvent.cleared.length(); i++) {
String fieldToDelete = docEvent.cleared.getString(i);
json.remove(fieldToDelete);
}
mergeJson(json, docEvent.fields);
realm.createOrUpdateObjectFromJson(getModelClass(), customizeFieldJson(json));
}
protected void onDocumentRemoved(DDPSubscription.Removed docEvent) {
RealmHelperBolts.executeTransaction(realm -> {
onDocumentRemoved(realm, docEvent);
return null;
}).continueWith(new LogcatIfError());
}
private void onDocumentRemoved(Realm realm, DDPSubscription.Removed docEvent)
throws JSONException {
//executed in RealmTransaction
realm.where(getModelClass()).equalTo("id", docEvent.docID).findAll().deleteAllFromRealm();
}
private void mergeJson(JSONObject target, JSONObject src) throws JSONException {
Iterator<String> iterator = src.keys();
while (iterator.hasNext()) {
String key = iterator.next();
target.put(key, src.get(key));
}
}
@Override public void keepalive() {
}
@Override public void unregister() {
if (rxSubscription != null) {
rxSubscription.unsubscribe();
}
if (!TextUtils.isEmpty(subscriptionId)) {
webSocketAPI.unsubscribe(subscriptionId).continueWith(new LogcatIfError());
}
}
}
package chat.rocket.android.service.ddp_subscriber;
import android.content.Context;
import chat.rocket.android.model.MeteorLoginServiceConfiguration;
import chat.rocket.android.ws.RocketChatWebSocketAPI;
import io.realm.RealmObject;
/**
* meteor.loginServiceConfiguration subscriber
*/
public class LoginServiceConfigurationSubscriber extends AbstractDDPDocEventSubscriber {
public LoginServiceConfigurationSubscriber(Context context, RocketChatWebSocketAPI api) {
super(context, api);
}
@Override protected String getSubscriptionName() {
return "meteor.loginServiceConfiguration";
}
@Override protected String getSubscriptionCallbackName() {
return "meteor_accounts_loginServiceConfiguration";
}
@Override protected Class<? extends RealmObject> getModelClass() {
return MeteorLoginServiceConfiguration.class;
}
}
package chat.rocket.android.service.ddp_subscription;
import android.content.Context;
import android.text.TextUtils;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.Iterator;
import chat.rocket.android.helper.LogcatIfError;
import chat.rocket.android.service.Registerable;
import chat.rocket.android.ws.RocketChatWebSocketAPI;
import chat.rocket.android_ddp.DDPSubscription;
import io.realm.Realm;
import io.realm.RealmObject;
import jp.co.crowdworks.realm_java_helpers_bolts.RealmHelperBolts;
import rx.Subscription;
import timber.log.Timber;
abstract class AbstractDDPDocEventSubscriber implements Registerable {
protected final Context mContext;
protected final RocketChatWebSocketAPI mAPI;
private String mID;
private Subscription mSubscription;
public AbstractDDPDocEventSubscriber(Context context, RocketChatWebSocketAPI api) {
mContext = context;
mAPI = api;
}
protected abstract String getSubscriptionName();
protected abstract String getSubscriptionCallbackName();
protected abstract Class<? extends RealmObject> getModelClass();
protected JSONObject customizeFieldJSON(JSONObject json) { return json; }
@Override
public void register() {
mAPI.subscribe(getSubscriptionName(), null).onSuccess(task -> {
mID = task.getResult().id;
return null;
}).continueWith(task -> {
if (task.isFaulted()) {
Timber.w(task.getError());
}
return null;
});
RealmHelperBolts.executeTransaction(realm -> {
realm.delete(getModelClass());
return null;
}).onSuccess(task -> {
registerSubscriptionCallback();
return null;
}).continueWith(new LogcatIfError());
}
private void registerSubscriptionCallback() {
mSubscription = mAPI.getSubscriptionCallback()
.filter(event -> event instanceof DDPSubscription.DocEvent
&& getSubscriptionCallbackName().equals(((DDPSubscription.DocEvent) event).collection))
.cast(DDPSubscription.DocEvent.class)
.subscribe(docEvent -> {
try {
if (docEvent instanceof DDPSubscription.Added.Before) {
onDocumentAdded((DDPSubscription.Added) docEvent); //ignore Before
} else if (docEvent instanceof DDPSubscription.Added) {
onDocumentAdded((DDPSubscription.Added) docEvent);
} else if (docEvent instanceof DDPSubscription.Removed) {
onDocumentRemoved((DDPSubscription.Removed) docEvent);
} else if (docEvent instanceof DDPSubscription.Changed) {
onDocumentChanged((DDPSubscription.Changed) docEvent);
} else if (docEvent instanceof DDPSubscription.MovedBefore) {
//ignore movedBefore
}
} catch (Exception e) {
Timber.w(e);
}
});
}
protected void onDocumentAdded(DDPSubscription.Added docEvent) {
RealmHelperBolts.executeTransaction(realm -> {
onDocumentAdded(realm, docEvent);
return null;
}).continueWith(new LogcatIfError());
}
protected void onDocumentChanged(DDPSubscription.Changed docEvent) {
RealmHelperBolts.executeTransaction(realm -> {
onDocumentChanged(realm, docEvent);
return null;
}).continueWith(new LogcatIfError());
}
protected void onDocumentRemoved(DDPSubscription.Removed docEvent) {
RealmHelperBolts.executeTransaction(realm -> {
onDocumentRemoved(realm, docEvent);
return null;
}).continueWith(new LogcatIfError());
}
private void mergeJSON(JSONObject target, JSONObject src) throws JSONException {
Iterator<String> it = src.keys();
while(it.hasNext()) {
String key = it.next();
target.put(key, src.get(key));
}
}
private void onDocumentAdded(Realm realm, DDPSubscription.Added docEvent) throws JSONException {
//executed in RealmTransaction
JSONObject json = new JSONObject().put("id", docEvent.docID);
mergeJSON(json, docEvent.fields);
realm.createOrUpdateObjectFromJson(getModelClass(), customizeFieldJSON(json));
}
private void onDocumentChanged(Realm realm, DDPSubscription.Changed docEvent) throws JSONException {
//executed in RealmTransaction
JSONObject json = new JSONObject().put("id", docEvent.docID);
for (int i=0; i<docEvent.cleared.length(); i++) {
String fieldToDelete = docEvent.cleared.getString(i);
json.remove(fieldToDelete);
}
mergeJSON(json, docEvent.fields);
realm.createOrUpdateObjectFromJson(getModelClass(), customizeFieldJSON(json));
}
private void onDocumentRemoved(Realm realm, DDPSubscription.Removed docEvent) throws JSONException {
//executed in RealmTransaction
realm.where(getModelClass()).equalTo("id", docEvent.docID).findAll().deleteAllFromRealm();
}
@Override
public void keepalive() {
}
@Override
public void unregister() {
if (mSubscription != null) mSubscription.unsubscribe();
if (!TextUtils.isEmpty(mID)) {
mAPI.unsubscribe(mID).continueWith(new LogcatIfError());
}
}
}
package chat.rocket.android.service.ddp_subscription;
import android.content.Context;
import chat.rocket.android.model.doc.MeteorLoginServiceConfiguration;
import chat.rocket.android.ws.RocketChatWebSocketAPI;
import io.realm.RealmObject;
public class LoginServiceConfigurationSubscriber extends AbstractDDPDocEventSubscriber {
public LoginServiceConfigurationSubscriber(Context context, RocketChatWebSocketAPI api) {
super(context, api);
}
@Override
protected String getSubscriptionName() {
return "meteor.loginServiceConfiguration";
}
@Override
protected String getSubscriptionCallbackName() {
return "meteor_accounts_loginServiceConfiguration";
}
@Override
protected Class<? extends RealmObject> getModelClass() {
return MeteorLoginServiceConfiguration.class;
}
}
package chat.rocket.android.service.observer; package chat.rocket.android.service.observer;
import android.content.Context; import android.content.Context;
import chat.rocket.android.service.Registerable; import chat.rocket.android.service.Registerable;
import chat.rocket.android.ws.RocketChatWebSocketAPI; import chat.rocket.android.ws.RocketChatWebSocketAPI;
import io.realm.RealmObject; import io.realm.RealmObject;
import jp.co.crowdworks.realm_java_helpers.RealmListObserver; import jp.co.crowdworks.realm_java_helpers.RealmListObserver;
abstract class AbstractModelObserver<T extends RealmObject> extends RealmListObserver<T> implements Registerable { abstract class AbstractModelObserver<T extends RealmObject> extends RealmListObserver<T>
implements Registerable {
protected final Context mContext; protected final Context context;
protected final RocketChatWebSocketAPI mAPI; protected final RocketChatWebSocketAPI webSocketAPI;
public AbstractModelObserver(Context context, RocketChatWebSocketAPI api) { protected AbstractModelObserver(Context context, RocketChatWebSocketAPI api) {
mContext = context; this.context = context;
mAPI = api; webSocketAPI = api;
} }
@Override public void register() {
@Override
public void register() {
sub(); sub();
} }
@Override @Override public void unregister() {
public void unregister() {
unsub(); unsub();
} }
} }
...@@ -7,8 +7,6 @@ import android.util.AttributeSet; ...@@ -7,8 +7,6 @@ import android.util.AttributeSet;
abstract class AbstractCustomFontTextView extends AppCompatTextView { abstract class AbstractCustomFontTextView extends AppCompatTextView {
protected abstract String getFont();
public AbstractCustomFontTextView(Context context, AttributeSet attrs, int defStyle) { public AbstractCustomFontTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); super(context, attrs, defStyle);
init(); init();
...@@ -24,11 +22,15 @@ abstract class AbstractCustomFontTextView extends AppCompatTextView { ...@@ -24,11 +22,15 @@ abstract class AbstractCustomFontTextView extends AppCompatTextView {
init(); init();
} }
protected abstract String getFont();
private void init() { private void init() {
String font = getFont(); String font = getFont();
if (font!=null) { if (font != null) {
Typeface tf = TypefaceHelper.getTypeface(getContext(), font); Typeface typeface = TypefaceHelper.getTypeface(getContext(), font);
if (tf!=null) setTypeface(tf); if (typeface != null) {
setTypeface(typeface);
}
} }
} }
} }
...@@ -3,6 +3,9 @@ package chat.rocket.android.view; ...@@ -3,6 +3,9 @@ package chat.rocket.android.view;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
/**
* TextView with font-awesome.
*/
public class FontAwesomeTextView extends AbstractCustomFontTextView { public class FontAwesomeTextView extends AbstractCustomFontTextView {
public FontAwesomeTextView(Context context, AttributeSet attrs, int defStyle) { public FontAwesomeTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); super(context, attrs, defStyle);
...@@ -16,8 +19,7 @@ public class FontAwesomeTextView extends AbstractCustomFontTextView { ...@@ -16,8 +19,7 @@ public class FontAwesomeTextView extends AbstractCustomFontTextView {
super(context); super(context);
} }
@Override @Override protected String getFont() {
protected String getFont() {
return "fontawesome-webfont.ttf"; return "fontawesome-webfont.ttf";
} }
} }
...@@ -3,6 +3,9 @@ package chat.rocket.android.view; ...@@ -3,6 +3,9 @@ package chat.rocket.android.view;
import android.content.Context; import android.content.Context;
import android.util.AttributeSet; import android.util.AttributeSet;
/**
* TextView with fontello.
*/
public class FontelloTextView extends AbstractCustomFontTextView { public class FontelloTextView extends AbstractCustomFontTextView {
public FontelloTextView(Context context, AttributeSet attrs, int defStyle) { public FontelloTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); super(context, attrs, defStyle);
...@@ -16,8 +19,7 @@ public class FontelloTextView extends AbstractCustomFontTextView { ...@@ -16,8 +19,7 @@ public class FontelloTextView extends AbstractCustomFontTextView {
super(context); super(context);
} }
@Override @Override protected String getFont() {
protected String getFont() {
return "fontello.ttf"; return "fontello.ttf";
} }
} }
...@@ -3,29 +3,32 @@ package chat.rocket.android.view; ...@@ -3,29 +3,32 @@ package chat.rocket.android.view;
import android.content.Context; import android.content.Context;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.util.Log; import android.util.Log;
import java.util.Hashtable; import java.util.Hashtable;
// ref:https://code.google.com/p/android/issues/detail?id=9904#c7 /**
* Helper for reading typeface. ref:https://code.google.com/p/android/issues/detail?id=9904#c7
*/
public class TypefaceHelper { public class TypefaceHelper {
private static final String TAG = TypefaceHelper.class.getName(); private static final String TAG = TypefaceHelper.class.getName();
private static final Hashtable<String, Typeface> cache = new Hashtable<String, Typeface>(); private static final Hashtable<String, Typeface> CACHE = new Hashtable<String, Typeface>();
public static Typeface getTypeface(Context c, String assetPath) { /**
synchronized (cache) { * read font in assets directory.
if (!cache.containsKey(assetPath)) { */
public static Typeface getTypeface(Context context, String assetPath) {
synchronized (CACHE) {
if (!CACHE.containsKey(assetPath)) {
try { try {
Typeface t = Typeface.createFromAsset(c.getAssets(), Typeface typeface = Typeface.createFromAsset(context.getAssets(), assetPath);
assetPath); CACHE.put(assetPath, typeface);
cache.put(assetPath, t); } catch (Exception exception) {
} catch (Exception e) { Log.e(TAG,
Log.e(TAG, "Could not get typeface '" + assetPath "Could not get typeface '" + assetPath + "' because " + exception.getMessage());
+ "' because " + e.getMessage());
return null; return null;
} }
} }
return cache.get(assetPath); return CACHE.get(assetPath);
} }
} }
} }
...@@ -11,14 +11,14 @@ import android.view.View; ...@@ -11,14 +11,14 @@ import android.view.View;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import java.util.ArrayList;
import chat.rocket.android.R; import chat.rocket.android.R;
import java.util.ArrayList;
/**
* View for indicating "waiting for connection ..." and so on.
*/
public class WaitingView extends LinearLayout { public class WaitingView extends LinearLayout {
private ArrayList<View> mDots; private ArrayList<View> dots;
public WaitingView(Context context) { public WaitingView(Context context) {
super(context); super(context);
...@@ -46,58 +46,60 @@ public class WaitingView extends LinearLayout { ...@@ -46,58 +46,60 @@ public class WaitingView extends LinearLayout {
int count = 3; int count = 3;
if (attrs != null) { if (attrs != null) {
TypedArray a = context.getTheme().obtainStyledAttributes( TypedArray array =
attrs, context.getTheme().obtainStyledAttributes(attrs, R.styleable.WaitingView, 0, 0);
R.styleable.WaitingView, 0, 0); size = array.getDimensionPixelSize(R.styleable.WaitingView_dotSize, size);
size = a.getDimensionPixelSize(R.styleable.WaitingView_dotSize, size); count = array.getInteger(R.styleable.WaitingView_dotCount, count);
count = a.getInteger(R.styleable.WaitingView_dotCount, count); array.recycle();
a.recycle();
} }
mDots = new ArrayList<>(); dots = new ArrayList<>();
setOrientation(HORIZONTAL); setOrientation(HORIZONTAL);
for (int i=0; i<count; i++) addDot(context, size); for (int i = 0; i < count; i++) {
addDot(context, size);
}
addOnAttachStateChangeListener(new OnAttachStateChangeListener() { addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
@Override @Override public void onViewAttachedToWindow(View view) {
public void onViewAttachedToWindow(View view) {
start(); start();
} }
@Override @Override public void onViewDetachedFromWindow(View view) {
public void onViewDetachedFromWindow(View view) {
cancel(); cancel();
} }
}); });
} }
private void addDot(Context context, int size) { private void addDot(Context context, int size) {
FrameLayout f = new FrameLayout(context); FrameLayout frameLayout = new FrameLayout(context);
f.setLayoutParams(new LinearLayoutCompat.LayoutParams(size*3/2, size*3/2)); frameLayout.setLayoutParams(new LinearLayoutCompat.LayoutParams(size * 3 / 2, size * 3 / 2));
ImageView dot = new ImageView(context); ImageView dot = new ImageView(context);
dot.setImageResource(R.drawable.white_circle); dot.setImageResource(R.drawable.white_circle);
dot.setLayoutParams(new FrameLayout.LayoutParams(size, size, Gravity.CENTER)); dot.setLayoutParams(new FrameLayout.LayoutParams(size, size, Gravity.CENTER));
f.addView(dot); frameLayout.addView(dot);
addView(f); addView(frameLayout);
mDots.add(dot); dots.add(dot);
} }
private void start() { private void start() {
for(int i=0; i<mDots.size(); i++) { for (int i = 0; i < dots.size(); i++) {
animateDot(mDots.get(i), 160*i, 480, 480); animateDot(dots.get(i), 160 * i, 480, 480);
} }
} }
private void animateDot(final View dot, final long startDelay, final long duration, final long interval) { private void animateDot(final View dot, final long startDelay, final long duration,
final long interval) {
dot.setScaleX(0); dot.setScaleX(0);
dot.setScaleY(0); dot.setScaleY(0);
dot.animate() dot.animate()
.scaleX(1).scaleY(1) .scaleX(1)
.scaleY(1)
.setDuration(duration) .setDuration(duration)
.setStartDelay(startDelay) .setStartDelay(startDelay)
.withEndAction(() -> { .withEndAction(() -> {
dot.animate() dot.animate()
.scaleX(0).scaleY(0) .scaleX(0)
.scaleY(0)
.setDuration(duration) .setDuration(duration)
.setStartDelay(0) .setStartDelay(0)
.withEndAction(() -> { .withEndAction(() -> {
...@@ -109,7 +111,7 @@ public class WaitingView extends LinearLayout { ...@@ -109,7 +111,7 @@ public class WaitingView extends LinearLayout {
} }
private void cancel() { private void cancel() {
for(View dot: mDots) { for (View dot : dots) {
dot.clearAnimation(); dot.clearAnimation();
} }
} }
......
package chat.rocket.android.ws; package chat.rocket.android.ws;
import org.json.JSONArray;
import java.util.UUID;
import bolts.Task; import bolts.Task;
import chat.rocket.android.helper.OkHttpHelper; import chat.rocket.android.helper.OkHttpHelper;
import chat.rocket.android_ddp.DDPClient; import chat.rocket.android_ddp.DDPClient;
import chat.rocket.android_ddp.DDPClientCallback; import chat.rocket.android_ddp.DDPClientCallback;
import chat.rocket.android_ddp.DDPSubscription; import chat.rocket.android_ddp.DDPSubscription;
import java.util.UUID;
import org.json.JSONArray;
import rx.Observable; import rx.Observable;
/**
* API for several POST actions.
*/
public class RocketChatWebSocketAPI { public class RocketChatWebSocketAPI {
private final DDPClient mDDPClient; private final DDPClient ddpClient;
private final String mHostName; private final String hostname;
private RocketChatWebSocketAPI(String hostname) { private RocketChatWebSocketAPI(String hostname) {
mDDPClient = new DDPClient(OkHttpHelper.getClientForWebSocket()); ddpClient = new DDPClient(OkHttpHelper.getClientForWebSocket());
mHostName = hostname; this.hostname = hostname;
} }
/**
* create new API client instance.
*/
public static RocketChatWebSocketAPI create(String hostname) { public static RocketChatWebSocketAPI create(String hostname) {
return new RocketChatWebSocketAPI(hostname); return new RocketChatWebSocketAPI(hostname);
} }
/**
* Connect to WebSocket server with DDP client.
*/
public Task<DDPClientCallback.Connect> connect() { public Task<DDPClientCallback.Connect> connect() {
return mDDPClient.connect("wss://" + mHostName + "/websocket"); return ddpClient.connect("wss://" + hostname + "/websocket");
} }
/**
* Returns whether DDP client is connected to WebSocket server.
*/
public boolean isConnected() { public boolean isConnected() {
return mDDPClient.isConnected(); return ddpClient.isConnected();
} }
/**
* close connection.
*/
public void close() { public void close() {
mDDPClient.close(); ddpClient.close();
} }
/**
* Subscribe with DDP client.
*/
public Task<DDPSubscription.Ready> subscribe(final String name, JSONArray param) { public Task<DDPSubscription.Ready> subscribe(final String name, JSONArray param) {
return mDDPClient.sub(UUID.randomUUID().toString(), name, param); return ddpClient.sub(UUID.randomUUID().toString(), name, param);
} }
public Task<DDPSubscription.NoSub> unsubscribe(final String id) { /**
return mDDPClient.unsub(id); * Unsubscribe with DDP client.
*/
public Task<DDPSubscription.NoSub> unsubscribe(final String subscriptionId) {
return ddpClient.unsub(subscriptionId);
} }
/**
* Returns Observable for handling DDP subscription.
*/
public Observable<DDPSubscription.Event> getSubscriptionCallback() { public Observable<DDPSubscription.Event> getSubscriptionCallback() {
return mDDPClient.getSubscriptionCallback(); return ddpClient.getSubscriptionCallback();
} }
} }
<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="24.0"
android:viewportWidth="24.0" android:viewportWidth="24.0"
android:viewportHeight="24.0"> android:width="24dp">
<path <path
android:fillColor="#FFFFFFFF" android:fillColor="#FFFFFFFF"
android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/> android:pathData="M12,4l-1.41,1.41L16.17,11H4v2h12.17l-5.58,5.59L12,20l8,-8z"/>
......
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<!--item android:state_enabled="false" android:color="@color/textColorLink" /--> <!--item android:state_enabled="false" android:color="@color/textColorLink" /-->
<item android:color="@color/textColorLink" /> <item android:color="@color/textColorLink"/>
</selector> </selector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape android:shape="oval" xmlns:android="http://schemas.android.com/apk/res/android"> <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/userstatus_away"/> <solid android:color="@color/userstatus_away"/>
<stroke android:width="1dp" android:color="@color/userstatus_away_outline" /> <stroke
android:color="@color/userstatus_away_outline"
android:width="1dp"/>
</shape> </shape>
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape android:shape="oval" xmlns:android="http://schemas.android.com/apk/res/android"> <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/userstatus_busy"/> <solid android:color="@color/userstatus_busy"/>
<stroke android:width="1dp" android:color="@color/userstatus_busy_outline" /> <stroke
android:color="@color/userstatus_busy_outline"
android:width="1dp"/>
</shape> </shape>
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape android:shape="oval" xmlns:android="http://schemas.android.com/apk/res/android"> <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/userstatus_offline"/> <solid android:color="@color/userstatus_offline"/>
<stroke android:width="1dp" android:color="@color/userstatus_offline_outline" /> <stroke
android:color="@color/userstatus_offline_outline"
android:width="1dp"/>
</shape> </shape>
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<shape android:shape="oval" xmlns:android="http://schemas.android.com/apk/res/android"> <shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/userstatus_online"/> <solid android:color="@color/userstatus_online"/>
<stroke android:width="1dp" android:color="@color/userstatus_online_outline" /> <stroke
android:color="@color/userstatus_online_outline"
android:width="1dp"/>
</shape> </shape>
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
>
<include layout="@layout/sidebar" /> <include layout="@layout/sidebar"/>
<android.support.design.widget.CoordinatorLayout <android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
>
<android.support.design.widget.AppBarLayout <android.support.design.widget.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
>
<android.support.v7.widget.Toolbar <android.support.v7.widget.Toolbar
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:title="@string/app_name"/> app:title="@string/app_name"
/>
</android.support.design.widget.AppBarLayout> </android.support.design.widget.AppBarLayout>
<FrameLayout <FrameLayout
...@@ -27,8 +31,8 @@ ...@@ -27,8 +31,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/white" android:background="@color/white"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior"
</FrameLayout> ></FrameLayout>
</android.support.design.widget.CoordinatorLayout> </android.support.design.widget.CoordinatorLayout>
</LinearLayout> </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" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
>
<include layout="@layout/sidebar" /> <include layout="@layout/sidebar"/>
<android.support.design.widget.CoordinatorLayout <android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
>
<android.support.design.widget.AppBarLayout <android.support.design.widget.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
>
<android.support.v7.widget.Toolbar <android.support.v7.widget.Toolbar
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:title="@string/app_name"/> app:title="@string/app_name"
/>
</android.support.design.widget.AppBarLayout> </android.support.design.widget.AppBarLayout>
<FrameLayout <FrameLayout
...@@ -26,8 +32,8 @@ ...@@ -26,8 +32,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@color/white" android:background="@color/white"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior"
</FrameLayout> ></FrameLayout>
</android.support.design.widget.CoordinatorLayout> </android.support.design.widget.CoordinatorLayout>
</android.support.v4.widget.SlidingPaneLayout> </android.support.v4.widget.SlidingPaneLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/avatar_image_size_large" android:layout_height="@dimen/avatar_image_size_large"> android:layout_width="@dimen/avatar_image_size_large"
android:layout_height="@dimen/avatar_image_size_large"
>
<FrameLayout <FrameLayout
android:id="@+id/avatar_color" android:id="@+id/avatar_color"
android:layout_width="@dimen/avatar_image_size_large" android:layout_width="@dimen/avatar_image_size_large"
android:layout_height="@dimen/avatar_image_size_large" android:layout_height="@dimen/avatar_image_size_large"
android:layout_gravity="center"> android:layout_gravity="center"
>
<TextView <TextView
android:id="@+id/avatar_initials" android:id="@+id/avatar_initials"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center"
android:textSize="@dimen/avatar_text_size_large" android:textSize="@dimen/avatar_text_size_large"
android:layout_gravity="center"/> />
</FrameLayout> </FrameLayout>
<ImageView <ImageView
android:id="@+id/avatar_img" android:id="@+id/avatar_img"
android:layout_width="@dimen/avatar_image_size_large" android:layout_width="@dimen/avatar_image_size_large"
android:layout_height="@dimen/avatar_image_size_large" android:layout_height="@dimen/avatar_image_size_large"
android:scaleType="centerInside"
android:src="@drawable/ic_default_avatar" android:src="@drawable/ic_default_avatar"
android:scaleType="centerInside"/> />
</FrameLayout> </FrameLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/avatar_image_size_normal" android:layout_height="@dimen/avatar_image_size_normal"> android:layout_width="@dimen/avatar_image_size_normal"
android:layout_height="@dimen/avatar_image_size_normal"
>
<FrameLayout <FrameLayout
android:id="@+id/avatar_color" android:id="@+id/avatar_color"
android:layout_width="@dimen/avatar_image_size_normal" android:layout_width="@dimen/avatar_image_size_normal"
android:layout_height="@dimen/avatar_image_size_normal" android:layout_height="@dimen/avatar_image_size_normal"
android:layout_gravity="center"> android:layout_gravity="center"
>
<TextView <TextView
android:id="@+id/avatar_initials" android:id="@+id/avatar_initials"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center"
android:textSize="@dimen/avatar_text_size_normal" android:textSize="@dimen/avatar_text_size_normal"
android:layout_gravity="center"/> />
</FrameLayout> </FrameLayout>
<ImageView <ImageView
android:id="@+id/avatar_img" android:id="@+id/avatar_img"
android:layout_width="@dimen/avatar_image_size_normal" android:layout_width="@dimen/avatar_image_size_normal"
android:layout_height="@dimen/avatar_image_size_normal" android:layout_height="@dimen/avatar_image_size_normal"
android:scaleType="centerInside"
android:src="@drawable/ic_default_avatar" android:src="@drawable/ic_default_avatar"
android:scaleType="centerInside"/> />
</FrameLayout> </FrameLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout 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_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto" android:background="?attr/colorPrimaryDark"
android:background="?attr/colorPrimaryDark"> >
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:minWidth="288dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:layout_gravity="center"
android:background="@color/white" android:background="@color/white"
android:minWidth="288dp"
android:orientation="horizontal"
android:padding="@dimen/margin_24" android:padding="@dimen/margin_24"
android:layout_gravity="center"> >
<LinearLayout <LinearLayout
android:layout_width="0px" android:layout_width="0px"
android:layout_weight="1"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:layout_weight="1"
android:orientation="vertical"
>
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="hostname" android:text="hostname"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"/> android:textAppearance="@style/TextAppearance.AppCompat.Caption"
/>
<EditText <EditText
android:id="@+id/editor_hostname" android:id="@+id/editor_hostname"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:singleLine="true"
android:hint="demo.rocket.chat" android:hint="demo.rocket.chat"
android:imeOptions="actionGo" android:imeOptions="actionGo"
android:inputType="textWebEditText"/> android:inputType="textWebEditText"
android:singleLine="true"
/>
</LinearLayout> </LinearLayout>
<Space <Space
android:layout_width="@dimen/margin_8" android:layout_width="@dimen/margin_8"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
/>
<android.support.design.widget.FloatingActionButton <android.support.design.widget.FloatingActionButton
android:id="@+id/btn_connect" android:id="@+id/btn_connect"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="end|bottom"
app:elevation="2dp"
app:fabSize="mini" app:fabSize="mini"
app:srcCompat="@drawable/ic_arrow_forward_white_24dp" app:srcCompat="@drawable/ic_arrow_forward_white_24dp"
app:elevation="2dp" />
android:layout_gravity="end|bottom"/>
</LinearLayout> </LinearLayout>
</FrameLayout> </FrameLayout>
\ No newline at end of file
...@@ -2,10 +2,12 @@ ...@@ -2,10 +2,12 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorPrimaryDark"> android:background="?attr/colorPrimaryDark"
>
<chat.rocket.android.view.WaitingView <chat.rocket.android.view.WaitingView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center"/> android:layout_gravity="center"
/>
</FrameLayout> </FrameLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="288dp" android:layout_width="288dp"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="start" android:layout_gravity="start"
android:background="?attr/colorPrimary" android:background="?attr/colorPrimary"
android:orientation="vertical" android:orientation="vertical"
android:theme="@style/AppTheme.Dark"> android:theme="@style/AppTheme.Dark"
>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?attr/colorPrimaryDark"
android:orientation="vertical" android:orientation="vertical"
android:background="?attr/colorPrimaryDark"> >
<LinearLayout <LinearLayout
android:id="@+id/user_info_container" android:id="@+id/user_info_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:background="?attr/selectableItemBackground"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="@dimen/margin_16" android:padding="@dimen/margin_16"
android:background="?attr/selectableItemBackground"> >
<ImageView <ImageView
android:id="@+id/img_userstatus" android:id="@+id/img_userstatus"
android:layout_width="8dp" android:layout_width="8dp"
android:layout_height="8dp" android:layout_height="8dp"
android:src="@drawable/userstatus_online"/> android:src="@drawable/userstatus_online"
/>
<Space <Space
android:layout_width="@dimen/margin_8" android:layout_width="@dimen/margin_8"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"
/>
<include layout="@layout/avatar_container_large"/> <include layout="@layout/avatar_container_large"/>
<FrameLayout <FrameLayout
android:layout_width="0px" android:layout_width="0px"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginLeft="@dimen/margin_8" android:layout_marginLeft="@dimen/margin_8"
android:layout_marginRight="@dimen/margin_8"> android:layout_marginRight="@dimen/margin_8"
android:layout_weight="1"
>
<TextView <TextView
android:id="@+id/txt_account_info" android:id="@+id/txt_account_info"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textSize="14sp"
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:text="John Doe"/> android:text="John Doe"
android:textSize="14sp"
/>
</FrameLayout> </FrameLayout>
...@@ -57,7 +63,8 @@ ...@@ -57,7 +63,8 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/fa_chevron_down" android:text="@string/fa_chevron_down"
android:textSize="16dp"/> android:textSize="16dp"
/>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/content" android:id="@+id/content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
>
</FrameLayout> </FrameLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string translatable="false" name="fa_chevron_down">&#xf078;</string> <string name="fa_chevron_down" translatable="false">&#xf078;</string>
</resources> </resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<declare-styleable name="WaitingView"> <declare-styleable name="WaitingView">
<attr name="dotSize" format="dimension" /> <attr format="dimension" name="dotSize"/>
<attr name="dotCount" format="integer" /> <attr format="integer" name="dotCount"/>
</declare-styleable> </declare-styleable>
<dimen name="def_waiting_view_dot_size">16dp</dimen> <dimen name="def_waiting_view_dot_size">16dp</dimen>
</resources> </resources>
\ No newline at end of file
...@@ -11,9 +11,13 @@ buildscript { ...@@ -11,9 +11,13 @@ buildscript {
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
classpath 'me.tatarka:gradle-retrolambda:3.3.1' classpath 'me.tatarka:gradle-retrolambda:3.3.1'
classpath 'me.tatarka.retrolambda.projectlombok:lombok.ast:0.2.3.a2'
classpath "io.realm:realm-gradle-plugin:2.1.1" classpath "io.realm:realm-gradle-plugin:2.1.1"
classpath 'com.jakewharton.hugo:hugo-plugin:1.2.1' classpath 'com.jakewharton.hugo:hugo-plugin:1.2.1'
} }
// Exclude the version that the android plugin depends on.
configurations.classpath.exclude group: 'com.android.tools.external.lombok'
} }
allprojects { allprojects {
......
<?xml version="1.0"?><!DOCTYPE module PUBLIC <?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN" "-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd"> "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
...@@ -15,204 +16,213 @@ ...@@ -15,204 +16,213 @@
--> -->
<module name="Checker"> <module name="Checker">
<property name="charset" value="UTF-8" /> <property name="charset" value="UTF-8"/>
<property name="severity" value="error" /> <property name="severity" value="warning"/>
<property name="fileExtensions" value="java, properties, xml" /> <property name="fileExtensions" value="java, properties, xml"/>
<!-- Checks for whitespace --> <!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html --> <!-- See http://checkstyle.sf.net/config_whitespace.html -->
<module name="FileTabCharacter"> <module name="FileTabCharacter">
<property name="eachLine" value="true" /> <property name="eachLine" value="true"/>
</module>
<module name="SuppressionCommentFilter">
<property name="offCommentFormat" value="CHECKSTYLE:OFF (\w+)"/>
<property name="onCommentFormat" value="CHECKSTYLE:ON (\w+)"/>
<property name="checkFormat" value="$1"/>
</module>
<module name="SuppressionFilter">
<property name="file" value="config/quality/checkstyle/checkstyle-suppressions.xml"/>
</module> </module>
<module name="TreeWalker"> <module name="TreeWalker">
<module name="OuterTypeFilename" /> <module name="OuterTypeFilename"/>
<module name="IllegalTokenText"> <module name="IllegalTokenText">
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL" /> <property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
<property name="format" <property name="format"
value="\\u00(08|09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)" /> value="\\u00(08|09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
<property name="message" value="Avoid using corresponding octal or Unicode escape." /> <property name="message" value="Avoid using corresponding octal or Unicode escape."/>
</module> </module>
<module name="AvoidEscapedUnicodeCharacters"> <module name="AvoidEscapedUnicodeCharacters">
<property name="allowEscapesForControlCharacters" value="true" /> <property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowByTailComment" value="true" /> <property name="allowByTailComment" value="true"/>
<property name="allowNonPrintableEscapes" value="true" /> <property name="allowNonPrintableEscapes" value="true"/>
</module> </module>
<module name="LineLength"> <module name="LineLength">
<property name="max" value="100" /> <property name="max" value="100"/>
<property name="ignorePattern" <property name="ignorePattern"
value="^package.*|^import.*|a href|href|http://|https://|ftp://" /> value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module> </module>
<module name="AvoidStarImport" /> <module name="AvoidStarImport"/>
<module name="OneTopLevelClass" /> <module name="OneTopLevelClass"/>
<module name="NoLineWrap" /> <module name="NoLineWrap"/>
<module name="EmptyBlock"> <module name="EmptyBlock">
<property name="option" value="TEXT" /> <property name="option" value="TEXT"/>
<property name="tokens" <property name="tokens"
value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH" /> value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
</module> </module>
<module name="NeedBraces" /> <module name="NeedBraces"/>
<module name="LeftCurly"> <module name="LeftCurly">
<property name="maxLineLength" value="100" /> <property name="maxLineLength" value="100"/>
</module> </module>
<module name="RightCurly" /> <module name="RightCurly"/>
<module name="RightCurly"> <module name="RightCurly">
<property name="option" value="alone" /> <property name="option" value="alone"/>
<property name="tokens" <property name="tokens"
value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO, STATIC_INIT, INSTANCE_INIT" /> value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO, STATIC_INIT, INSTANCE_INIT"/>
</module> </module>
<module name="WhitespaceAround"> <module name="WhitespaceAround">
<property name="allowEmptyConstructors" value="true" /> <property name="allowEmptyConstructors" value="true"/>
<property name="allowEmptyMethods" value="true" /> <property name="allowEmptyMethods" value="true"/>
<property name="allowEmptyTypes" value="true" /> <property name="allowEmptyTypes" value="true"/>
<property name="allowEmptyLoops" value="true" /> <property name="allowEmptyLoops" value="true"/>
<message key="ws.notFollowed" <message key="ws.notFollowed"
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)" /> value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
<message key="ws.notPreceded" <message key="ws.notPreceded"
value="WhitespaceAround: ''{0}'' is not preceded with whitespace." /> value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
</module> </module>
<module name="OneStatementPerLine" /> <module name="OneStatementPerLine"/>
<module name="MultipleVariableDeclarations" /> <module name="MultipleVariableDeclarations"/>
<module name="ArrayTypeStyle" /> <module name="ArrayTypeStyle"/>
<module name="MissingSwitchDefault" /> <module name="MissingSwitchDefault"/>
<module name="FallThrough" /> <module name="FallThrough"/>
<module name="UpperEll" /> <module name="UpperEll"/>
<module name="ModifierOrder" /> <module name="ModifierOrder"/>
<module name="EmptyLineSeparator"> <module name="EmptyLineSeparator">
<property name="allowNoEmptyLineBetweenFields" value="true" /> <property name="allowNoEmptyLineBetweenFields" value="true"/>
</module> </module>
<module name="SeparatorWrap"> <module name="SeparatorWrap">
<property name="tokens" value="DOT" /> <property name="tokens" value="DOT"/>
<property name="option" value="nl" /> <property name="option" value="nl"/>
</module> </module>
<module name="SeparatorWrap"> <module name="SeparatorWrap">
<property name="tokens" value="COMMA" /> <property name="tokens" value="COMMA"/>
<property name="option" value="EOL" /> <property name="option" value="EOL"/>
</module> </module>
<module name="PackageName"> <!-- TODO module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$" /> <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
<message key="name.invalidPattern" <message key="name.invalidPattern"
value="Package name ''{0}'' must match pattern ''{1}''." /> value="Package name ''{0}'' must match pattern ''{1}''."/>
</module> </module-->
<module name="TypeName"> <module name="TypeName">
<message key="name.invalidPattern" <message key="name.invalidPattern"
value="Type name ''{0}'' must match pattern ''{1}''." /> value="Type name ''{0}'' must match pattern ''{1}''."/>
</module> </module>
<module name="MemberName"> <module name="MemberName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$" /> <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<message key="name.invalidPattern" <message key="name.invalidPattern"
value="Member name ''{0}'' must match pattern ''{1}''." /> value="Member name ''{0}'' must match pattern ''{1}''."/>
</module> </module>
<module name="ParameterName"> <module name="ParameterName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$" /> <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<message key="name.invalidPattern" <message key="name.invalidPattern"
value="Parameter name ''{0}'' must match pattern ''{1}''." /> value="Parameter name ''{0}'' must match pattern ''{1}''."/>
</module> </module>
<module name="CatchParameterName"> <module name="CatchParameterName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$" /> <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<message key="name.invalidPattern" <message key="name.invalidPattern"
value="Catch parameter name ''{0}'' must match pattern ''{1}''." /> value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
</module> </module>
<module name="LocalVariableName"> <module name="LocalVariableName">
<property name="tokens" value="VARIABLE_DEF" /> <property name="tokens" value="VARIABLE_DEF"/>
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$" /> <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
<property name="allowOneCharVarInForLoop" value="true" /> <property name="allowOneCharVarInForLoop" value="true"/>
<message key="name.invalidPattern" <message key="name.invalidPattern"
value="Local variable name ''{0}'' must match pattern ''{1}''." /> value="Local variable name ''{0}'' must match pattern ''{1}''."/>
</module> </module>
<module name="ClassTypeParameterName"> <module name="ClassTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)" /> <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern" <message key="name.invalidPattern"
value="Class type name ''{0}'' must match pattern ''{1}''." /> value="Class type name ''{0}'' must match pattern ''{1}''."/>
</module> </module>
<module name="MethodTypeParameterName"> <module name="MethodTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)" /> <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern" <message key="name.invalidPattern"
value="Method type name ''{0}'' must match pattern ''{1}''." /> value="Method type name ''{0}'' must match pattern ''{1}''."/>
</module> </module>
<module name="InterfaceTypeParameterName"> <module name="InterfaceTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)" /> <property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
<message key="name.invalidPattern" <message key="name.invalidPattern"
value="Interface type name ''{0}'' must match pattern ''{1}''." /> value="Interface type name ''{0}'' must match pattern ''{1}''."/>
</module> </module>
<module name="NoFinalizer" /> <module name="NoFinalizer"/>
<module name="GenericWhitespace"> <module name="GenericWhitespace">
<message key="ws.followed" <message key="ws.followed"
value="GenericWhitespace ''{0}'' is followed by whitespace." /> value="GenericWhitespace ''{0}'' is followed by whitespace."/>
<message key="ws.preceded" <message key="ws.preceded"
value="GenericWhitespace ''{0}'' is preceded with whitespace." /> value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
<message key="ws.illegalFollow" <message key="ws.illegalFollow"
value="GenericWhitespace ''{0}'' should followed by whitespace." /> value="GenericWhitespace ''{0}'' should followed by whitespace."/>
<message key="ws.notPreceded" <message key="ws.notPreceded"
value="GenericWhitespace ''{0}'' is not preceded with whitespace." /> value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
</module> </module>
<module name="Indentation"> <module name="Indentation">
<property name="basicOffset" value="2" /> <property name="basicOffset" value="2"/>
<property name="braceAdjustment" value="0" /> <property name="braceAdjustment" value="0"/>
<property name="caseIndent" value="2" /> <property name="caseIndent" value="2"/>
<property name="throwsIndent" value="4" /> <property name="throwsIndent" value="4"/>
<property name="lineWrappingIndentation" value="4" /> <property name="lineWrappingIndentation" value="4"/>
<property name="arrayInitIndent" value="2" /> <property name="arrayInitIndent" value="2"/>
</module> </module>
<module name="AbbreviationAsWordInName"> <module name="AbbreviationAsWordInName">
<property name="ignoreFinal" value="false" /> <property name="ignoreFinal" value="false"/>
<property name="allowedAbbreviationLength" value="1" /> <!-- TODO property name="allowedAbbreviationLength" value="1"/-->
</module> </module>
<module name="OverloadMethodsDeclarationOrder" /> <module name="OverloadMethodsDeclarationOrder"/>
<module name="VariableDeclarationUsageDistance" /> <module name="VariableDeclarationUsageDistance"/>
<module name="CustomImportOrder"> <!-- TODO module name="CustomImportOrder">
<property name="specialImportsRegExp" value="com.google" /> <property name="specialImportsRegExp" value="com.google"/>
<property name="sortImportsInGroupAlphabetically" value="true" /> <property name="sortImportsInGroupAlphabetically" value="true"/>
<property name="customImportOrderRules" <property name="customImportOrderRules"
value="STATIC###SPECIAL_IMPORTS###THIRD_PARTY_PACKAGE###STANDARD_JAVA_PACKAGE" /> value="STATIC###SPECIAL_IMPORTS###THIRD_PARTY_PACKAGE###STANDARD_JAVA_PACKAGE"/>
</module> </module-->
<module name="MethodParamPad" /> <module name="MethodParamPad"/>
<module name="OperatorWrap"> <module name="OperatorWrap">
<property name="option" value="NL" /> <property name="option" value="NL"/>
<property name="tokens" <property name="tokens"
value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR " /> value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR "/>
</module> </module>
<module name="AnnotationLocation"> <module name="AnnotationLocation">
<property name="tokens" <property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF" />
</module> </module>
<module name="AnnotationLocation"> <module name="AnnotationLocation">
<property name="tokens" value="VARIABLE_DEF" /> <property name="tokens" value="VARIABLE_DEF"/>
<property name="allowSamelineMultipleAnnotations" value="true" /> <property name="allowSamelineMultipleAnnotations" value="true"/>
</module> </module>
<module name="NonEmptyAtclauseDescription" /> <module name="NonEmptyAtclauseDescription"/>
<module name="JavadocTagContinuationIndentation" /> <module name="JavadocTagContinuationIndentation"/>
<module name="SummaryJavadoc"> <module name="SummaryJavadoc">
<property name="forbiddenSummaryFragments" <property name="forbiddenSummaryFragments"
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )" /> value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
</module> </module>
<module name="JavadocParagraph" /> <module name="JavadocParagraph"/>
<module name="AtclauseOrder"> <module name="AtclauseOrder">
<property name="tagOrder" value="@param, @return, @throws, @deprecated" /> <property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
<property name="target" <property name="target"
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF" /> value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
</module> </module>
<module name="JavadocMethod"> <module name="JavadocMethod">
<property name="scope" value="public" /> <property name="scope" value="public"/>
<property name="allowMissingParamTags" value="true" /> <property name="allowMissingParamTags" value="true"/>
<property name="allowMissingThrowsTags" value="true" /> <property name="allowMissingThrowsTags" value="true"/>
<property name="allowMissingReturnTag" value="true" /> <property name="allowMissingReturnTag" value="true"/>
<property name="minLineCount" value="2" /> <property name="minLineCount" value="2"/>
<property name="allowedAnnotations" value="Override, Test" /> <property name="allowedAnnotations" value="Override, Test"/>
<property name="allowThrowsTagsForSubclasses" value="true" /> <property name="allowThrowsTagsForSubclasses" value="true"/>
</module> </module>
<module name="MethodName"> <module name="MethodName">
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$" /> <property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
<message key="name.invalidPattern" <message key="name.invalidPattern"
value="Method name ''{0}'' must match pattern ''{1}''." /> value="Method name ''{0}'' must match pattern ''{1}''."/>
</module> </module>
<module name="SingleLineJavadoc"> <module name="SingleLineJavadoc">
<property name="ignoreInlineTags" value="false" /> <property name="ignoreInlineTags" value="false"/>
</module> </module>
<module name="EmptyCatchBlock"> <module name="EmptyCatchBlock">
<property name="exceptionVariableName" value="expected" /> <property name="exceptionVariableName" value="expected"/>
</module> </module>
<module name="CommentsIndentation" /> <module name="CommentsIndentation"/>
</module> </module>
</module> </module>
\ No newline at end of file
<?xml version="1.0"?>
<!DOCTYPE suppressions PUBLIC
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<suppressions>
<!-- suppress some checks for classes extending RealmObject -->
<suppress checks="JavadocMethod" files="chat[\\/]rocket[\\/]android[\\/]model"/>
</suppressions>
\ No newline at end of file
...@@ -8,9 +8,9 @@ ...@@ -8,9 +8,9 @@
</Match> </Match>
<!-- All bugs in test classes, except for JUnit-specific bugs --> <!-- All bugs in test classes, except for JUnit-specific bugs -->
<Match> <Match>
<Class name="~.*\.*Test" /> <Class name="~.*\.*Test"/>
<Not> <Not>
<Bug code="IJU" /> <Bug code="IJU"/>
</Not> </Not>
</Match> </Match>
......
...@@ -9,19 +9,19 @@ ...@@ -9,19 +9,19 @@
<exclude-pattern>.*/R.java</exclude-pattern> <exclude-pattern>.*/R.java</exclude-pattern>
<exclude-pattern>.*/gen/.*</exclude-pattern> <exclude-pattern>.*/gen/.*</exclude-pattern>
<rule ref="rulesets/java/android.xml" /> <rule ref="rulesets/java/android.xml"/>
<rule ref="rulesets/java/clone.xml" /> <rule ref="rulesets/java/clone.xml"/>
<rule ref="rulesets/java/finalizers.xml" /> <rule ref="rulesets/java/finalizers.xml"/>
<rule ref="rulesets/java/imports.xml"> <rule ref="rulesets/java/imports.xml">
<!-- Espresso is designed this way !--> <!-- Espresso is designed this way !-->
<exclude name="TooManyStaticImports" /> <exclude name="TooManyStaticImports"/>
</rule> </rule>
<rule ref="rulesets/java/basic.xml" /> <rule ref="rulesets/java/basic.xml"/>
<rule ref="rulesets/java/naming.xml"> <rule ref="rulesets/java/naming.xml">
<!--<exclude name="AbstractNaming" />--> <!--<exclude name="AbstractNaming" />-->
<exclude name="LongVariable" /> <exclude name="LongVariable"/>
<!--<exclude name="ShortMethodName" />--> <!--exclude name="ShortMethodName" /-->
<!--<exclude name="ShortVariable" />--> <!--exclude name="ShortVariable" /-->
<!--<exclude name="ShortClassName" />--> <!--<exclude name="ShortClassName" />-->
<!--<exclude name="VariableNamingConventions" />--> <!--<exclude name="VariableNamingConventions" />-->
</rule> </rule>
......
...@@ -7,8 +7,7 @@ ...@@ -7,8 +7,7 @@
* - pmd * - pmd
* *
* The three tasks above are added as dependencies of the check task so running check will * The three tasks above are added as dependencies of the check task so running check will
* run all of them. * run all of them.*/
*/
apply plugin: 'checkstyle' apply plugin: 'checkstyle'
apply plugin: 'findbugs' apply plugin: 'findbugs'
...@@ -35,13 +34,13 @@ task checkstyle(type: Checkstyle, group: 'Verification', description: 'Runs code ...@@ -35,13 +34,13 @@ task checkstyle(type: Checkstyle, group: 'Verification', description: 'Runs code
} }
} }
classpath = files( ) classpath = files()
} }
task findbugs(type: FindBugs, task findbugs(type: FindBugs,
group: 'Verification', group: 'Verification',
description: 'Inspect java bytecode for bugs', description: 'Inspect java bytecode for bugs',
dependsOn: ['compileDebugSources','compileReleaseSources']) { dependsOn: ['compileDebugSources', 'compileReleaseSources']) {
ignoreFailures = false ignoreFailures = false
effort = "max" effort = "max"
......
# Project-wide Gradle settings. # Project-wide Gradle settings.
# IDE (e.g. Android Studio) users: # IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override* # Gradle settings configured through the IDE *will override*
# any settings specified in this file. # any settings specified in this file.
# For more details on how to configure your build environment visit # For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html # http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process. # Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings. # The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m org.gradle.jvmargs=-Xmx1536m
# When configured, Gradle will run in incubating parallel mode. # When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit # This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
......
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