content: /*
   * Copyright (C) 2015 Actor LLC. <https://actor.im>
   */
  
  package im.actor.runtime.android;
  
  import android.content.BroadcastReceiver;
  import android.content.Context;
  import android.content.Intent;
  import android.content.IntentFilter;
  import android.content.SharedPreferences;
  
  import im.actor.runtime.util.ExponentialBackoff;
  import im.actor.runtime.Runtime;
  import im.actor.runtime.actors.ThreadPriority;
  import im.actor.runtime.android.threading.AndroidDispatcher;
  import im.actor.runtime.android.threading.AndroidImmediateDispatcher;
  import im.actor.runtime.android.time.SntpClient;
  import im.actor.runtime.generic.GenericThreadingProvider;
  import im.actor.runtime.Log;
  import im.actor.runtime.threading.Dispatcher;
  import im.actor.runtime.threading.ImmediateDispatcher;
  
  public class AndroidThreadingProvider extends GenericThreadingProvider {
  
      private static final String PROPS = "time_sync.ini";
  
      private SharedPreferences preference;
      private long syncDelta;
      private String serverHost;
  
      public AndroidThreadingProvider() {
          this.serverHost = "europe.pool.ntp.org";
  
          Runtime.dispatch(() -> {
              this.preference = AndroidContext.getContext().getSharedPreferences(PROPS, Context.MODE_PRIVATE);
              this.syncDelta = preference.getLong("delta", syncDelta);
  
              invalidateSync();
  
              IntentFilter timeChangedFilter = new IntentFilter();
              timeChangedFilter.addAction(Intent.ACTION_TIME_CHANGED);
              timeChangedFilter.addAction(Intent.ACTION_DATE_CHANGED);
              AndroidContext.getContext().registerReceiver(new BroadcastReceiver() {
                  @Override
                  public void onReceive(Context context, Intent intent) {
                      Log.d("AndroidClockSync", "Time changed: invalidating sync");
                      invalidateSync();
                  }
              }, timeChangedFilter);
          });
      }
  
      // TODO: Better invalidation
      private void invalidateSync() {
          Log.d("AndroidClockSync", "Starting sync...");
          new Thread() {
              @Override
              public void run() {
                  SntpClient client = new SntpClient();
                  ExponentialBackoff exponentialBackoff = new ExponentialBackoff();
                  while (!client.requestTime(serverHost, 10000)) {
                      exponentialBackoff.onFailure();
                      try {
                          Thread.sleep(exponentialBackoff.exponentialWait());
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
                  syncDelta = client.getClockOffset();
                  preference.edit().putLong("delta", syncDelta).commit();
                  Log.d("AndroidClockSync", "Synced. Time delta: " + syncDelta + " ms");
              }
          }.start();
      }
  
      @Override
      public long getSyncedCurrentTime() {
          return System.currentTimeMillis() + syncDelta;
      }
  
      @Override
      public Dispatcher createDispatcher(String name) {
          return new AndroidDispatcher(name);
      }
  
      @Override
      public ImmediateDispatcher createImmediateDispatcher(String name, ThreadPriority priority) {
          return new AndroidImmediateDispatcher(name, priority);
      }
  }
  