private void updateStatus(final double percentage) {
    long now = System.currentTimeMillis();
    long deltaTime = now - uploadLastTimePoint;
    long currentOffset = (long) (percentage * uploadFileLength);
    long deltaSize = currentOffset - uploadLastOffset;
    if (deltaTime <= 100) {
      return;
    }

    final String speed = Tools.formatSpeed(deltaSize, deltaTime);
    // update
    uploadLastTimePoint = now;
    uploadLastOffset = currentOffset;

    AsyncRun.run(
        new Runnable() {
          @Override
          public void run() {
            int progress = (int) (percentage * 100);
            uploadProgressBar.setProgress(progress);
            uploadPercentageTextView.setText(progress + " %");
            uploadSpeedTextView.setText(speed);
          }
        });
  }
 private void writeLog(final String msg) {
   AsyncRun.run(
       new Runnable() {
         @Override
         public void run() {
           uploadLogTextView.append(msg);
           uploadLogTextView.append("\r\n");
         }
       });
 }
  private void upload(String uploadToken) {
    if (this.uploadManager == null) {
      this.uploadManager = new UploadManager();
    }
    File uploadFile = new File(this.uploadFilePath);
    String uploadXParam = this.uploadXParamEditText.getText().toString();
    Map<String, String> xParams = new HashMap<String, String>();
    xParams.put("x:saveKeyEx", uploadXParam);
    UploadOptions uploadOptions =
        new UploadOptions(
            xParams,
            null,
            false,
            new UpProgressHandler() {
              @Override
              public void progress(String key, double percent) {
                updateStatus(percent);
              }
            },
            null);
    final long startTime = System.currentTimeMillis();
    final long fileLength = uploadFile.length();
    this.uploadFileLength = fileLength;
    this.uploadLastTimePoint = startTime;
    this.uploadLastOffset = 0;
    AsyncRun.run(
        new Runnable() {
          @Override
          public void run() {
            // prepare status
            uploadPercentageTextView.setText("0 %");
            uploadSpeedTextView.setText("0 KB/s");
            uploadFileLengthTextView.setText(Tools.formatSize(fileLength));
            uploadStatusLayout.setVisibility(LinearLayout.VISIBLE);
          }
        });
    writeLog(context.getString(R.string.qiniu_upload_file) + "...");
    this.uploadManager.put(
        uploadFile,
        null,
        uploadToken,
        new UpCompletionHandler() {
          @Override
          public void complete(String key, ResponseInfo respInfo, JSONObject jsonData) {
            AsyncRun.run(
                new Runnable() {
                  @Override
                  public void run() {
                    // reset status
                    uploadStatusLayout.setVisibility(LinearLayout.INVISIBLE);
                    uploadProgressBar.setProgress(0);
                  }
                });
            long lastMillis = System.currentTimeMillis() - startTime;
            if (respInfo.isOK()) {
              try {
                String fileKey = jsonData.getString("key");
                String fileHash = jsonData.getString("hash");
                writeLog("File Size: " + Tools.formatSize(uploadFileLength));
                writeLog("File Key: " + fileKey);
                writeLog("File Hash: " + fileHash);
                writeLog("Last Time: " + Tools.formatMilliSeconds(lastMillis));
                writeLog("Average Speed: " + Tools.formatSpeed(fileLength, lastMillis));
                writeLog("X-Reqid: " + respInfo.reqId);
                writeLog("X-Via: " + respInfo.xvia);
                writeLog("--------------------------------");
              } catch (JSONException e) {
                AsyncRun.run(
                    new Runnable() {
                      @Override
                      public void run() {
                        Toast.makeText(
                                context,
                                context.getString(R.string.qiniu_upload_file_response_parse_error),
                                Toast.LENGTH_LONG)
                            .show();
                      }
                    });

                writeLog(context.getString(R.string.qiniu_upload_file_response_parse_error));
                if (jsonData != null) {
                  writeLog(jsonData.toString());
                }
                writeLog("--------------------------------");
              }
            } else {
              AsyncRun.run(
                  new Runnable() {
                    @Override
                    public void run() {
                      Toast.makeText(
                              context,
                              context.getString(R.string.qiniu_upload_file_failed),
                              Toast.LENGTH_LONG)
                          .show();
                    }
                  });
              writeLog(respInfo.toString());
              if (jsonData != null) {
                writeLog(jsonData.toString());
              }
              writeLog("--------------------------------");
            }
          }
        },
        uploadOptions);
  }