Ejemplo n.º 1
0
  private synchronized void trim() {
    for (int i = 0; i < 3; ++i) {
      if (maxSize == 0 || size <= maxSize) return;

      LinkedList<String> files = database.getFilesForTrim(size - maxSize);

      while (size > maxSize) {
        File oldest = null;
        for (ListIterator<String> it = files.listIterator(); it.hasNext(); ) {
          File file = pathToFile(it.next());
          if (isPageFile(file) && pagesSize < maxPagesSize) continue;
          it.remove();
          oldest = file;
          break;
        }
        if (oldest == null) {
          Logger.e(TAG, "No files to trim");
          break;
        } else {
          Logger.d(TAG, "Deleting " + oldest.getPath());
          if (!delete(oldest)) {
            Logger.e(TAG, "Cannot delete cache file: " + oldest.getPath());
            break;
          }
        }
      }
    }
  }
Ejemplo n.º 2
0
 private void makeDir() {
   if (!directory.exists()) {
     if (!directory.mkdirs()) {
       Logger.e(TAG, "Unable to create file cache dir " + directory.getPath());
     }
   }
 }
Ejemplo n.º 3
0
 private void makeNomedia() {
   try {
     pathToFile(NOMEDIA).createNewFile();
   } catch (Exception e) {
     Logger.e(TAG, "couldn't create .nomedia file", e);
   }
 }
  private View instantiate(String name, Context context, AttributeSet attrs) {
    try {
      Constructor<? extends View> constructor = CONSTRUCTOR_MAP.get(name);
      if (constructor == null) {
        Class<? extends View> clazz = null;
        if (name.indexOf('.') != -1) {
          clazz = context.getClassLoader().loadClass(name).asSubclass(View.class);
        } else {
          for (String prefix : CLASS_PREFIX_LIST) {
            try {
              clazz = context.getClassLoader().loadClass(prefix + name).asSubclass(View.class);
              break;
            } catch (ClassNotFoundException e) {
            }
          }
          if (clazz == null) throw new ClassNotFoundException("couldn't find class: " + name);
        }
        constructor = clazz.getConstructor(CONSTRUCTOR_SIGNATURE);
        CONSTRUCTOR_MAP.put(name, constructor);
      }

      Object[] args = constructorArgs;
      args[0] = context;
      args[1] = attrs;

      constructor.setAccessible(true);
      View view = constructor.newInstance(args);
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && view instanceof ViewStub)
        CompatibilityImpl.setLayoutInflater((ViewStub) view, inflater.cloneInContext(context));
      return view;
    } catch (Exception e) {
      Logger.e(TAG, "couldn't instantiate class " + name, e);
      return null;
    }
  }
  public static void setCustomTheme(Context context, SparseIntArray customAttrs) {
    if (customAttrs == null || customAttrs.size() == 0) {
      currentInstance = null;
      return;
    }

    TypedValue tmp = new TypedValue();
    context.getTheme().resolveAttribute(android.R.attr.textColorPrimary, tmp, true);
    int textColorPrimaryOriginal =
        (tmp.type >= TypedValue.TYPE_FIRST_COLOR_INT && tmp.type <= TypedValue.TYPE_LAST_COLOR_INT)
            ? tmp.data
            : Color.TRANSPARENT;
    int textColorPrimaryOverridden =
        customAttrs.get(android.R.attr.textColorPrimary, textColorPrimaryOriginal);

    try {
      processWindow(context, customAttrs, textColorPrimaryOriginal, textColorPrimaryOverridden);
    } catch (Exception e) {
      Logger.e(TAG, e);
    }

    currentInstance =
        new CustomThemeHelper(
            context, customAttrs, textColorPrimaryOriginal, textColorPrimaryOverridden);
  }
Ejemplo n.º 6
0
 public SendPostModel deserializeDraft(String hash) {
   try {
     return deserialize(FileCache.PREFIX_DRAFTS + hash, SendPostModel.class);
   } catch (Exception e) {
     Logger.e(TAG, e);
     return null;
   }
 }
Ejemplo n.º 7
0
 public SerializableBoardsList deserializeBoardsList(String hash) {
   try {
     return deserialize(FileCache.PREFIX_BOARDS + hash, SerializableBoardsList.class);
   } catch (Exception e) {
     Logger.e(TAG, e);
     return null;
   }
 }
Ejemplo n.º 8
0
 public SerializablePage deserializePage(String hash) {
   try {
     return deserialize(FileCache.PREFIX_PAGES + hash, SerializablePage.class);
   } catch (Exception e) {
     Logger.e(TAG, e);
     return null;
   }
 }
Ejemplo n.º 9
0
 private void ensureInitialized() {
   if (initialized) return;
   synchronized (initLock) {
     while (!initialized) {
       try {
         initLock.wait();
       } catch (Exception e) {
         Logger.e(TAG, e);
       }
     }
   }
 }
Ejemplo n.º 10
0
  private <T> T deserialize(File file, Class<T> type) {
    if (file == null || !file.exists()) {
      return null;
    }

    synchronized (serializeLock) {
      Input input = null;
      try {
        input = new Input(new FileInputStream(file));
        return kryo.readObject(input, type);
      } catch (Exception e) {
        Logger.e(TAG, e);
      } catch (OutOfMemoryError oom) {
        MainApplication.freeMemory();
        Logger.e(TAG, oom);
      } finally {
        IOUtils.closeQuietly(input);
      }
    }

    return null;
  }
Ejemplo n.º 11
0
 @Override
 public void run() {
   synchronized (serializeLock) {
     File file = fileCache.create(filename);
     Output output = null;
     try {
       output =
           isHoneycomb()
               ? new KryoOutputHC(new FileOutputStream(file))
               : new Output(new FileOutputStream(file));
       kryo.writeObject(output, obj);
     } catch (Exception e) {
       Logger.e(TAG, e);
     } catch (OutOfMemoryError oom) {
       MainApplication.freeMemory();
       Logger.e(TAG, oom);
     } finally {
       IOUtils.closeQuietly(output);
     }
     fileCache.put(file);
   }
 }
Ejemplo n.º 12
0
  public TabsState deserializeTabsState() {
    File file1 = fileCache.getImmediately(FileCache.TABS_FILENAME);
    File file2 = fileCache.getImmediately(FileCache.TABS_FILENAME_2);
    File[] files;
    if (file1 == null && file2 == null) files = new File[0];
    else if (file1 == null) files = new File[] {file2};
    else if (file2 == null) files = new File[] {file1};
    else
      files =
          file1.lastModified() > file2.lastModified()
              ? new File[] {file2, file1}
              : new File[] {file1, file2};

    for (File file : files) {
      try {
        TabsState obj = deserialize(file, TabsState.class);
        if (obj != null && obj.tabsArray != null && obj.tabsIdStack != null) return obj;
      } catch (Exception e) {
        Logger.e(TAG, e);
      }
    }
    return TabsState.obtainDefault();
  }
  private static void setLollipopMenuOverflowIconColor(final ViewGroup toolbar, final int color) {
    try {
      // for API 23 (Android 6): at this point method Toolbar.setOverflowIcon(Drawable) has no
      // effect
      toolbar.getClass().getMethod("getMenu").invoke(toolbar);
      AppearanceUtils.callWhenLoaded(
          toolbar,
          new Runnable() {
            @Override
            public void run() {
              try {
                final ViewGroup actionMenuView =
                    (ViewGroup) findViewByClassName(toolbar, "android.widget.ActionMenuView");

                Runnable setOverflowIcon =
                    new Runnable() {
                      @Override
                      public void run() {
                        try {
                          Class<?> toolbarClass = toolbar.getClass();
                          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                            Drawable overflowIcon =
                                (Drawable)
                                    toolbarClass.getMethod("getOverflowIcon").invoke(toolbar);
                            setColorFilter(overflowIcon);
                            toolbarClass
                                .getMethod("setOverflowIcon", Drawable.class)
                                .invoke(toolbar, overflowIcon);
                          } else {
                            ImageView overflowButton =
                                (ImageView)
                                    findViewByClassName(
                                        actionMenuView,
                                        "android.widget.ActionMenuPresenter$OverflowMenuButton");
                            if (overflowButton != null) {
                              Drawable overflowIcon = overflowButton.getDrawable();
                              setColorFilter(overflowIcon);
                              overflowButton.setImageDrawable(overflowIcon);
                            }
                          }
                        } catch (Exception e) {
                          Logger.e(TAG, e);
                        }
                      }

                      private void setColorFilter(Drawable overflowIcon) {
                        overflowIcon.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
                      }
                    };

                if (actionMenuView.getChildCount() == 0) {
                  AppearanceUtils.callWhenLoaded(
                      actionMenuView == null ? toolbar : actionMenuView, setOverflowIcon);
                } else {
                  setOverflowIcon.run();
                }
              } catch (Exception e) {
                Logger.e(TAG, e);
              }
            }

            private View findViewByClassName(ViewGroup group, String className) {
              for (int i = 0, size = group.getChildCount(); i < size; ++i) {
                View child = group.getChildAt(i);
                if (child.getClass().getName().equals(className)) {
                  return child;
                }
              }
              return null;
            }
          });
    } catch (Exception e) {
      Logger.e(TAG, e);
    }
  }
  private static void processWindow(
      Context context,
      SparseIntArray attrs,
      int textColorPrimaryOriginal,
      int textColorPrimaryOverridden) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) return;
    boolean isLollipop = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;

    int materialPrimaryIndex = attrs.indexOfKey(R.attr.materialPrimary);
    int materialPrimaryDarkIndex = attrs.indexOfKey(R.attr.materialPrimaryDark);
    int materialNavigationBarIndex = attrs.indexOfKey(R.attr.materialNavigationBar);
    boolean overrideActionbarColor = materialPrimaryIndex >= 0;
    boolean overridePanelsColor =
        Math.max(materialPrimaryDarkIndex, materialNavigationBarIndex) >= 0;

    boolean overrideTextColor = textColorPrimaryOriginal != textColorPrimaryOverridden;
    if (!overrideTextColor && !overrideActionbarColor && !overridePanelsColor) return;

    Window window = ((Activity) context).getWindow();
    final View decorView = window.getDecorView();
    Resources resources = context.getResources();

    if (overrideActionbarColor) {
      try {
        Drawable background = new ColorDrawable(attrs.valueAt(materialPrimaryIndex));
        Object actionBar = context.getClass().getMethod("getActionBar").invoke(context);
        actionBar
            .getClass()
            .getMethod("setBackgroundDrawable", Drawable.class)
            .invoke(actionBar, background);
      } catch (Exception e) {
        Logger.e(TAG, e);
      }
    }

    if (overrideTextColor) {
      int id = resources.getIdentifier("action_bar_title", "id", "android");
      if (id != 0) {
        View v = decorView.findViewById(id);
        if (v instanceof TextView) ((TextView) v).setTextColor(textColorPrimaryOverridden);
      }
    }

    if (isLollipop && overrideTextColor) {
      try {
        int id = resources.getIdentifier("action_bar", "id", "android");
        if (id == 0) throw new Exception("'android:id/action_bar' identifier not found");
        View v = decorView.findViewById(id);
        if (v == null) throw new Exception("view with id 'android:id/action_bar' not found");
        Class<?> toolbarClass = Class.forName("android.widget.Toolbar");
        if (!toolbarClass.isInstance(v))
          throw new Exception(
              "view 'android:id/action_bar' is not instance of android.widget.Toolbar");
        toolbarClass
            .getMethod("setTitleTextColor", int.class)
            .invoke(v, textColorPrimaryOverridden);
        setLollipopMenuOverflowIconColor((ViewGroup) v, textColorPrimaryOverridden);
      } catch (Exception e) {
        Logger.e(TAG, e);
      }
    }

    if (isLollipop && overridePanelsColor) {
      try {
        if (materialPrimaryDarkIndex >= 0) {
          window
              .getClass()
              .getMethod("setStatusBarColor", int.class)
              .invoke(window, attrs.valueAt(materialPrimaryDarkIndex));
        }
        if (materialNavigationBarIndex >= 0) {
          window
              .getClass()
              .getMethod("setNavigationBarColor", int.class)
              .invoke(window, attrs.valueAt(materialNavigationBarIndex));
        }
      } catch (Exception e) {
        Logger.e(TAG, e);
      }
    }
  }
  @Override
  public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
    int mappingCount = 0;
    if (attrs != null) {
      for (int i = 0, size = attrs.getAttributeCount(); i < size; ++i) {
        String value = attrs.getAttributeValue(i);
        if (!value.startsWith("?")) continue;
        int attrId =
            resources.getIdentifier(
                value.substring(1), null, null); // Integer.parseInt(value.substring(1));
        if (attrId == 0) {
          Logger.e(TAG, "couldn't get id for attribute: " + value);
          continue;
        }
        int index = customAttrs.indexOfKey(attrId);
        if (index >= 0) {
          mappingKeys[mappingCount] = attrs.getAttributeNameResource(i);
          mappingValues[mappingCount] = customAttrs.valueAt(index);
          ++mappingCount;
        }
      }
    }

    if (mappingCount == 0 && textColorPrimaryOverridden == textColorPrimaryOriginal) return null;

    View view = instantiate(name, context, attrs);
    if (view == null) return null;

    boolean shouldOverrideTextColor =
        textColorPrimaryOverridden != textColorPrimaryOriginal && view instanceof TextView;
    for (int i = 0; i < mappingCount; ++i) {
      switch (mappingKeys[i]) {
        case android.R.attr.background:
          view.setBackgroundColor(mappingValues[i]);
          break;
        case android.R.attr.textColor:
          if (view instanceof TextView) {
            ((TextView) view).setTextColor(mappingValues[i]);
            shouldOverrideTextColor = false;
          } else {
            Logger.e(
                TAG,
                "couldn't apply attribute 'textColor' on class "
                    + name
                    + " (not instance of TextView)");
          }
          break;
        case android.R.attr.divider:
          if (view instanceof ListView) {
            ListView listView = (ListView) view;
            int dividerHeight = listView.getDividerHeight();
            listView.setDivider(new ColorDrawable(mappingValues[i]));
            listView.setDividerHeight(dividerHeight);
          } else {
            Logger.e(
                TAG,
                "couldn't apply attribute 'divider' on class "
                    + name
                    + " (not instance of ListView)");
          }
          break;
        default:
          String attrResName = null;
          try {
            attrResName = resources.getResourceName(mappingKeys[i]);
          } catch (Exception e) {
            attrResName = Integer.toString(mappingKeys[i]);
          }
          Logger.e(TAG, "couldn't apply attribure '" + attrResName + "' on class " + name);
      }
    }

    if (shouldOverrideTextColor) {
      TextView tv = (TextView) view;
      if (tv.getCurrentTextColor() == textColorPrimaryOriginal) {
        tv.setTextColor(textColorPrimaryOverridden);
      }
    }

    return view;
  }
  @Override
  public void handle(final Activity activity, final CancellableTask task, final Callback callback) {
    try {
      final HttpClient httpClient =
          MainApplication.getInstance().getChanModule(chanName).getHttpClient();
      final String usingURL = scheme + RECAPTCHA_FALLBACK_URL + publicKey;
      Header[] customHeaders = new Header[] {new BasicHeader(HttpHeaders.REFERER, usingURL)};
      String htmlChallenge =
          HttpStreamer.getInstance()
              .getStringFromUrl(
                  usingURL,
                  HttpRequestModel.builder().setGET().setCustomHeaders(customHeaders).build(),
                  httpClient,
                  null,
                  task,
                  false);

      Matcher challengeMatcher =
          Pattern.compile("name=\"c\" value=\"([\\w-]+)").matcher(htmlChallenge);
      if (challengeMatcher.find()) {
        final String challenge = challengeMatcher.group(1);
        HttpResponseModel responseModel =
            HttpStreamer.getInstance()
                .getFromUrl(
                    scheme + RECAPTCHA_IMAGE_URL + challenge + "&k=" + publicKey,
                    HttpRequestModel.builder().setGET().build(),
                    httpClient,
                    null,
                    task);
        try {
          InputStream imageStream = responseModel.stream;
          final Bitmap challengeBitmap = BitmapFactory.decodeStream(imageStream);

          final String message;
          Matcher messageMatcher =
              Pattern.compile("imageselect-message(?:.*?)>(.*?)</div>").matcher(htmlChallenge);
          if (messageMatcher.find()) message = messageMatcher.group(1).replaceAll("<[^>]*>", "");
          else message = null;

          final Bitmap candidateBitmap;
          Matcher candidateMatcher =
              Pattern.compile(
                      "fbc-imageselect-candidates(?:.*?)src=\"data:image/(?:.*?);base64,([^\"]*)\"")
                  .matcher(htmlChallenge);
          if (candidateMatcher.find()) {
            Bitmap bmp = null;
            try {
              byte[] imgData = Base64.decode(candidateMatcher.group(1), Base64.DEFAULT);
              bmp = BitmapFactory.decodeByteArray(imgData, 0, imgData.length);
            } catch (Exception e) {
            }
            candidateBitmap = bmp;
          } else candidateBitmap = null;

          activity.runOnUiThread(
              new Runnable() {
                final int maxX = 3;
                final int maxY = 3;
                final boolean[] isSelected = new boolean[maxX * maxY];

                @SuppressLint("InlinedApi")
                @Override
                public void run() {
                  LinearLayout rootLayout = new LinearLayout(activity);
                  rootLayout.setOrientation(LinearLayout.VERTICAL);

                  if (candidateBitmap != null) {
                    ImageView candidateView = new ImageView(activity);
                    candidateView.setImageBitmap(candidateBitmap);
                    int picSize =
                        (int) (activity.getResources().getDisplayMetrics().density * 50 + 0.5f);
                    candidateView.setLayoutParams(new LinearLayout.LayoutParams(picSize, picSize));
                    candidateView.setScaleType(ImageView.ScaleType.FIT_XY);
                    rootLayout.addView(candidateView);
                  }

                  if (message != null) {
                    TextView textView = new TextView(activity);
                    textView.setText(message);
                    textView.setTextAppearance(activity, android.R.style.TextAppearance);
                    textView.setLayoutParams(
                        new LinearLayout.LayoutParams(
                            LinearLayout.LayoutParams.MATCH_PARENT,
                            LinearLayout.LayoutParams.WRAP_CONTENT));
                    rootLayout.addView(textView);
                  }

                  FrameLayout frame = new FrameLayout(activity);
                  frame.setLayoutParams(
                      new LinearLayout.LayoutParams(
                          LinearLayout.LayoutParams.MATCH_PARENT,
                          LinearLayout.LayoutParams.WRAP_CONTENT));

                  final ImageView imageView = new ImageView(activity);
                  imageView.setLayoutParams(
                      new FrameLayout.LayoutParams(
                          FrameLayout.LayoutParams.MATCH_PARENT,
                          FrameLayout.LayoutParams.MATCH_PARENT));
                  imageView.setScaleType(ImageView.ScaleType.FIT_XY);
                  imageView.setImageBitmap(challengeBitmap);
                  frame.addView(imageView);

                  final LinearLayout selector = new LinearLayout(activity);
                  selector.setLayoutParams(
                      new FrameLayout.LayoutParams(
                          FrameLayout.LayoutParams.MATCH_PARENT,
                          FrameLayout.LayoutParams.MATCH_PARENT));
                  AppearanceUtils.callWhenLoaded(
                      imageView,
                      new Runnable() {
                        @Override
                        public void run() {
                          selector.setLayoutParams(
                              new FrameLayout.LayoutParams(
                                  imageView.getWidth(), imageView.getHeight()));
                        }
                      });
                  selector.setOrientation(LinearLayout.VERTICAL);
                  selector.setWeightSum(maxY);
                  for (int y = 0; y < maxY; ++y) {
                    LinearLayout subSelector = new LinearLayout(activity);
                    subSelector.setLayoutParams(
                        new LinearLayout.LayoutParams(
                            LinearLayout.LayoutParams.MATCH_PARENT, 0, 1f));
                    subSelector.setOrientation(LinearLayout.HORIZONTAL);
                    subSelector.setWeightSum(maxX);
                    for (int x = 0; x < maxX; ++x) {
                      FrameLayout switcher = new FrameLayout(activity);
                      switcher.setLayoutParams(
                          new LinearLayout.LayoutParams(
                              0, LinearLayout.LayoutParams.MATCH_PARENT, 1f));
                      switcher.setTag(new int[] {x, y});
                      switcher.setOnClickListener(
                          new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                              int[] coord = (int[]) v.getTag();
                              int index = coord[1] * maxX + coord[0];
                              isSelected[index] = !isSelected[index];
                              v.setBackgroundColor(
                                  isSelected[index]
                                      ? Color.argb(128, 0, 255, 0)
                                      : Color.TRANSPARENT);
                            }
                          });
                      subSelector.addView(switcher);
                    }
                    selector.addView(subSelector);
                  }

                  frame.addView(selector);
                  rootLayout.addView(frame);

                  Button checkButton = new Button(activity);
                  checkButton.setLayoutParams(
                      new LinearLayout.LayoutParams(
                          LinearLayout.LayoutParams.MATCH_PARENT,
                          LinearLayout.LayoutParams.WRAP_CONTENT));
                  checkButton.setText(android.R.string.ok);
                  rootLayout.addView(checkButton);

                  ScrollView dlgView = new ScrollView(activity);
                  dlgView.addView(rootLayout);

                  final Dialog dialog = new Dialog(activity);
                  dialog.setTitle("Recaptcha");
                  dialog.setContentView(dlgView);
                  dialog.setCanceledOnTouchOutside(false);
                  dialog.setOnCancelListener(
                      new DialogInterface.OnCancelListener() {
                        @Override
                        public void onCancel(DialogInterface dialog) {
                          if (!task.isCancelled()) {
                            callback.onError("Cancelled");
                          }
                        }
                      });
                  dialog
                      .getWindow()
                      .setLayout(
                          ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                  dialog.show();

                  checkButton.setOnClickListener(
                      new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                          dialog.dismiss();
                          if (task.isCancelled()) return;
                          PriorityThreadFactory.LOW_PRIORITY_FACTORY
                              .newThread(
                                  new Runnable() {
                                    @Override
                                    public void run() {
                                      try {
                                        List<NameValuePair> pairs = new ArrayList<NameValuePair>();
                                        pairs.add(new BasicNameValuePair("c", challenge));
                                        for (int i = 0; i < isSelected.length; ++i)
                                          if (isSelected[i])
                                            pairs.add(
                                                new BasicNameValuePair(
                                                    "response", Integer.toString(i)));

                                        HttpRequestModel request =
                                            HttpRequestModel.builder()
                                                .setPOST(
                                                    new UrlEncodedFormEntityHC4(pairs, "UTF-8"))
                                                .setCustomHeaders(
                                                    new Header[] {
                                                      new BasicHeader(HttpHeaders.REFERER, usingURL)
                                                    })
                                                .build();
                                        String response =
                                            HttpStreamer.getInstance()
                                                .getStringFromUrl(
                                                    usingURL,
                                                    request,
                                                    httpClient,
                                                    null,
                                                    task,
                                                    false);
                                        String hash = "";
                                        Matcher matcher =
                                            Pattern.compile(
                                                    "fbc-verification-token(?:.*?)<textarea[^>]*>([^<]*)<",
                                                    Pattern.DOTALL)
                                                .matcher(response);
                                        if (matcher.find()) hash = matcher.group(1);

                                        if (hash.length() > 0) {
                                          Recaptcha2solved.push(publicKey, hash);
                                          activity.runOnUiThread(
                                              new Runnable() {
                                                @Override
                                                public void run() {
                                                  callback.onSuccess();
                                                }
                                              });
                                        } else {
                                          throw new RecaptchaException(
                                              "incorrect answer (hash is empty)");
                                        }
                                      } catch (final Exception e) {
                                        Logger.e(TAG, e);
                                        if (task.isCancelled()) return;
                                        handle(activity, task, callback);
                                      }
                                    }
                                  })
                              .start();
                        }
                      });
                }
              });
        } finally {
          responseModel.release();
        }
      } else throw new Exception("can't parse recaptcha challenge answer");
    } catch (final Exception e) {
      Logger.e(TAG, e);
      if (!task.isCancelled()) {
        activity.runOnUiThread(
            new Runnable() {
              @Override
              public void run() {
                callback.onError(e.getMessage() != null ? e.getMessage() : e.toString());
              }
            });
      }
    }
  }