class SyncAsyncTask extends MessengerAsyncTask<Void, Void, Void> {

  private static final String TAG_TIME = App.newSubTag(App.TAG_TIME, "Async");

  @Nonnull private final List<SyncTask> syncTasks;

  public SyncAsyncTask(@Nonnull List<SyncTask> syncTasks) {
    super(true);
    this.syncTasks = syncTasks;
  }

  @Override
  protected Void doWork(@Nonnull List<Void> voids) {
    for (Account account : getAccountService().getEnabledAccounts()) {
      final SyncData syncData = new SyncDataImpl(account.getId());

      for (SyncTask syncTask : syncTasks) {
        final long start = currentTimeMillis();
        syncTask.doTask(syncData);
        final long end = currentTimeMillis();
        final long duration = end - start;
        if (duration > 1000) {
          Log.e(
              TAG_TIME,
              "Work time is too long for account: "
                  + account
                  + " and task: "
                  + syncTask
                  + ". Time: "
                  + duration
                  + "ms");
        }
      }
    }

    return null;
  }

  @Override
  protected void onSuccessPostExecute(@Nullable Void result) {}

  @Override
  public String toString() {
    return "SyncAsyncTask{" + "syncTasks=" + syncTasks + '}';
  }
}
@Singleton
public class VkRealm extends AbstractRealm<VkAccountConfiguration> {

  /*
   **********************************************************************
   *
   *                           CONSTANTS
   *
   **********************************************************************
   */

  @Nonnull private static final String REALM_ID = "vk";

  @Nonnull public static final String TAG = App.newTag("VK");

  /*
   **********************************************************************
   *
   *                           AUTO INJECTED FIELDS
   *
   **********************************************************************
   */

  @Inject @Nonnull private Application context;

  @Inject @Nonnull private ImageLoader imageLoader;

  @Inject @Nonnull private NotificationService notificationService;

  /*
   **********************************************************************
   *
   *                           FIELDS
   *
   **********************************************************************
   */

  @Nonnull private final HttpRealmIconService.UrlGetter iconUrlGetter = new VkIconUrlGetter();

  @Nonnull private final HttpRealmIconService.UrlGetter photoUrlGetter = new VkPhotoUrlGetter();

  /*@Nonnull*/
  private volatile HttpRealmIconService iconService;

  /*
   **********************************************************************
   *
   *                           CONSTRUCTORS
   *
   **********************************************************************
   */

  public VkRealm() {
    super(
        REALM_ID,
        R.string.mpp_vk_realm_name,
        R.drawable.mpp_vk_icon,
        VkAccountConfigurationFragment.class,
        VkAccountConfiguration.class,
        true,
        null,
        true);
  }

  /*
   **********************************************************************
   *
   *                           METHODS
   *
   **********************************************************************
   */

  @Nonnull
  @Override
  public Account<VkAccountConfiguration> newAccount(
      @Nonnull String accountId,
      @Nonnull User user,
      @Nonnull VkAccountConfiguration configuration,
      @Nonnull AccountState state,
      @Nonnull AccountSyncData syncData) {
    return new VkAccount(accountId, this, user, configuration, state, syncData);
  }

  @Nonnull
  @Override
  public AccountBuilder newAccountBuilder(
      @Nonnull VkAccountConfiguration configuration, @Nullable Account editedAccount) {
    return new VkAccountBuilder(this, (VkAccount) editedAccount, configuration);
  }

  @Nonnull
  @Override
  public List<AProperty> getUserDisplayProperties(@Nonnull User user, @Nonnull Context context) {
    final List<AProperty> result = super.getUserDisplayProperties(user, context);

    final String bdate = user.getPropertyValueByName("bdate");
    if (!isEmpty(bdate)) {
      final String birthDate = formatBirthDate(bdate);
      if (birthDate != null) {
        result.add(newProperty(context.getString(R.string.mpp_birth_date), birthDate));
      }
    }

    return result;
  }

  @Nullable
  private String formatBirthDate(@Nonnull String value) {
    int dateParts = 1;
    for (int i = 0; i < value.length(); i++) {
      if (value.charAt(i) == '.') {
        dateParts++;
      }
    }

    if (dateParts > 1) {
      final SimpleDateFormat dt;
      if (dateParts > 2) {
        dt = new SimpleDateFormat("dd.MM.yyyy");
      } else {
        dt = new SimpleDateFormat("dd.MM");
      }
      try {
        final DateFormat df;
        if (dateParts > 2) {
          df = SimpleDateFormat.getDateInstance();
        } else {
          df = new SimpleDateFormat("dd.MM");
        }
        return df.format(dt.parse(value));
      } catch (ParseException e) {
        return null;
      }
    } else {
      return null;
    }
  }

  @Override
  public void init(@Nonnull Context context) {
    super.init(context);
  }

  @Nonnull
  @Override
  public synchronized RealmIconService getRealmIconService() {
    if (iconService == null) {
      iconService =
          new HttpRealmIconService(
              context,
              imageLoader,
              R.drawable.mpp_icon_user,
              R.drawable.mpp_icon_users,
              iconUrlGetter,
              photoUrlGetter);
    }
    return iconService;
  }

  @Nullable
  @Override
  public Cipherer<VkAccountConfiguration, VkAccountConfiguration> getCipherer() {
    return new VkRealmConfigurationCipherer(
        App.getSecurityService().getStringSecurityService().getCipherer());
  }

  @Override
  public boolean handleException(@Nonnull Throwable e, @Nonnull Account account) {
    boolean handled = super.handleException(e, account);
    if (!handled) {
      if (e instanceof VkResponseErrorException) {
        final VkResponseErrorException cause = (VkResponseErrorException) e;
        if ("5".equals(cause.getError().getErrorId())) {
          notificationService.add(
              newNotification(R.string.mpp_vk_notification_auth_token_expired, MessageType.error)
                  .solvedBy(newOpenAccountConfSolution(account)));
        } else {
          notificationService.add(newVkNotification(cause));
        }

        handled = true;
      }
    }
    return handled;
  }

  @Nonnull
  private static Notification newVkNotification(@Nonnull VkResponseErrorException e) {
    return Notifications.newNotification(
            R.string.mpp_vk_notification_error,
            MessageType.error,
            e.getError().getErrorDescription())
        .causedBy(e);
  }

  @Override
  public boolean isHtmlMessage() {
    return true;
  }

  /*
   **********************************************************************
   *
   *                           STATIC
   *
   **********************************************************************
   */

  private static final class VkPhotoUrlGetter implements HttpRealmIconService.UrlGetter {

    @Nullable
    @Override
    public String getUrl(@Nonnull User user) {
      String result = user.getPropertyValueByName("photoRec");

      if (result == null) {
        result = user.getPropertyValueByName("photoBig");
      }

      if (result == null) {
        result = user.getPropertyValueByName("photo");
      }

      return result;
    }
  }

  private static final class VkIconUrlGetter implements HttpRealmIconService.UrlGetter {

    @Nullable
    @Override
    public String getUrl(@Nonnull User user) {
      String result = user.getPropertyValueByName("photo");

      if (result == null) {
        result = user.getPropertyValueByName("photoRec");
      }

      if (result == null) {
        result = user.getPropertyValueByName("photoBig");
      }

      return result;
    }
  }

  private static class VkRealmConfigurationCipherer
      implements Cipherer<VkAccountConfiguration, VkAccountConfiguration> {

    @Nonnull private final Cipherer<String, String> stringCipherer;

    private VkRealmConfigurationCipherer(@Nonnull Cipherer<String, String> stringCipherer) {
      this.stringCipherer = stringCipherer;
    }

    @Nonnull
    public VkAccountConfiguration encrypt(
        @Nonnull SecretKey secret, @Nonnull VkAccountConfiguration decrypted)
        throws CiphererException {
      final VkAccountConfiguration encrypted = decrypted.clone();
      encrypted.setAccessParameters(
          stringCipherer.encrypt(secret, decrypted.getAccessToken()), decrypted.getUserId());
      return encrypted;
    }

    @Nonnull
    public VkAccountConfiguration decrypt(
        @Nonnull SecretKey secret, @Nonnull VkAccountConfiguration encrypted)
        throws CiphererException {
      final VkAccountConfiguration decrypted = encrypted.clone();
      decrypted.setAccessParameters(
          stringCipherer.decrypt(secret, encrypted.getAccessToken()), encrypted.getUserId());
      return decrypted;
    }
  }

  private static class AuthTokenExpiredSolver implements Runnable {
    @Override
    public void run() {}
  }
}
 @Override
 public Account call() throws InvalidCredentialsException, AccountAlreadyExistsException {
   return App.getAccountService().saveAccount(accountBuilder);
 }
 @Nullable
 @Override
 public Cipherer<VkAccountConfiguration, VkAccountConfiguration> getCipherer() {
   return new VkRealmConfigurationCipherer(
       App.getSecurityService().getStringSecurityService().getCipherer());
 }