예제 #1
0
 /** 当所有的刷新逻辑完成后,记录调用一下,否则你的ListView将一直处于正在刷新状态。 */
 public void finishRefreshing() {
   if (log.isDebugEnabled()) {
     log.debug("Finishing refreshing status ...");
   }
   preferences.edit().putLong(UPDATED_AT + mId, System.currentTimeMillis()).commit();
   hideHeader();
 }
예제 #2
0
 /** 更新下拉头中的信息。 */
 private void updateHeaderView() {
   if (log.isDebugEnabled()) {
     log.debug(
         "updateHeaderView, current status :" + currentStatus + ", last status :" + lastStatus);
   }
   if (lastStatus != currentStatus) {
     if (currentStatus == STATUS_PULL_TO_REFRESH) {
       description.setText(getResources().getString(R.string.pull_to_refresh));
       arrow.setVisibility(View.VISIBLE);
       progressBar.setVisibility(View.GONE);
       rotateArrow();
     } else if (currentStatus == STATUS_RELEASE_TO_REFRESH) {
       description.setText(getResources().getString(R.string.release_to_refresh));
       arrow.setVisibility(View.VISIBLE);
       progressBar.setVisibility(View.GONE);
       rotateArrow();
     } else if (currentStatus == STATUS_REFRESHING) {
       description.setText(getResources().getString(R.string.refreshing));
       progressBar.setVisibility(View.VISIBLE);
       arrow.clearAnimation();
       arrow.setVisibility(View.GONE);
     }
     refreshUpdatedAtValue();
   }
 }
예제 #3
0
 public void hideHeader() {
   if (log.isDebugEnabled()) {
     log.debug("Exitting refreshing status ...");
   }
   // currentStatus = STATUS_REFRESH_FINISHED;
   // updateHeaderView();
   if (this.headerLayoutParams != null) {
     new HideHeaderTask().execute();
   } else {
     this.pendingTasks.add(new HideHeaderTask());
   }
 }
예제 #4
0
 protected void setOnRefreshing(boolean callListener) {
   if (log.isDebugEnabled()) {
     log.debug("Set on refreshing status ...");
   }
   currentStatus = STATUS_REFRESHING;
   updateHeaderView();
   if (this.headerLayoutParams != null) {
     new RefreshingTask(callListener).execute();
   } else {
     this.pendingTasks.add(new RefreshingTask(callListener));
   }
 }
예제 #5
0
 @Override
 protected void onLayout(boolean changed, int l, int t, int r, int b) {
   if (log.isDebugEnabled()) {
     log.debug("onLayout ...");
   }
   super.onLayout(changed, l, t, r, b);
   if (changed && (headerLayoutParams == null)) {
     hideHeaderHeight = -header.getHeight();
     if (log.isDebugEnabled()) {
       log.debug("init Layout params, hideHeaderHeight :" + hideHeaderHeight);
     }
     headerLayoutParams = (MarginLayoutParams) header.getLayoutParams();
     headerLayoutParams.topMargin = hideHeaderHeight;
     if (!this.pendingTasks.isEmpty()) {
       for (AsyncTask<Void, Integer, Integer> task : this.pendingTasks) {
         task.execute();
       }
       this.pendingTasks.clear();
     }
   } else {
     if (log.isDebugEnabled()) {
       log.debug("onLayout, hideHeaderHeight :" + headerLayoutParams.topMargin);
     }
     // header.setLayoutParams(headerLayoutParams);
   }
 }
public class ImprorDataProcessDialog extends BaseActivity {
  private static final Trace log = Trace.register(ImprorDataProcessDialog.class);
  //   com.wxxr.callhelper.widget.MyProcesBar processbar;
  SeekBar processbar;
  TextView processtip;
  ArrayList<String> addperson;
  private Context mcontext;
  private int requestcode;
  private String tip;
  Handler hanler =
      new Handler() {
        public void handleMessage(android.os.Message msg) {
          if (processbar.getMax() != 0) {
            processtip.setText(tip + (processbar.getProgress() * 100 / processbar.getMax()) + "%");
          }
        };
      };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub

    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.import_sysmes_processbar);
    requestcode = getIntent().getIntExtra(Constant.REQUEST_CODE, -1);

    processbar = (SeekBar) findViewById(R.id.my_progressbar1);

    processtip = (TextView) findViewById(R.id.my_progressbar_tip);

    addperson = getIntent().getStringArrayListExtra(Constant.IMPORT_OR_DEL_NUMS);
    mcontext = this.getBaseContext();
    if (getIntent().getStringExtra(Constant.IS_IMPORT_NUMS).length() > 0) {
      tip = "正在导入联系人";
      new ImportSysmes().execute();
    } else {
      tip = "正在导出联系人";
      findViewById(R.id.gd_iv_titlebar_icon)
          .setBackgroundResource(R.drawable.gd_dialog_private_contacts_icon_out);
      new ExportToSysmes().execute();
    }
  }

  @Override
  public void onBackPressed() {
    // TODO Auto-generated method stub
    //	super.onBackPressed();
  }

  @Override
  protected void onResume() {
    // TODO Auto-generated method stub
    super.onResume();
  }

  class ImportSysmes extends AsyncTask<Context, Void, Void> {

    @Override
    protected void onPreExecute() {
      // TODO Auto-generated method stub
      super.onPreExecute();
    }

    @Override
    protected void onPostExecute(Void result) {
      super.onPostExecute(result);
      finish();
    }

    @Override
    protected Void doInBackground(Context... params) {
      int size = addperson.size();
      int total = 0;
      // 计算信息总数
      for (int i = 0; i < size; i++) {
        total = +Tools.getSmsOfPhoneNum(mcontext, null, addperson.get(i));
      }

      processbar.setMax(total);
      processbar.setProgress(1);
      // 开始导入,不要用列表,避免内存不足
      IPrivateSMService service = getService(IPrivateSMService.class);
      boolean needbell = service.isRingBellWhenReceiving();
      service.setRingBellWhenReceiving(false);
      int curvalue = 0;
      for (int i = 0; i < size; i++) {
        String phonenum = addperson.get(i);
        if (phonenum.length() > 10) {
          phonenum = phonenum.substring(phonenum.length() - 11, phonenum.length());
        }
        Cursor c =
            mcontext
                .getApplicationContext()
                .getContentResolver()
                .query(
                    Sms.CONTENT_URI, null, Sms.ADDRESS + " like '%" + phonenum + "%'", null, null);
        if (c.moveToFirst()) {
          if (log.isDebugEnabled()) {
            log.debug("find sms of ==========", phonenum);
          }
          String threadid = c.getString(c.getColumnIndex(Sms.THREAD_ID));

          Cursor cursor =
              mcontext
                  .getApplicationContext()
                  .getContentResolver()
                  .query(Sms.CONTENT_URI, null, Sms.THREAD_ID + " = " + threadid, null, null);
          ContentResolver contentResolver = mcontext.getApplicationContext().getContentResolver();
          StringBuffer sb = new StringBuffer();
          if (cursor.moveToFirst()) {
            if (log.isDebugEnabled()) {
              log.debug("ImprorDataProcessDialog cont==", cursor.getCount());
            }
            int bodyindex = cursor.getColumnIndex(Sms.BODY);
            int dateindex = cursor.getColumnIndex(Sms.DATE);
            int typeindex = cursor.getColumnIndex(Sms.TYPE);
            int numindex = cursor.getColumnIndex(Sms.ADDRESS);
            int idindex = cursor.getColumnIndex(Sms._ID);

            do {
              TextMessageBean bean = new TextMessageBean();
              String strnum = cursor.getString(numindex);
              String body = cursor.getString(bodyindex);
              // 回执和漏电短信都不用要导入
              if (SMSFilter.isSmsFailHuizhi(body, strnum)
                  || SMSFilter.isSmsOKHuizhi(body, strnum)
                  || getService(ILouHuaHuizhiService.class).isMatch(body, strnum)) {

              } else {
                // 去除前缀,只保留后边的11位
                if (strnum.endsWith(phonenum)) {
                  strnum = phonenum;
                }
                int type = cursor.getInt(typeindex);
                if (type == 1) {
                  bean.setMo(strnum);
                } else if (type == 2) {
                  bean.setMt(strnum);
                }

                bean.setNumber(strnum);
                bean.setTimestamp(cursor.getLong(dateindex));
                bean.setContent(body);
                service.onSMReceived(bean, false, true);
                sb.append(cursor.getString(idindex)).append(",");
              }
              curvalue++;
              processbar.setProgress(curvalue);
              processbar.setMax(total);
              hanler.sendEmptyMessage(0);
              if (log.isDebugEnabled()) {
                log.debug("ImprorDataProcessDialog text==", cursor.getString(bodyindex));
              }
            } while (cursor.moveToNext());
            cursor.close();
            if (sb.toString().length() > 0) {
              sb.deleteCharAt(sb.length() - 1);
              String ids = sb.toString();
              contentResolver.delete(Sms.CONTENT_URI, Sms._ID + " in (" + ids + ")", null);
            }
          }

        } else {
          if (log.isDebugEnabled()) {
            log.debug("not find sms of $$$$$$$$ ", phonenum);
          }
        }
        c.close();
        service.setRingBellWhenReceiving(needbell);
        setResult(requestcode);
      }

      return null;
    }
  }

  class ExportToSysmes extends AsyncTask<Context, Void, Void> {

    @Override
    protected void onPreExecute() {
      // TODO Auto-generated method stub
      super.onPreExecute();
    }

    @Override
    protected void onPostExecute(Void result) {
      showMessageBox("成功导出联系人信息");
      super.onPostExecute(result);
      finish();
    }

    @Override
    protected Void doInBackground(Context... params) {
      int size = addperson.size();
      int total = 0;
      // 计算信息总数
      for (int i = 0; i < size; i++) {
        List<TextMessageBean> list =
            getService(IPrivateSMService.class).getAllMessageOf(addperson.get(i));
        if (list != null) {
          total += list.size();
          list = null;
        }
      }

      processbar.setMax(total);
      processbar.setProgress(1);
      int curcount = 0;
      for (int j = 0; j < size; j++) {
        String num = addperson.get(j);
        List<TextMessageBean> list = getService(IPrivateSMService.class).getAllMessageOf(num);
        if (list != null) {
          Tools.remove_to_sysbox.put(num, "");
          int leng = list.size();
          for (int k = 0; k < leng; k++) {
            Tools.inserToSysmsgOne(list.get(k), mcontext);
            curcount++;
            hanler.sendEmptyMessage(0);
            processbar.setProgress(curcount);
          }

          getService(IPrivateSMService.class).deleteMessage(num, j == size - 1);
        }
        getService(IPrivateSMService.class).removePrivateNumber(num);
      }
      return null;
    }
  }
}
public class SiMiSettingActivity extends BaseActivity implements OnClickListener {

  private static final Trace log = Trace.register(SiMiSettingActivity.class);

  ImageView iv_kaiguan;
  ImageView iv_finish;
  boolean lMRFlag = true;
  TextView tv_update;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.simiren_setting_dialog);

    iv_kaiguan = (ImageView) findViewById(R.id.iv_simi_kaiguan);
    tv_update = (TextView) findViewById(R.id.tv_update);

    iv_kaiguan.setOnClickListener(this);
    tv_update.setOnClickListener(this);
    try {
      this.lMRFlag = getService(IPrivateSMService.class).isRingBellWhenReceiving() == false;
      if (this.lMRFlag) {
        iv_kaiguan.setBackgroundResource(R.drawable.close);
      } else {
        iv_kaiguan.setBackgroundResource(R.drawable.open);
      }
    } catch (Throwable t) {

    }
  }

  @Override
  public void onClick(View v) {
    switch (v.getId()) {
      case R.id.iv_simi_kaiguan:
        if (lMRFlag) {
          iv_kaiguan.setBackgroundResource(R.drawable.open);
          lMRFlag = false;
          doUpdate(true);
        } else {
          iv_kaiguan.setBackgroundResource(R.drawable.close);
          lMRFlag = true;
          doUpdate(false);
        }

        break;
      case R.id.iv_finish:
        finish();
        break;
      case R.id.tv_update:
        Intent intent = new Intent();
        // intent.putExtra("from_simi", "update_password");
        intent.setClass(SiMiSettingActivity.this, SiMiSuoHomePasswordActivity.class);
        intent.putExtra("resetpass", "yes");
        startActivity(intent);
        //	finish();

        break;
    }
  }

  private void doUpdate(final boolean bool) {
    new CMProgressMonitor(this) {
      @Override
      protected void handleFailed(Throwable cause) {
        log.error("Caught exception when sending SM message", cause);
        showMessageBox("提醒设置失败,原因:[" + cause.getLocalizedMessage() + "]");
      }

      @Override
      protected void handleDone(Object returnVal) {
        showMessageBox("提醒设置成功");
      }

      /*
       * (non-Javadoc)
       *
       * @see com.wxxr.callhelper.CMProgressMonitor#getDialogParams()
       */
      @Override
      protected Map<String, Object> getDialogParams() {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put(DIALOG_PARAM_KEY_TITLE, "系统提示:");
        map.put(DIALOG_PARAM_KEY_MESSAGE, "正在更新提醒设置,请稍侯...");
        return map;
      }
    }.executeOnMonitor(
        new Callable<Object>() {

          @Override
          public Object call() throws Exception {
            getService(IPrivateSMService.class).setRingBellWhenReceiving(bool);
            return null;
          }
        });
  }
}
예제 #8
0
 public void hideHeaderDirect() {
   if (log.isDebugEnabled()) {
     log.debug("Exitting refreshing status ...");
   }
   header.setVisibility(View.GONE);
 }
예제 #9
0
public class RefreshableView extends LinearLayout implements OnTouchListener {
  private static final Trace log = Trace.register(RefreshableView.class);
  /** 下拉状态 */
  public static final int STATUS_PULL_TO_REFRESH = 0;

  /** 释放立即刷新状态 */
  public static final int STATUS_RELEASE_TO_REFRESH = 1;

  /** 正在刷新状态 */
  public static final int STATUS_REFRESHING = 2;

  /** 刷新完成或未刷新状态 */
  public static final int STATUS_REFRESH_FINISHED = 3;

  /** 下拉头部回滚的速度 */
  public static final int SCROLL_SPEED = -20;

  /** 一分钟的毫秒值,用于判断上次的更新时间 */
  public static final long ONE_MINUTE = 60 * 1000;

  /** 一小时的毫秒值,用于判断上次的更新时间 */
  public static final long ONE_HOUR = 60 * ONE_MINUTE;

  /** 一天的毫秒值,用于判断上次的更新时间 */
  public static final long ONE_DAY = 24 * ONE_HOUR;

  /** 一月的毫秒值,用于判断上次的更新时间 */
  public static final long ONE_MONTH = 30 * ONE_DAY;

  /** 一年的毫秒值,用于判断上次的更新时间 */
  public static final long ONE_YEAR = 12 * ONE_MONTH;

  /** 上次更新时间的字符串常量,用于作为SharedPreferences的键值 */
  private static final String UPDATED_AT = "updated_at";

  /** 下拉刷新的回调接口 */
  private PullToRefreshListener mListener;

  /** 用于存储上次更新时间 */
  private SharedPreferences preferences;

  /** 下拉头的View */
  private View header;

  /** 需要去下拉刷新的ListView */
  private IPullableView pullableView;

  /** 刷新时显示的进度条 */
  private ProgressBar progressBar;

  /** 指示下拉和释放的箭头 */
  private ImageView arrow;

  /** 指示下拉和释放的文字描述 */
  private TextView description;

  /** 上次更新时间的文字描述 */
  private TextView updateAt;

  /** 下拉头的布局参数 */
  private MarginLayoutParams headerLayoutParams;

  /** 上次更新时间的毫秒值 */
  private long lastUpdateTime;

  /** 为了防止不同界面的下拉刷新在上次更新时间上互相有冲突,使用id来做区分 */
  private int mId = -1;

  /** 下拉头的高度 */
  private int hideHeaderHeight;

  /**
   * 当前处理什么状态,可选值有STATUS_PULL_TO_REFRESH, STATUS_RELEASE_TO_REFRESH, STATUS_REFRESHING 和
   * STATUS_REFRESH_FINISHED
   */
  private int currentStatus = STATUS_REFRESH_FINISHED;;

  /** 记录上一次的状态是什么,避免进行重复操作 */
  private int lastStatus = currentStatus;

  /** 手指按下时的屏幕纵坐标 */
  private float yDown;

  /** 在被判定为滚动之前用户手指可以移动的最大值。 */
  private int touchSlop;

  // /**
  // * 是否已加载过一次layout,这里onLayout中的初始化只需加载一次
  // */
  // private boolean loadOnce;

  /** 当前是否可以下拉,只有ListView滚动到头的时候才允许下拉 */
  private boolean ableToPull;

  private Handler handler;

  private boolean pullStarted = false;

  private ArrayList<AsyncTask<Void, Integer, Integer>> pendingTasks =
      new ArrayList<AsyncTask<Void, Integer, Integer>>();

  /**
   * 下拉刷新控件的构造函数,会在运行时动态添加一个下拉头的布局。
   *
   * @param context
   * @param attrs
   */
  public RefreshableView(Context context, AttributeSet attrs) {
    super(context, attrs);
    preferences = PreferenceManager.getDefaultSharedPreferences(context);
    header = LayoutInflater.from(context).inflate(R.layout.pull_to_refresh, null, true);
    progressBar = (ProgressBar) header.findViewById(R.id.progress_bar);
    arrow = (ImageView) header.findViewById(R.id.arrow_up);
    description = (TextView) header.findViewById(R.id.description);
    updateAt = (TextView) header.findViewById(R.id.updated_at);
    touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
    refreshUpdatedAtValue();
    setOrientation(VERTICAL);
    addView(header, 0);
  }

  public void setHandler(Handler handler) {
    this.handler = handler;
  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    if (log.isDebugEnabled()) {
      log.debug("onLayout ...");
    }
    super.onLayout(changed, l, t, r, b);
    if (changed && (headerLayoutParams == null)) {
      hideHeaderHeight = -header.getHeight();
      if (log.isDebugEnabled()) {
        log.debug("init Layout params, hideHeaderHeight :" + hideHeaderHeight);
      }
      headerLayoutParams = (MarginLayoutParams) header.getLayoutParams();
      headerLayoutParams.topMargin = hideHeaderHeight;
      if (!this.pendingTasks.isEmpty()) {
        for (AsyncTask<Void, Integer, Integer> task : this.pendingTasks) {
          task.execute();
        }
        this.pendingTasks.clear();
      }
    } else {
      if (log.isDebugEnabled()) {
        log.debug("onLayout, hideHeaderHeight :" + headerLayoutParams.topMargin);
      }
      // header.setLayoutParams(headerLayoutParams);
    }
  }

  public boolean isInRefreshing() {
    return currentStatus == STATUS_REFRESHING;
  }

  /** 当ListView被触摸时调用,其中处理了各种下拉刷新的具体逻辑。 */
  @Override
  public boolean onTouch(View v, MotionEvent event) {
    setIsAbleToPull(v, event);
    boolean retVal = false;
    if (ableToPull) {
      switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
          yDown = event.getRawY();
          // if(log.isDebugEnabled()){
          // log.debug("Touch down, y position :"+yDown);
          // }
          break;
        case MotionEvent.ACTION_MOVE:
          float yMove = event.getRawY();
          int distance = (int) (yMove - yDown);
          // if(log.isDebugEnabled()){
          // log.debug("Touch down and move, y position :"+yMove+", move distance :"+distance);
          // }
          // 如果手指是下滑状态,并且下拉头是完全隐藏的,就屏蔽下拉事件
          if (distance <= 0 && headerLayoutParams.topMargin <= hideHeaderHeight) {
            return false;
          }
          if (distance < touchSlop) {
            return false;
          }
          if (!pullStarted) {
            // if(log.isDebugEnabled()){
            // log.debug("Pull started, touchSlop = "+touchSlop);
            // }
            pullableView.pullStarted(v);
            pullStarted = true;
          }
          retVal = true;
          if (currentStatus != STATUS_REFRESHING) {
            if (headerLayoutParams.topMargin > 0) {
              currentStatus = STATUS_RELEASE_TO_REFRESH;
            } else {
              currentStatus = STATUS_PULL_TO_REFRESH;
            }
            // 通过偏移下拉头的topMargin值,来实现下拉效果
            headerLayoutParams.topMargin = (distance / 2) + hideHeaderHeight;
            header.setLayoutParams(headerLayoutParams);
          }
          break;
        case MotionEvent.ACTION_UP:
        default:
          if (pullStarted) {
            pullableView.pullEnded(v);
            pullStarted = false;
            retVal = true;
          }
          if (currentStatus == STATUS_RELEASE_TO_REFRESH) {
            // 松手时如果是释放立即刷新状态,就去调用正在刷新的任务
            setOnRefreshing(true);
          } else if (currentStatus == STATUS_PULL_TO_REFRESH) {
            // 松手时如果是下拉状态,就去调用隐藏下拉头的任务
            hideHeader();
          }
          break;
      }
      // 时刻记得更新下拉头中的信息
      if (currentStatus == STATUS_PULL_TO_REFRESH || currentStatus == STATUS_RELEASE_TO_REFRESH) {
        updateHeaderView();
        // 当前正处于下拉或释放状态,要让ListView失去焦点,否则被点击的那一项会一直处于选中状态
        pullableView.getView().setPressed(false);
        pullableView.getView().setFocusable(false);
        pullableView.getView().setFocusableInTouchMode(false);
        lastStatus = currentStatus;
        // 当前正处于下拉或释放状态,通过返回true屏蔽掉ListView的滚动事件
      }
    }
    return retVal;
  }

  public void setOnRefreshing() {
    setOnRefreshing(false);
  }

  protected void setOnRefreshing(boolean callListener) {
    if (log.isDebugEnabled()) {
      log.debug("Set on refreshing status ...");
    }
    currentStatus = STATUS_REFRESHING;
    updateHeaderView();
    if (this.headerLayoutParams != null) {
      new RefreshingTask(callListener).execute();
    } else {
      this.pendingTasks.add(new RefreshingTask(callListener));
    }
  }

  /**
   * 给下拉刷新控件注册一个监听器。
   *
   * @param listener 监听器的实现。
   * @param id 为了防止不同界面的下拉刷新在上次更新时间上互相有冲突, 请不同界面在注册下拉刷新监听器时一定要传入不同的id。
   */
  public void setOnRefreshListener(PullToRefreshListener listener, int id) {
    mListener = listener;
    mId = id;
  }

  /** 当所有的刷新逻辑完成后,记录调用一下,否则你的ListView将一直处于正在刷新状态。 */
  public void finishRefreshing() {
    if (log.isDebugEnabled()) {
      log.debug("Finishing refreshing status ...");
    }
    preferences.edit().putLong(UPDATED_AT + mId, System.currentTimeMillis()).commit();
    hideHeader();
  }

  public void hideHeader() {
    if (log.isDebugEnabled()) {
      log.debug("Exitting refreshing status ...");
    }
    // currentStatus = STATUS_REFRESH_FINISHED;
    // updateHeaderView();
    if (this.headerLayoutParams != null) {
      new HideHeaderTask().execute();
    } else {
      this.pendingTasks.add(new HideHeaderTask());
    }
  }

  public void hideHeaderDirect() {
    if (log.isDebugEnabled()) {
      log.debug("Exitting refreshing status ...");
    }
    header.setVisibility(View.GONE);
  }

  /**
   * 根据当前ListView的滚动状态来设定 {@link #ableToPull}
   * 的值,每次都需要在onTouch中第一个执行,这样可以判断出当前应该是滚动ListView,还是应该进行下拉。
   *
   * @param event
   */
  private void setIsAbleToPull(View view, MotionEvent event) {
    boolean oldVal = this.ableToPull;
    this.ableToPull = this.pullableView.isPullable(view, event);
    if (this.ableToPull) {
      if (oldVal == false) {
        yDown = event.getRawY();
      }
    } else if (headerLayoutParams.topMargin != hideHeaderHeight) {
      headerLayoutParams.topMargin = hideHeaderHeight;
      header.setLayoutParams(headerLayoutParams);
    }
    if (pullableView.getView() instanceof ListView) {
      View firstChild = ((ListView) pullableView.getView()).getChildAt(0);
      if (firstChild != null) {
        int firstVisiblePos = ((ListView) pullableView.getView()).getFirstVisiblePosition();
        if (firstVisiblePos == 0 && firstChild.getTop() == 0) {
          if (!ableToPull) {
            yDown = event.getRawY();
          }
          // 如果首个元素的上边缘,距离父布局值为0,就说明ListView滚动到了最顶部,此时应该允许下拉刷新
          ableToPull = true;
        } else {
          if (headerLayoutParams.topMargin != hideHeaderHeight) {
            headerLayoutParams.topMargin = hideHeaderHeight;
            header.setLayoutParams(headerLayoutParams);
          }
          ableToPull = false;
        }
      } else {
        // 如果ListView中没有元素,也应该允许下拉刷新
        ableToPull = true;
      }
    }
  }

  /** 更新下拉头中的信息。 */
  private void updateHeaderView() {
    if (log.isDebugEnabled()) {
      log.debug(
          "updateHeaderView, current status :" + currentStatus + ", last status :" + lastStatus);
    }
    if (lastStatus != currentStatus) {
      if (currentStatus == STATUS_PULL_TO_REFRESH) {
        description.setText(getResources().getString(R.string.pull_to_refresh));
        arrow.setVisibility(View.VISIBLE);
        progressBar.setVisibility(View.GONE);
        rotateArrow();
      } else if (currentStatus == STATUS_RELEASE_TO_REFRESH) {
        description.setText(getResources().getString(R.string.release_to_refresh));
        arrow.setVisibility(View.VISIBLE);
        progressBar.setVisibility(View.GONE);
        rotateArrow();
      } else if (currentStatus == STATUS_REFRESHING) {
        description.setText(getResources().getString(R.string.refreshing));
        progressBar.setVisibility(View.VISIBLE);
        arrow.clearAnimation();
        arrow.setVisibility(View.GONE);
      }
      refreshUpdatedAtValue();
    }
  }

  /** 根据当前的状态来旋转箭头。 */
  private void rotateArrow() {
    float pivotX = arrow.getWidth() / 2f;
    float pivotY = arrow.getHeight() / 2f;
    float fromDegrees = 0f;
    float toDegrees = 0f;
    if (currentStatus == STATUS_PULL_TO_REFRESH) {
      fromDegrees = 180f;
      toDegrees = 360f;
    } else if (currentStatus == STATUS_RELEASE_TO_REFRESH) {
      fromDegrees = 0f;
      toDegrees = 180f;
    }
    RotateAnimation animation = new RotateAnimation(fromDegrees, toDegrees, pivotX, pivotY);
    animation.setDuration(100);
    animation.setFillAfter(true);
    arrow.startAnimation(animation);
  }

  public long getLastUpdateTime() {
    return preferences.getLong(UPDATED_AT + mId, -1);
  }

  /** 刷新下拉头中上次更新时间的文字描述。 */
  private void refreshUpdatedAtValue() {
    lastUpdateTime = getLastUpdateTime();
    long currentTime = System.currentTimeMillis();
    long timePassed = currentTime - lastUpdateTime;
    long timeIntoFormat;
    String updateAtValue;
    if (lastUpdateTime == -1) {
      updateAtValue = getResources().getString(R.string.not_updated_yet);
    } else if (timePassed < 0) {
      updateAtValue = getResources().getString(R.string.time_error);
    } else if (timePassed < ONE_MINUTE) {
      updateAtValue = getResources().getString(R.string.updated_just_now);
    } else if (timePassed < ONE_HOUR) {
      timeIntoFormat = timePassed / ONE_MINUTE;
      String value = timeIntoFormat + "分钟";
      updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
    } else if (timePassed < ONE_DAY) {
      timeIntoFormat = timePassed / ONE_HOUR;
      String value = timeIntoFormat + "小时";
      updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
    } else if (timePassed < ONE_MONTH) {
      timeIntoFormat = timePassed / ONE_DAY;
      String value = timeIntoFormat + "天";
      updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
    } else if (timePassed < ONE_YEAR) {
      timeIntoFormat = timePassed / ONE_MONTH;
      String value = timeIntoFormat + "个月";
      updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
    } else {
      timeIntoFormat = timePassed / ONE_YEAR;
      String value = timeIntoFormat + "年";
      updateAtValue = String.format(getResources().getString(R.string.updated_at), value);
    }
    updateAt.setText(updateAtValue);
  }

  /**
   * 正在刷新的任务,在此任务中会去回调注册进来的下拉刷新监听器。
   *
   * @author guolin
   */
  class RefreshingTask extends AsyncTask<Void, Integer, Integer> {
    private final boolean callListener;

    RefreshingTask(boolean bool) {
      this.callListener = bool;
    }

    @Override
    protected Integer doInBackground(Void... params) {
      if (log.isDebugEnabled()) {
        log.debug("Execute RefreshingTask ...");
      }
      int topMargin = headerLayoutParams.topMargin;
      while (true) {
        topMargin = topMargin + SCROLL_SPEED;
        if (topMargin <= 0) {
          topMargin = 0;
          break;
        }
        publishProgress(topMargin);
        sleep(10);
      }
      currentStatus = STATUS_REFRESHING;
      publishProgress(0);
      if (this.callListener && (mListener != null)) {
        mListener.onRefresh();
      }

      return topMargin;
    }

    @Override
    protected void onProgressUpdate(Integer... topMargin) {
      if (handler != null) {
        handler.handle();
      }
      updateHeaderView();
      headerLayoutParams.topMargin = topMargin[0];
      header.setLayoutParams(headerLayoutParams);
    }
  }

  interface Handler {
    void handle();
  }

  /**
   * 隐藏下拉头的任务,当未进行下拉刷新或下拉刷新完成后,此任务将会使下拉头重新隐藏。
   *
   * @author guolin
   */
  class HideHeaderTask extends AsyncTask<Void, Integer, Integer> {

    @Override
    protected Integer doInBackground(Void... params) {
      int topMargin = headerLayoutParams.topMargin;
      while (true) {
        topMargin = topMargin + SCROLL_SPEED;
        if (topMargin <= hideHeaderHeight) {
          topMargin = hideHeaderHeight;
          break;
        }
        publishProgress(topMargin);
        sleep(10);
      }
      return topMargin;
    }

    @Override
    protected void onProgressUpdate(Integer... topMargin) {
      headerLayoutParams.topMargin = topMargin[0];
      header.setLayoutParams(headerLayoutParams);
    }

    @Override
    protected void onPostExecute(Integer topMargin) {
      headerLayoutParams.topMargin = topMargin;
      header.setLayoutParams(headerLayoutParams);
      currentStatus = STATUS_REFRESH_FINISHED;
    }
  }

  /**
   * 使当前线程睡眠指定的毫秒数。
   *
   * @param time 指定当前线程睡眠多久,以毫秒为单位
   */
  private void sleep(int time) {
    try {
      Thread.sleep(time);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

  /**
   * 下拉刷新的监听器,使用下拉刷新的地方应该注册此监听器来获取刷新回调。
   *
   * @author guolin
   */
  public interface PullToRefreshListener {

    /** 刷新时会去回调此方法,在方法内编写具体的刷新逻辑。注意此方法是在子线程中调用的, 你可以不必另开线程来进行耗时操作。 */
    void onRefresh();
  }

  /** @return the pullableView */
  public IPullableView getPullableView() {
    return pullableView;
  }

  /** @param pullableView the pullableView to set */
  public void setPullableView(IPullableView pullableView) {
    if (pullableView == null) {
      throw new IllegalArgumentException("Invalid pass in view : NULL");
    }
    this.pullableView = pullableView;
    addView(this.pullableView.getView());
    this.pullableView.setOnTouchListener(this);
  }

  // public void setIsAbleToPull(boolean isAbleToPull) {
  // this.ableToPull = isAbleToPull;
  // }

}