亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

Android Wear計時器開發

2020-04-11 11:41:55
字體:
來源:轉載
供稿:網友

記得在2013年12月的時候,有系列文章是介紹怎么開發一個智能手表的App,讓用戶可以在足球比賽中記錄停表時間。隨著Android Wear的問世,在可穿戴設備中開發一款這樣的App確實是個很不錯的想法,但是按照目前對于Android Wear的架構了解來說,似乎有些困難。所以本系列文章我們就重寫這個應用,帶領大家進入Android Wear的世界。

本文不會長篇大論地講解我們要開發的這款App的用途,因為我們在之前的系列文章已經深入了解過了。這么說吧,這是一個計時類應用,在比賽開始的時候開始執行,在比賽的過程中可以暫停(停表),然后45分鐘過去后會有震動提醒,然后比賽進行45分鐘后也會有提醒。

在開始之前,很有必要先看看我們為什么要重寫這個App而不是直接上代碼。智能手表使用的是一個修改版的Android1.6的系統,所以它的架構很像一個運行Android1.6的手機,所以我們的App基于一個Activity,我們所有的工作都運行在這個Activity上。在開始學習智能手表開發之前,我們必須很清楚地知道,我們之前的設計在Android Wear上并不適用,盡管它也是支持Activity,但是在Android Wear上工作方式是不同的。在手機或者平板上,如果一個Activity從sleep狀態回到喚醒狀態,Activity會被重新喚醒,但是在Wear上卻不是這樣。一段時間過去后Wear設備會進入sleep,但是在設備喚醒后,處于sleep狀態的Activity卻不會再被喚醒了。

首先這個問題使我非常驚訝,我一直很想知道Activity有了這個限制后,還能開發實用的App嗎?后來才發現這個問題完全是多慮的,我漸漸地發現,要開發一個實用的App也很簡單――我們只需要轉變我們的軟件設計模式,使它更符合Android Wear的體系結構,而不是當做一個手機來看。

這里我們需要考慮的最基本的問題是,這個計時應用程序需要基于一個一直運行的服務來記錄時間。但是基于長運行的服務不是一個好的方案,因為它會耗電。這里我們提到的記錄時間這個關鍵詞,也就是說,我們并不需要真的實現一個長運行的服務,只要在用戶需要看的時候我們可以更新消息顯示就行。在大部分的時間里,其實用戶只需要了解大概過去了多長時間,只有在比賽暫?;蛘咧袌隹旖Y束的時候才需要顯示更詳細的信息。所以在大部分的時間里,我們只需要顯示精確到分鐘即可,然后在用戶需要的時候才精確到秒。

我們要實現這個方法的基本方法就是使用AlarmManager每分鐘觸發一次更新通知事件,去更新分鐘顯示。這個通知事件還包括顯示精確到秒的Activity,但是只有在用戶滑動屏幕的時候才會顯示整個通知。通過這種方式我們可以在必須顯示的時候才去更新消息,所以對大部分設備來說,每分鐘更新一次消息顯示比一直運行一個服務更加省電。

下圖顯示充分證明了這點,首先我們需要打開通知,這樣就可以得到精確到秒的顯示了。

matchtimer

然而,在有信息顯示或者設備休眠的時候,我們只需要顯示精確到分鐘就可以了。

matchtimer_notification

matchtimer_sleep

有一件事情需要說明一下,就是這個App的名字已經改變了。之前在在I'm Watch的版本上叫做“Footy Timer”,現在改為“Match Timer”。因為在使用語音啟動App的時候,Google的聲音識別對“Footy”這個詞很不敏感,我們用“ok Google,start Footy Timer”這個命令不能啟動應用,而使用“ok Google,start Match Timer”就可以使用。

最后,很抱歉這篇文章沒有代碼,但是本系列文章會稍微有些變動。以前本人會在每篇文章末尾附上文章相關的代碼段,這個請放心,之后的文章還是會這樣的,因為這個是一個功能完善的App,而不是系列技術文章,所以在接下來的文章會包含一些代碼示例和注釋,在本系列文章完結的時候會附上整個項目的源碼。

Match Timer 可以在Google Play上找到:https://play.google.com/store/apps/details?id=com.stylingandroid.matchtimer

上面我們解釋了為什么要在Android Wear重寫這個計時器app(因為之前已經在“I'm Watch”里面開發過了),下面我們就來看看代碼。

我們以這個app的一個核心類開始,這個類負責控制計時器的狀態。這個類包含了4個long類型的變量:第一個代表計時器開始的時間;第二個代表計時器停止的時間(在運行中的話,它就是0);第三個代表計時器停表的時間(如果當前沒有停表,那它也是0),第四個代表總共停表的時長。通過這四個變量我們就可以維持計時器的狀態了,還可以通過計算得到我們需要展示的其他信息。這個類的基本功能就是都是為了操作這些變量,即維持計時器的這些狀態。

   public final class MatchTimer {  .  .  .  public static final int MINUTE_MILLIS = 60000;   private long start;  private long currentStoppage;  private long totalStoppages;  private long end;  .  .  .  public long getElapsed() {    if (isRunning()) {      return System.currentTimeMillis() - start;    }    if (end > 0) {      return end - start;    }    return 0;  }   public boolean isRunning() {    return start > 0 && end == 0;  }   public boolean isPaused() {    return currentStoppage > 0;  }   public int getElapsedMinutes() {    return (int) ((System.currentTimeMillis() - start) / MINUTE_MILLIS);  }   public long getTotalStoppages() {    long now = System.currentTimeMillis();    if (isPaused()) {      return totalStoppages + (now - currentStoppage);    }    return totalStoppages;  }   public long getPlayed() {    return getElapsed() - getTotalStoppages();  }   public long getStartTime() {    return start;  }  .  .  .  } 

這些都是基本的java代碼,就不費時間講了。下面的函數更高級一些,可以操作計時器的狀態。

   public final class MatchTimer {  .  .  .  public void start() {    if (end > 0) {      start = System.currentTimeMillis() - (end - start);      end = 0;    } else {      start = System.currentTimeMillis();    }    save();  }   public void stop() {    if (isPaused()) {      resume();    }    end = System.currentTimeMillis();    save();  }   public void pause() {    currentStoppage = System.currentTimeMillis();    save();  }   public void resume() {    totalStoppages += System.currentTimeMillis() - currentStoppage;    currentStoppage = 0L;    save();  }   public void reset() {    resetWithoutSave();    save();  }   private void resetWithoutSave() {    start = 0L;    currentStoppage = 0L;    totalStoppages = 0L;    end = 0L;  }  } 

這些還是基本的Java代碼,也可以不用講了。只有save()方法我們還沒有見到,這是在類的最后寫的,這個函數才值得的我們講講。

前一篇文章我們討論了關于喚醒機制的問題,我們不需要去維持一個長連接或者后臺服務,只需要維持這幾個計時器的狀態就可以了。我們使用SharedPreference來實現:

   public final class MatchTimer implements SharedPreferences.OnSharedPreferenceChangeListener {  private static final String KEY_START = "com.stylingandroid.matchtimer.KEY_START";  private static final String KEY_CURRENT_STOPPAGE = "com.stylingandroid.matchtimer.KEY_CURRENT_STOPPAGE";  private static final String KEY_TOTAL_STOPPAGES = "com.stylingandroid.matchtimer.KEY_TOTAL_STOPPAGES";  private static final String KEY_END = "com.stylingandroid.matchtimer.KEY_END";  private static final String PREFERENCES = "MatchTimer";   private final SharedPreferences preferences;   public static MatchTimer newInstance(Context context) {    SharedPreferences preferences = context.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE);    long start = preferences.getLong(KEY_START, 0);    long currentStoppage = preferences.getLong(KEY_CURRENT_STOPPAGE, 0);    long totalStoppages = preferences.getLong(KEY_TOTAL_STOPPAGES, 0);    long end = preferences.getLong(KEY_END, 0);    return new MatchTimer(preferences, start, currentStoppage, totalStoppages, end);  }   private MatchTimer(SharedPreferences preferences, long start, long currentStoppage, long totalStoppages, long end) {    this.preferences = preferences;    this.start = start;    this.currentStoppage = currentStoppage;    this.totalStoppages = totalStoppages;    this.end = end;  }   public void save() {    preferences.edit()        .putLong(KEY_START, start)        .putLong(KEY_CURRENT_STOPPAGE, currentStoppage)        .putLong(KEY_TOTAL_STOPPAGES, totalStoppages)        .putLong(KEY_END, end)        .apply();  }   public void registerForUpdates() {    preferences.registerOnSharedPreferenceChangeListener(this);  }   public void unregisterForUpdates() {    preferences.unregisterOnSharedPreferenceChangeListener(this);  }   @Override  public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {    long value = sharedPreferences.getLong(key, 0L);    if (key.equals(KEY_START)) {      start = value;    } else if (key.equals(KEY_END)) {      end = value;    } else if (key.equals(KEY_CURRENT_STOPPAGE)) {      currentStoppage = value;    } else if (key.equals(KEY_TOTAL_STOPPAGES)) {      totalStoppages = value;    }  }  .  .  .} 

我們需要的就是newInstance()方法從SharedPreference中構造一個MatchTimer實例,我們還需要save()方法,可以幫我們把當前的計時器狀態保存到SharedPreference中。

最后我們要說明的是,如果某一部分持有MatchTimer對象的引用,但是其他對象已經改變了計時器的狀態,就可能會發生異常(見下一篇文章)。所以我們還需要提供一些方法去注冊和注銷MatchTImer的實例,在Sharedpreference的值改變時去接收計時器狀態的變化。

現在我們已經定義了一個基本的計時器了,下一篇文章我們會介紹怎么保持計時器的狀態以及在需要的時候去喚醒這些狀態。

Match Timer 可以在Google Play上下載:Match Timer.

在本系列前幾篇文章中,我們介紹了Android Wear計時器app,對設計思路和app的結構進行了分析。本文將講解如何定時喚醒程序提醒用戶。

對于為什么不用后臺服務的方式一直運行,我們已經進行了解釋――這種方式非常耗電。因此,我們必須要有一個定時喚醒機制。我們可以使用AlarmManager來實現這個機制,定時執行一個Intent,然后通知BroadcastReceiver。之所以選擇BroadcastReceiver而不用IntentService,是因為我們要運行的任務是輕量級的而且生命周期非常短暫。使用BroadcastReceiver可以避免每次執行任務的時候都經歷Service的整個生命周期。因此,對于我們這種輕量級的任務來說非常合適――我們執行的任務都在毫秒級。

BroadcastReceiver的核心在于onReceiver方法,我們需要在這里安排各種事件響應。

   public class MatchTimerReceiver extends BroadcastReceiver {  public static final int MINUTE_MILLIS = 60000;  private static final long DURATION = 45 * MINUTE_MILLIS;   private static final Intent UPDATE_INTENT = new Intent(ACTION_UPDATE);  private static final Intent ELAPSED_ALARM = new Intent(ACTION_ELAPSED_ALARM);  private static final Intent FULL_TIME_ALARM = new Intent(ACTION_FULL_TIME_ALARM);   private static final int REQUEST_UPDATE = 1;  private static final int REQUEST_ELAPSED = 2;  private static final int REQUEST_FULL_TIME = 3;   public static void setUpdate(Context context) {    context.sendBroadcast(UPDATE_INTENT);  }  .  .  .  private void reset(MatchTimer timer) {    timer.reset();  }   private void resume(Context context, MatchTimer timer) {    timer.resume();    long playedEnd = timer.getStartTime() + timer.getTotalStoppages() + DURATION;    if (playedEnd > System.currentTimeMillis()) {      setAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM, playedEnd);    }  }   private void pause(Context context, MatchTimer timer) {    timer.pause();    cancelAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM);    long elapsedEnd = timer.getStartTime() + DURATION;    if (!isAlarmSet(context, REQUEST_ELAPSED, ELAPSED_ALARM) && elapsedEnd > System.currentTimeMillis()) {      setAlarm(context, REQUEST_ELAPSED, ELAPSED_ALARM, elapsedEnd);    }  }   private void stop(Context context, MatchTimer timer) {    timer.stop();    cancelAlarm(context, REQUEST_UPDATE, UPDATE_INTENT);    cancelAlarm(context, REQUEST_ELAPSED, ELAPSED_ALARM);    cancelAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM);  }   private void start(Context context, MatchTimer timer) {    timer.start();    long elapsedEnd = timer.getStartTime() + DURATION;    setRepeatingAlarm(context, REQUEST_UPDATE, UPDATE_INTENT);    if (timer.getTotalStoppages() > 0 && !timer.isPaused()) {      long playedEnd = timer.getStartTime() + timer.getTotalStoppages() + DURATION;      if (playedEnd > System.currentTimeMillis()) {        setAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM, playedEnd);      }      if (elapsedEnd > System.currentTimeMillis()) {        setAlarm(context, REQUEST_ELAPSED, ELAPSED_ALARM, elapsedEnd);      }    } else {      if (elapsedEnd > System.currentTimeMillis()) {        setAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM, elapsedEnd);      }    }  }  .  .  .  } 

代碼還是非常直觀易于理解的。首先實例化一個MatchTimer對象(從SharedPreference中讀取數據),然后分別傳給對應的事件處理Handler。之后等待動作發生,最后更新Notification。

這里會處理8個事件動作,其中5個負責控制計時器的狀態(START、STOP、PAUSE、RESUME、RESET);一個負責更新Notification,剩下兩個負責到45分鐘喚醒后震動提示。

我們先從這幾個控制狀態開始:

   public class MatchTimerReceiver extends BroadcastReceiver {  public static final int MINUTE_MILLIS = 60000;  private static final long DURATION = 45 * MINUTE_MILLIS;   private static final Intent UPDATE_INTENT = new Intent(ACTION_UPDATE);  private static final Intent ELAPSED_ALARM = new Intent(ACTION_ELAPSED_ALARM);  private static final Intent FULL_TIME_ALARM = new Intent(ACTION_FULL_TIME_ALARM);   private static final int REQUEST_UPDATE = 1;  private static final int REQUEST_ELAPSED = 2;  private static final int REQUEST_FULL_TIME = 3;   public static void setUpdate(Context context) {    context.sendBroadcast(UPDATE_INTENT);  }  .  .  .  private void reset(MatchTimer timer) {    timer.reset();  }   private void resume(Context context, MatchTimer timer) {    timer.resume();    long playedEnd = timer.getStartTime() + timer.getTotalStoppages() + DURATION;    if (playedEnd > System.currentTimeMillis()) {      setAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM, playedEnd);    }  }   private void pause(Context context, MatchTimer timer) {    timer.pause();    cancelAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM);    long elapsedEnd = timer.getStartTime() + DURATION;    if (!isAlarmSet(context, REQUEST_ELAPSED, ELAPSED_ALARM) && elapsedEnd > System.currentTimeMillis()) {      setAlarm(context, REQUEST_ELAPSED, ELAPSED_ALARM, elapsedEnd);    }  }   private void stop(Context context, MatchTimer timer) {    timer.stop();    cancelAlarm(context, REQUEST_UPDATE, UPDATE_INTENT);    cancelAlarm(context, REQUEST_ELAPSED, ELAPSED_ALARM);    cancelAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM);  }   private void start(Context context, MatchTimer timer) {    timer.start();    long elapsedEnd = timer.getStartTime() + DURATION;    setRepeatingAlarm(context, REQUEST_UPDATE, UPDATE_INTENT);    if (timer.getTotalStoppages() > 0 && !timer.isPaused()) {      long playedEnd = timer.getStartTime() + timer.getTotalStoppages() + DURATION;      if (playedEnd > System.currentTimeMillis()) {        setAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM, playedEnd);      }      if (elapsedEnd > System.currentTimeMillis()) {        setAlarm(context, REQUEST_ELAPSED, ELAPSED_ALARM, elapsedEnd);      }    } else {      if (elapsedEnd > System.currentTimeMillis()) {        setAlarm(context, REQUEST_FULL_TIME, FULL_TIME_ALARM, elapsedEnd);      }    }  }  .  .  .  } 

這些方法主要有兩個功能:首先設置MatchTimer的狀態,然后設置時間提醒的鬧鈴,改變參數就可以播放鬧鈴。這個功能還可以封裝成一個工具方法,叫setUpdate()。這樣外部也可以觸發計時器的更新。

我們使用標準AlarmManager的方法來設置鬧鈴:

   public class MatchTimerReceiver extends BroadcastReceiver {  .  .  .  public static final int MINUTE_MILLIS = 60000;  .  .  .   private void setRepeatingAlarm(Context context, int requestCode, Intent intent) {    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), MINUTE_MILLIS, pendingIntent);  }   private boolean isAlarmSet(Context context, int requestCode, Intent intent) {    return PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_NO_CREATE) != null;  }   private void setAlarm(Context context, int requestCode, Intent intent, long time) {    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);    alarmManager.setExact(AlarmManager.RTC_WAKEUP, time, pendingIntent);  }   private void cancelAlarm(Context context, int requestCode, Intent intent) {    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, requestCode, intent, PendingIntent.FLAG_NO_CREATE);    if (pendingIntent != null) {      AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);      alarmManager.cancel(pendingIntent);      pendingIntent.cancel();    }  }  .  .  .  } 

這里值得討論的是setRepeatingAlarm()這個方法。因為在Wear在實現方式上有點不一樣。我們會在Start事件中每秒鐘觸發一次鬧鈴更新Notification動作,所以這里需要記錄具體已經過去了多少分鐘。正常來說我們會每隔60秒觸發一次這個動作,但是在Wear上不能這么做。原因是――當設備在喚醒著的時候可以這樣做,但是如果設備進入睡眠狀態就需要重新計算下一分鐘的邊界值。這就需要異步更新部件,然后設備只需要每分鐘喚醒一次。一分鐘結束后在計時器需要更新狀態的時候觸發操作。

對于我們的計時器應用來說,顯示的分鐘數會比實際時間少1分鐘。但是顯示分鐘并不要求非常實時(但顯示秒數時需要非常精確),所以我們可以這樣操作:

完整的alarm Handler是這樣使用振動服務的:

   public class MatchTimerReceiver extends BroadcastReceiver {  .  .  .  private static final long[] ELAPSED_PATTERN = {0, 500, 250, 500, 250, 500};  private static final long[] FULL_TIME_PATTERN = {0, 1000, 500, 1000, 500, 1000};   private void elapsedAlarm(Context context) {    Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);    vibrator.vibrate(ELAPSED_PATTERN, -1);  }   private void fullTimeAlarm(Context context) {    Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);    vibrator.vibrate(FULL_TIME_PATTERN, -1);  }  .  .  .  } 

最后,我們通過這個方法來構造Notification然后呈現給用戶:

   public class MatchTimerReceiver extends BroadcastReceiver {  public static final int NOTIFICATION_ID = 1;  .  .  .  private void updateNotification(Context context, MatchTimer timer) {    NotificationBuilder builder = new NotificationBuilder(context, timer);    Notification notification = builder.buildNotification();    NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);    notificationManager.notify(NOTIFICATION_ID, notification);  }  } 

Notification是Wear計時器的一個重要的部分,這里還需要一個自定義類來構造這些Notification通知。下一篇文章我們會講如何在計時器app中使用Notification。

Match Timer可以在Google Play上下載:Match Timer。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美精品日韩www.p站| 亚洲第一色在线| 最近免费中文字幕视频2019| 国产一区二区色| 在线观看久久av| 欧美另类69精品久久久久9999| 国产精品久久久久aaaa九色| 亚洲欧美国产日韩天堂区| 国产精品一区二区久久国产| 在线观看欧美视频| 懂色av一区二区三区| 亚洲精品美女网站| 欧美人与物videos| 亚洲第一视频在线观看| 亚洲一区二区三区毛片| 国产精品入口尤物| 色婷婷久久av| 日韩欧美精品中文字幕| 久久久噜噜噜久久中文字免| 中文字幕久久久| 亚洲欧美在线免费观看| 91美女片黄在线观看游戏| 久久精品国产视频| 亚洲国产成人在线视频| 97在线精品国自产拍中文| 亚洲综合在线播放| 亚洲自拍小视频免费观看| 久久露脸国产精品| 富二代精品短视频| 人人澡人人澡人人看欧美| 日韩av在线电影网| 亚洲成人av在线播放| 欧美在线视频免费播放| 亚洲第一精品电影| www国产亚洲精品久久网站| 91亚洲精品久久久| 欧美日韩美女在线| 亚洲欧美另类中文字幕| 国产女精品视频网站免费| 欧美亚洲国产日本| 中文字幕日韩精品在线| 亚洲美女www午夜| 日韩精品视频免费| 欧美精品videofree1080p| 成人久久久久久| 日韩精品www| 2024亚洲男人天堂| 久久久久成人精品| 精品人伦一区二区三区蜜桃网站| 69视频在线免费观看| 国产一区二区黑人欧美xxxx| 欧美激情视频播放| 55夜色66夜色国产精品视频| 一本色道久久综合狠狠躁篇怎么玩| 欧美视频免费在线观看| 黑人精品xxx一区一二区| 成人国产精品一区二区| 成人网在线免费看| 欧美一区二区三区四区在线| 欧美精品情趣视频| 亚洲图片制服诱惑| 日韩电影在线观看永久视频免费网站| 久久99视频精品| 国产亚洲欧美aaaa| 91免费人成网站在线观看18| 欧美猛交免费看| 欧美成人在线影院| 亚洲精品国产精品国产自| 在线一区二区日韩| 精品香蕉一区二区三区| 国产91精品久久久| 久久香蕉国产线看观看网| 在线观看国产成人av片| 国产福利精品在线| 国内精品久久久久影院 日本资源| 欧美一级免费看| 亚洲精品久久久久| 国产精品久久999| 国产精品扒开腿做| 亚洲日本成人女熟在线观看| 国产亚洲福利一区| 国产精品久久久久久久app| 欧美精品成人在线| 欧美性jizz18性欧美| 久久久久国产精品免费网站| 日韩精品在线免费播放| 97久久国产精品| 国外视频精品毛片| 日本高清不卡的在线| 欧美尺度大的性做爰视频| 成人久久一区二区| 亚洲免费一级电影| 国产精品免费一区二区三区都可以| 久久欧美在线电影| 日韩在线视频中文字幕| 国产精品视频网站| 亚洲最大福利视频网| 青青草99啪国产免费| 日韩大陆毛片av| 久久久久久高潮国产精品视| 91系列在线播放| 欧美猛男性生活免费| 久久成人亚洲精品| 欧美在线视频在线播放完整版免费观看| 91精品成人久久| 91精品视频免费观看| 亚洲free性xxxx护士白浆| 欧美激情三级免费| 亚洲xxxxx性| 国产大片精品免费永久看nba| 国产成人精品久久二区二区91| 亚洲第一免费网站| 亚洲爱爱爱爱爱| 欧美专区在线观看| 在线观看免费高清视频97| 国产69精品久久久久9999| 日本成人黄色片| 色爱av美腿丝袜综合粉嫩av| 伊人亚洲福利一区二区三区| 国产999精品| 亚洲精品中文字幕有码专区| 日产日韩在线亚洲欧美| 91最新在线免费观看| 欧美视频中文字幕在线| 欧美裸体xxxxx| 欧洲精品久久久| 精品国内产的精品视频在线观看| 欧美极品少妇xxxxⅹ喷水| 精品无人国产偷自产在线| 中文字幕九色91在线| 97视频国产在线| 97人洗澡人人免费公开视频碰碰碰| 亚洲成人精品视频在线观看| 清纯唯美日韩制服另类| 中文字幕欧美日韩在线| 亚洲片在线观看| 97超级碰在线看视频免费在线看| 欧美激情亚洲激情| 国产精品黄页免费高清在线观看| 国产91精品青草社区| 91视频88av| 亚洲香蕉成人av网站在线观看| 欧美黑人极品猛少妇色xxxxx| 久久青草福利网站| 日本久久久a级免费| 亚洲成年网站在线观看| 日韩精品视频在线免费观看| 91精品国产综合久久香蕉的用户体验| 国产日韩精品在线观看| 中文字幕精品影院| 国产亚洲人成网站在线观看| www日韩中文字幕在线看| 日韩美女在线播放| 久久精品成人一区二区三区| 欧美大成色www永久网站婷| 中文字幕亚洲情99在线| 日产精品99久久久久久| 亚洲福利视频二区| 亚洲国产成人在线视频| 91免费高清视频| 国产啪精品视频网站| 久久久久久有精品国产| 国产精品久久久久久久久久东京| 欧美性猛交99久久久久99按摩|