RocketChatService.java 4.65 KB
Newer Older
1 2 3 4 5 6 7 8
package chat.rocket.android.service;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import bolts.Task;
9
import chat.rocket.android.helper.LogcatIfError;
10
import chat.rocket.android.model.ServerConfig;
11 12 13
import chat.rocket.android.realm_helper.RealmHelper;
import chat.rocket.android.realm_helper.RealmListObserver;
import chat.rocket.android.realm_helper.RealmStore;
14
import io.realm.RealmResults;
15 16
import java.util.HashMap;
import java.util.List;
17

18 19 20
/**
 * Background service for Rocket.Chat.Application class.
 */
21 22
public class RocketChatService extends Service {

23
  private RealmHelper realmHelper;
Yusuke Iwaki's avatar
Yusuke Iwaki committed
24
  private HashMap<String, RocketChatWebSocketThread> webSocketThreads;
25
  private RealmListObserver<ServerConfig> connectionRequiredServerConfigObserver;
26

27 28 29 30 31 32
  /**
   * ensure RocketChatService alive.
   */
  public static void keepalive(Context context) {
    context.startService(new Intent(context, RocketChatService.class));
  }
33

34 35
  @Override public void onCreate() {
    super.onCreate();
Yusuke Iwaki's avatar
Yusuke Iwaki committed
36
    webSocketThreads = new HashMap<>();
37 38 39 40 41 42
    realmHelper = RealmStore.getDefault();
    connectionRequiredServerConfigObserver = realmHelper
            .createListObserver(realm -> realm.where(ServerConfig.class)
                .isNotNull("hostname")
                .equalTo("state", ServerConfig.STATE_READY)
                .findAll())
Yusuke Iwaki's avatar
Yusuke Iwaki committed
43
            .setOnUpdateListener(this::connectToServerWithServerConfig);
44

45 46 47 48 49 50 51
    refreshServerConfigState();
  }

  private void refreshServerConfigState() {
    realmHelper.executeTransaction(realm -> {
      RealmResults<ServerConfig> configs = realm.where(ServerConfig.class).findAll();
      for (ServerConfig config: configs) {
Yusuke Iwaki's avatar
Yusuke Iwaki committed
52 53 54
        if (config.getState() != ServerConfig.STATE_READY) {
          config.setState(ServerConfig.STATE_READY);
        }
55 56 57
      }
      return null;
    }).continueWith(new LogcatIfError());;
58
  }
59

60
  @Override public int onStartCommand(Intent intent, int flags, int startId) {
Yusuke Iwaki's avatar
Yusuke Iwaki committed
61 62 63 64 65 66 67 68 69 70 71 72 73 74
    List<ServerConfig> configs = realmHelper.executeTransactionForReadResults(realm ->
        realm.where(ServerConfig.class)
            .equalTo("state", ServerConfig.STATE_CONNECTED)
            .findAll());
    for (ServerConfig config: configs) {
      String serverConfigId = config.getServerConfigId();
      if (webSocketThreads.containsKey(serverConfigId)) {
        RocketChatWebSocketThread thread = webSocketThreads.get(serverConfigId);
        if (thread != null) {
          thread.keepalive();
        }
      }
    }

75 76 77
    realmHelper.executeTransaction(realm -> {
      RealmResults<ServerConfig> targetConfigs = realm
          .where(ServerConfig.class)
Yusuke Iwaki's avatar
Yusuke Iwaki committed
78
          .beginGroup()
79
          .equalTo("state", ServerConfig.STATE_CONNECTION_ERROR)
Yusuke Iwaki's avatar
Yusuke Iwaki committed
80 81 82
          .or()
          .isNotNull("error")
          .endGroup()
83
          .isNotNull("session")
84 85
          .findAll();
      for (ServerConfig config : targetConfigs) {
86 87
        config.setState(ServerConfig.STATE_READY);
        config.setError(null);
88 89 90 91 92 93
      }
      return null;
    }).onSuccessTask(task -> {
      connectionRequiredServerConfigObserver.keepalive();
      return null;
    });
94 95
    return START_STICKY;
  }
96

Yusuke Iwaki's avatar
Yusuke Iwaki committed
97 98 99 100
  private void connectToServerWithServerConfig(List<ServerConfig> configList) {
    if (configList.isEmpty()) {
      return;
    }
101

Yusuke Iwaki's avatar
Yusuke Iwaki committed
102 103 104 105 106
    ServerConfig config = configList.get(0);
    createWebSocketThread(config).onSuccess(task -> {
      RocketChatWebSocketThread thread = task.getResult();
      if (thread != null) {
        thread.keepalive();
107
      }
Yusuke Iwaki's avatar
Yusuke Iwaki committed
108 109 110
      return null;
    });
  }
111

Yusuke Iwaki's avatar
Yusuke Iwaki committed
112 113 114 115 116 117 118 119 120 121 122 123 124
  private Task<RocketChatWebSocketThread> createWebSocketThread(final ServerConfig config) {
    final String serverConfigId = config.getServerConfigId();
    webSocketThreads.put(serverConfigId, null);
    return ServerConfig.updateState(serverConfigId, ServerConfig.STATE_CONNECTING)
        .onSuccessTask(_task ->
            RocketChatWebSocketThread.getStarted(getApplicationContext(), config))
        .onSuccessTask(task ->
            ServerConfig.updateState(serverConfigId, ServerConfig.STATE_CONNECTED)
                .onSuccessTask(_task -> task))
        .onSuccessTask(task -> {
          webSocketThreads.put(serverConfigId, task.getResult());
          return task;
        });
125
  }
126

127
  private Task<RocketChatWebSocketThread> findOrCreateWebSocketThread(final ServerConfig config) {
Yusuke Iwaki's avatar
Yusuke Iwaki committed
128
    final String serverConfigId = config.getServerConfigId();
Yusuke Iwaki's avatar
Yusuke Iwaki committed
129
    if (webSocketThreads.containsKey(serverConfigId)) {
Yusuke Iwaki's avatar
Yusuke Iwaki committed
130
      return Task.forResult(webSocketThreads.get(serverConfigId));
131
    } else {
Yusuke Iwaki's avatar
Yusuke Iwaki committed
132
      return createWebSocketThread(config);
133
    }
134 135
  }

Yusuke Iwaki's avatar
Yusuke Iwaki committed
136 137 138
  @Nullable
  @Override
  public IBinder onBind(Intent intent) {
139 140
    return null;
  }
141
}