@Override
 public String toString() {
   return getClass().getSimpleName()
       + '['
       + currencyCode
       + ':'
       + GenericUtils.formatDebugValue(rate)
       + ']';
 }
    @Override
    public String toString() {
      final StringBuilder builder = new StringBuilder();

      builder.append(getClass().getSimpleName());
      builder.append('[');
      builder.append(hasAmount() ? GenericUtils.formatDebugValue(amount) : "null");
      builder.append(',');
      if (script.isSentToAddress() || script.isSentToP2SH())
        builder.append(script.getToAddress(Constants.NETWORK_PARAMETERS));
      else if (script.isSentToRawPubKey())
        for (final byte b : script.getPubKey()) builder.append(String.format("%02x", b));
      else if (script.isSentToMultiSig()) builder.append("multisig");
      else builder.append("unknown");
      builder.append(']');

      return builder.toString();
    }
 public boolean isHttpPaymentRequestUrl() {
   return paymentRequestUrl != null
       && (GenericUtils.startsWithIgnoreCase(paymentRequestUrl, "http:")
           || GenericUtils.startsWithIgnoreCase(paymentRequestUrl, "https:"));
 }
  private static Map<String, ExchangeRate> requestExchangeRates(
      final URL url,
      float dogeBtcConversion,
      final String userAgent,
      final String source,
      final String... fields) {
    final long start = System.currentTimeMillis();

    HttpURLConnection connection = null;
    Reader reader = null;

    try {
      connection = (HttpURLConnection) url.openConnection();

      connection.setInstanceFollowRedirects(false);
      connection.setConnectTimeout(Constants.HTTP_TIMEOUT_MS);
      connection.setReadTimeout(Constants.HTTP_TIMEOUT_MS);
      connection.addRequestProperty("User-Agent", userAgent);
      connection.addRequestProperty("Accept-Encoding", "gzip");
      connection.connect();

      final int responseCode = connection.getResponseCode();
      if (responseCode == HttpURLConnection.HTTP_OK) {
        final String contentEncoding = connection.getContentEncoding();

        InputStream is = new BufferedInputStream(connection.getInputStream(), 1024);
        if ("gzip".equalsIgnoreCase(contentEncoding)) is = new GZIPInputStream(is);

        reader = new InputStreamReader(is, Constants.UTF_8);
        final StringBuilder content = new StringBuilder();
        final long length = Io.copy(reader, content);

        final Map<String, ExchangeRate> rates = new TreeMap<String, ExchangeRate>();

        final JSONObject head = new JSONObject(content.toString());
        for (final Iterator<String> i = head.keys(); i.hasNext(); ) {
          final String currencyCode = i.next();
          if (!"timestamp".equals(currencyCode)) {
            final JSONObject o = head.getJSONObject(currencyCode);

            for (final String field : fields) {
              final String rate = o.optString(field, null);

              if (rate != null) {
                try {
                  BigDecimal btcRate = new BigDecimal(GenericUtils.toNanoCoins(rate, 0));
                  BigInteger dogeRate =
                      btcRate.multiply(BigDecimal.valueOf(dogeBtcConversion)).toBigInteger();

                  if (dogeRate.signum() > 0) {
                    rates.put(currencyCode, new ExchangeRate(currencyCode, dogeRate, source));
                    break;
                  }
                } catch (final ArithmeticException x) {
                  log.warn(
                      "problem fetching {} exchange rate from {} ({}): {}",
                      currencyCode,
                      url,
                      contentEncoding,
                      x.getMessage());
                }
              }
            }
          }
        }

        log.info(
            "fetched exchange rates from {} ({}), {} chars, took {} ms",
            url,
            contentEncoding,
            length,
            System.currentTimeMillis() - start);

        return rates;
      } else {
        log.warn("http status {} when fetching {}", responseCode, url);
      }
    } catch (final Exception x) {
      log.warn("problem fetching exchange rates from " + url, x);
    } finally {
      if (reader != null) {
        try {
          reader.close();
        } catch (final IOException x) {
          // swallow
        }
      }

      if (connection != null) connection.disconnect();
    }

    return null;
  }
  @Override
  public Cursor query(
      final Uri uri,
      final String[] projection,
      final String selection,
      final String[] selectionArgs,
      final String sortOrder) {
    if (Constants.BUG_OPENSSL_HEARTBLEED) return null;

    final long now = System.currentTimeMillis();
    SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getContext());
    int provider = Integer.parseInt(sp.getString(Configuration.PREFS_KEY_EXCHANGE_PROVIDER, "0"));
    boolean forceRefresh = sp.getBoolean(Configuration.PREFS_KEY_EXCHANGE_FORCE_REFRESH, false);
    if (forceRefresh)
      sp.edit().putBoolean(Configuration.PREFS_KEY_EXCHANGE_FORCE_REFRESH, false).commit();

    if (lastUpdated == 0 || now - lastUpdated > UPDATE_FREQ_MS) {
      float newDogeBtcConversion = -1;
      if ((dogeBtcConversion == -1 && newDogeBtcConversion == -1) || forceRefresh)
        newDogeBtcConversion = requestDogeBtcConversion(provider);

      if (newDogeBtcConversion != -1) dogeBtcConversion = newDogeBtcConversion;

      if (dogeBtcConversion == -1) return null;

      Map<String, ExchangeRate> newExchangeRates = null;
      if (newExchangeRates == null)
        newExchangeRates =
            requestExchangeRates(
                BITCOINAVERAGE_URL,
                dogeBtcConversion,
                userAgent,
                BITCOINAVERAGE_SOURCE,
                BITCOINAVERAGE_FIELDS);
      if (newExchangeRates == null)
        newExchangeRates =
            requestExchangeRates(
                BLOCKCHAININFO_URL,
                dogeBtcConversion,
                userAgent,
                BLOCKCHAININFO_SOURCE,
                BLOCKCHAININFO_FIELDS);

      if (newExchangeRates != null) {
        String providerUrl;
        switch (provider) {
          case 0:
            providerUrl = "http://www.cryptsy.com";
            break;
          case 1:
            providerUrl = "http://www.vircurex.com";
            break;
          default:
            providerUrl = "";
            break;
        }
        float mBTCRate = dogeBtcConversion * 1000;
        String strmBTCRate = String.format(Locale.US, "%.5f", mBTCRate).replace(',', '.');
        newExchangeRates.put(
            "mBTC",
            new ExchangeRate(
                "mBTC",
                new BigDecimal(GenericUtils.toNanoCoins(strmBTCRate, 0)).toBigInteger(),
                providerUrl));
        newExchangeRates.put(
            "DOGE", new ExchangeRate("DOGE", BigInteger.valueOf(100000000), "priceofdoge.com"));
        exchangeRates = newExchangeRates;
        lastUpdated = now;

        final ExchangeRate exchangeRateToCache = bestExchangeRate(config.getExchangeCurrencyCode());
        if (exchangeRateToCache != null) config.setCachedExchangeRate(exchangeRateToCache);
      }
    }

    if (exchangeRates == null || dogeBtcConversion == -1) return null;

    final MatrixCursor cursor =
        new MatrixCursor(new String[] {BaseColumns._ID, KEY_CURRENCY_CODE, KEY_RATE, KEY_SOURCE});

    if (selection == null) {
      for (final Map.Entry<String, ExchangeRate> entry : exchangeRates.entrySet()) {
        final ExchangeRate rate = entry.getValue();
        cursor
            .newRow()
            .add(rate.currencyCode.hashCode())
            .add(rate.currencyCode)
            .add(rate.rate.longValue())
            .add(rate.source);
      }
    } else if (selection.equals(KEY_CURRENCY_CODE)) {
      final ExchangeRate rate = bestExchangeRate(selectionArgs[0]);
      if (rate != null)
        cursor
            .newRow()
            .add(rate.currencyCode.hashCode())
            .add(rate.currencyCode)
            .add(rate.rate.longValue())
            .add(rate.source);
    }

    return cursor;
  }