RocketChatService.java 4.31 KB
Newer Older
1 2 3 4 5
package chat.rocket.android.service;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
6 7
import android.content.ServiceConnection;
import android.os.Binder;
8 9
import android.os.IBinder;
import android.support.annotation.Nullable;
Yusuke Iwaki's avatar
Yusuke Iwaki committed
10 11

import java.util.HashMap;
12
import java.util.concurrent.Semaphore;
13
import java.util.concurrent.TimeUnit;
14 15 16

import chat.rocket.android.activity.MainActivity;
import chat.rocket.persistence.realm.RealmStore;
Yusuke Iwaki's avatar
Yusuke Iwaki committed
17 18
import hugo.weaving.DebugLog;
import rx.Observable;
19
import rx.Single;
20

21 22 23
/**
 * Background service for Rocket.Chat.Application class.
 */
24
public class RocketChatService extends Service implements ConnectivityServiceInterface {
25

26
  private ConnectivityManagerInternal connectivityManager;
Yusuke Iwaki's avatar
Yusuke Iwaki committed
27
  private HashMap<String, RocketChatWebSocketThread> webSocketThreads;
28
  private Semaphore webSocketThreadLock = new Semaphore(1);
29 30 31 32 33 34 35 36

  public class LocalBinder extends Binder {
    ConnectivityServiceInterface getServiceInterface() {
      return RocketChatService.this;
    }
  }

  private final LocalBinder localBinder = new LocalBinder();
37

38 39 40
  /**
   * ensure RocketChatService alive.
   */
Yusuke Iwaki's avatar
Yusuke Iwaki committed
41
  /*package*/ static void keepAlive(Context context) {
42 43
    context.startService(new Intent(context, RocketChatService.class));
  }
44

45 46 47 48 49 50 51 52 53
  public static void bind(Context context, ServiceConnection serviceConnection) {
    context.bindService(
        new Intent(context, RocketChatService.class), serviceConnection, Context.BIND_AUTO_CREATE);
  }

  public static void unbind(Context context, ServiceConnection serviceConnection) {
    context.unbindService(serviceConnection);
  }

54
  @DebugLog
Yusuke Iwaki's avatar
Yusuke Iwaki committed
55 56
  @Override
  public void onCreate() {
57
    super.onCreate();
58 59 60
    connectivityManager = ConnectivityManager.getInstanceForInternal(getApplicationContext());
    connectivityManager.resetConnectivityStateList();
    webSocketThreads = new HashMap<>();
61
  }
62

63
  @DebugLog
Yusuke Iwaki's avatar
Yusuke Iwaki committed
64 65
  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
66
    connectivityManager.ensureConnections();
67
    return START_NOT_STICKY;
68
  }
69

70
  @Override
Yusuke Iwaki's avatar
Yusuke Iwaki committed
71
  public Single<Boolean> ensureConnectionToServer(String hostname) { //called via binder.
72 73 74 75 76
    return getOrCreateWebSocketThread(hostname)
        .doOnError(err -> {
          webSocketThreads.remove(hostname);
          connectivityManager.notifyConnectionLost(hostname, ConnectivityManagerInternal.REASON_NETWORK_ERROR);
        })
Yusuke Iwaki's avatar
Yusuke Iwaki committed
77
        .flatMap(webSocketThreads -> webSocketThreads.keepAlive());
Yusuke Iwaki's avatar
Yusuke Iwaki committed
78
  }
79

80
  @Override
Yusuke Iwaki's avatar
Yusuke Iwaki committed
81
  public Single<Boolean> disconnectFromServer(String hostname) { //called via binder.
82
    return Single.defer(() -> {
Yusuke Iwaki's avatar
Yusuke Iwaki committed
83 84 85
      if (!webSocketThreads.containsKey(hostname)) {
        return Single.just(true);
      }
86

87 88
      RocketChatWebSocketThread thread = webSocketThreads.get(hostname);
      if (thread != null) {
89
        return thread.terminate()
90 91 92 93 94 95 96 97 98 99 100 101 102
            // after disconnection from server
            .doAfterTerminate(() -> {
              // remove RCWebSocket key from HashMap
              webSocketThreads.remove(hostname);
              // remove RealmConfiguration key from HashMap
              RealmStore.sStore.remove(hostname);
              // clear "cache" SharedPreference
              this.getSharedPreferences("cache", 0).edit().clear().apply();
              // start a fresh new MainActivity
              Intent intent = new Intent(this, MainActivity.class);
              intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
              this.startActivity(intent);
            });
103
      } else {
Yusuke Iwaki's avatar
Yusuke Iwaki committed
104 105
        return Observable.timer(1, TimeUnit.SECONDS).toSingle()
            .flatMap(_val -> disconnectFromServer(hostname));
106
      }
Yusuke Iwaki's avatar
Yusuke Iwaki committed
107 108 109 110 111
    });
  }

  @DebugLog
  private Single<RocketChatWebSocketThread> getOrCreateWebSocketThread(String hostname) {
112
    return Single.defer(() -> {
113
      webSocketThreadLock.acquire();
Yusuke Iwaki's avatar
Yusuke Iwaki committed
114 115
      if (webSocketThreads.containsKey(hostname)) {
        RocketChatWebSocketThread thread = webSocketThreads.get(hostname);
116
        webSocketThreadLock.release();
117
        return Single.just(thread);
Yusuke Iwaki's avatar
Yusuke Iwaki committed
118 119
      }
      return RocketChatWebSocketThread.getStarted(getApplicationContext(), hostname)
120 121 122 123
          .doOnSuccess(thread -> {
            webSocketThreads.put(hostname, thread);
            webSocketThreadLock.release();
          });
Yusuke Iwaki's avatar
Yusuke Iwaki committed
124
    });
125 126
  }

Yusuke Iwaki's avatar
Yusuke Iwaki committed
127 128 129
  @Nullable
  @Override
  public IBinder onBind(Intent intent) {
130
    return localBinder;
131
  }
132
}