日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

tinode客户端安卓版编译手账

發布時間:2024/1/8 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 tinode客户端安卓版编译手账 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前一陣子我自己架設了一個tinode的IM服務器,

web直接可以運行

但是安卓版本的一直報錯,

具體信息為:

No subjectAltNames on the certificate match

問了作者,作者竟然把我的問題直接刪除了,還是自己調試代碼吧。畢竟源碼面前,了無秘密;

一、代碼地址

GitHub - tinode/tindroid: Tinode chat client application for Android

我從release部分下載了0.20.9版本源碼

二、更改源碼配置

1)根目錄下的build.gradle有2處需要更改,主要是版本信息,非git版本無從提取,隨便設置一下

static def gitVersionCode() {// If you are not compiling in a git directory and getting an error like// [A problem occurred evaluating root project 'master'. For input string: ""]// then just return your manually assigned error code like this:// return 12345def process = "git rev-list --count HEAD".execute()return 12345 }// Use current git tag as a version name. // For example, if the git tag is 'v0.20.0-rc1' then the version name will be '0.20.0-rc1'. static def gitVersionName() {// If you are not compiling in a git directory, you should manually assign version name:// return "MyVersionName"def process = "git describe --tags".execute()// Remove trailing CR and remove leading 'v' as in 'v1.2.3'return "1.2.3" }

2)app下面的build.gradle有3處需要修改

2.1)程序使用googleService,需要去官網注冊一下相關的資料,自己注冊一個新的應用,下載得到google-services.json,這個文件放置于app目錄;

2.2)google-services.json中我們注冊了一個應用的名字,這文件中有個package_name替換原來的應用ID,否則編譯不過

applicationId "com.birdschat.cn"

2.3)創建證書,文件放置于源碼同級目錄,比如我的:

../robinkeys/key.keystore

在根目錄下添加一個配置文件,叫keystore.properties,內容大概如下:

keystoreFile=../robin_keys/key.keystore keystoreAlias=key.keystore keystorePassword=123456 keyPassword=123456

并根據自己配置文件中的參數名,設置一下build.gradle:

signingConfigs {release {storeFile file(keystoreProperties['keystoreFile'])storePassword keystoreProperties['keystorePassword']keyAlias keystoreProperties['keyAlias']keyPassword keystoreProperties['keyPassword']}}

這樣應該就可以編譯了!!

3)取消客戶端WebSocket 的SSL雙向認證

但是運行后,設置了自己的服務器,以及使用加密模式,無法注冊或者登錄,

主要是我們的證書需要有域名,并且是申請來的,也就是有CA認證的,而不是自己生成的,不然無法實現雙向驗證,這主要是為了防止中間人攻擊;

但是我們往往就是自己內部試用,不需要這么麻煩,

需要對SDK部分代碼進行更該,參考:java websocket及忽略證書_nell_lee的博客-CSDN博客_websocket 忽略證書

更改后的代碼如下:Connection.java 全文

package co.tinode.tinodesdk;import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper;import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable;import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.security.SecureRandom; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CancellationException;import co.tinode.tinodesdk.model.MsgServerCtrl; import co.tinode.tinodesdk.model.ServerMessage; // import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.*; import java.net.Socket; import java.net.URI; import java.security.cert.CertificateException; import java.security.cert.X509Certificate;public class LargeFileHelper {private static final int BUFFER_SIZE = 65536;private static final String TWO_HYPHENS = "--";private static final String BOUNDARY = "*****" + System.currentTimeMillis() + "*****";private static final String LINE_END = "\r\n";private final URL mUrlUpload;private final String mHost;private final String mApiKey;private final String mAuthToken;private final String mUserAgent;private boolean mCanceled = false;private int mReqId = 1;public LargeFileHelper(URL urlUpload, String apikey, String authToken, String userAgent) {mUrlUpload = urlUpload;mHost = mUrlUpload.getHost();mApiKey = apikey;mAuthToken = authToken;mUserAgent = userAgent;handleSSLHandshake();}// robin add herepublic static void handleSSLHandshake() {try {TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}@Overridepublic void checkClientTrusted(X509Certificate[] certs, String authType) {}@Overridepublic void checkServerTrusted(X509Certificate[] certs, String authType) {}}};SSLContext sc = SSLContext.getInstance("TLS");// trustAllCerts信任所有的證書sc.init(null, trustAllCerts, new SecureRandom());HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {@Overridepublic boolean verify(String hostname, SSLSession session) {return true;}});} catch (Exception ignored) {}}// Upload file out of band. This should not be called on the UI thread.public ServerMessage upload(@NotNull InputStream in, @NotNull String filename, @NotNull String mimetype, long size,@Nullable String topic, @Nullable FileHelperProgress progress) throws IOException, CancellationException {mCanceled = false;HttpURLConnection conn = null;ServerMessage msg;try {conn = (HttpURLConnection) mUrlUpload.openConnection();conn.setDoOutput(true);conn.setUseCaches(false);conn.setRequestProperty("Connection", "Keep-Alive");conn.setRequestProperty("User-Agent", mUserAgent);conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);conn.setRequestProperty("X-Tinode-APIKey", mApiKey);if (mAuthToken != null) {// mAuthToken could be null when uploading avatar on sign up.conn.setRequestProperty("X-Tinode-Auth", "Token " + mAuthToken);}conn.setChunkedStreamingMode(0);DataOutputStream out = new DataOutputStream(new BufferedOutputStream(conn.getOutputStream()));// Write req ID.out.writeBytes(TWO_HYPHENS + BOUNDARY + LINE_END);out.writeBytes("Content-Disposition: form-data; name=\"id\"" + LINE_END);out.writeBytes(LINE_END);out.writeBytes(++mReqId + LINE_END);// Write topic.if (topic != null) {out.writeBytes(TWO_HYPHENS + BOUNDARY + LINE_END);out.writeBytes("Content-Disposition: form-data; name=\"topic\"" + LINE_END);out.writeBytes(LINE_END);out.writeBytes(topic + LINE_END);}// File section.out.writeBytes(TWO_HYPHENS + BOUNDARY + LINE_END);// Content-Disposition: form-data; name="file"; filename="1519014549699.pdf"out.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"" + filename + "\"" + LINE_END);// Content-Type: application/pdfout.writeBytes("Content-Type: " + mimetype + LINE_END);out.writeBytes("Content-Transfer-Encoding: binary" + LINE_END);out.writeBytes(LINE_END);// File bytes.copyStream(in, out, size, progress);out.writeBytes(LINE_END);// End of form boundary.out.writeBytes(TWO_HYPHENS + BOUNDARY + TWO_HYPHENS + LINE_END);out.flush();out.close();if (conn.getResponseCode() != 200) {throw new IOException("Failed to upload: " + conn.getResponseMessage() +" (" + conn.getResponseCode() + ")");}InputStream resp = new BufferedInputStream(conn.getInputStream());msg = readServerResponse(resp);resp.close();} finally {if (conn != null) {conn.disconnect();}}return msg;}// Uploads the file using Runnable, returns PromisedReply. Safe to call on UI thread.public PromisedReply<ServerMessage> uploadFuture(final InputStream in,final String filename,final String mimetype,final long size,final String topic,final FileHelperProgress progress) {final PromisedReply<ServerMessage> result = new PromisedReply<>();new Thread(() -> {try {ServerMessage msg = upload(in, filename, mimetype, size, topic, progress);if (mCanceled) {throw new CancellationException("Cancelled");}result.resolve(msg);} catch (Exception ex) {try {result.reject(ex);} catch (Exception ignored) {}}}).start();return result;}// Download file from the given URL if the URL's host is the default host. Should not be called on the UI thread.public long download(String downloadFrom, OutputStream out, FileHelperProgress progress)throws IOException, CancellationException {URL url = new URL(downloadFrom);long size = 0;String scheme = url.getProtocol();if (!scheme.equals("http") && !scheme.equals("https")) {// As a security measure refuse to download using non-http(s) protocols.return size;}HttpURLConnection urlConnection = null;try {urlConnection = (HttpURLConnection) url.openConnection();if (url.getHost().equals(mHost)) {// Send authentication only if the host is known.urlConnection.setRequestProperty("X-Tinode-APIKey", mApiKey);urlConnection.setRequestProperty("X-Tinode-Auth", "Token " + mAuthToken);}InputStream in = new BufferedInputStream(urlConnection.getInputStream());return copyStream(in, out, urlConnection.getContentLength(), progress);} finally {if (urlConnection != null) {urlConnection.disconnect();}}}// Downloads the file using Runnable, returns PromisedReply. Safe to call on UI thread.public PromisedReply<Long> downloadFuture(final String downloadFrom,final OutputStream out,final FileHelperProgress progress) {final PromisedReply<Long> result = new PromisedReply<>();new Thread(() -> {try {Long size = download(downloadFrom, out, progress);if (mCanceled) {throw new CancellationException("Cancelled");}result.resolve(size);} catch (Exception ex) {try {result.reject(ex);} catch (Exception ignored) {}}}).start();return result;}// Try to cancel an ongoing upload or download.public void cancel() {mCanceled = true;}public boolean isCanceled() {return mCanceled;}private int copyStream(@NotNull InputStream in, @NotNull OutputStream out, long size, @Nullable FileHelperProgress p)throws IOException, CancellationException {byte[] buffer = new byte[BUFFER_SIZE];int len, sent = 0;while ((len = in.read(buffer)) != -1) {if (mCanceled) {throw new CancellationException("Cancelled");}sent += len;out.write(buffer, 0, len);if (mCanceled) {throw new CancellationException("Cancelled");}if (p != null) {p.onProgress(sent, size);}}return sent;}private ServerMessage readServerResponse(InputStream in) throws IOException {MsgServerCtrl ctrl = null;ObjectMapper mapper = Tinode.getJsonMapper();JsonParser parser = mapper.getFactory().createParser(in);if (parser.nextToken() != JsonToken.START_OBJECT) {throw new JsonParseException(parser, "Packet must start with an object",parser.getCurrentLocation());}if (parser.nextToken() != JsonToken.END_OBJECT) {String name = parser.getCurrentName();parser.nextToken();JsonNode node = mapper.readTree(parser);if (name.equals("ctrl")) {ctrl = mapper.readValue(node.traverse(), MsgServerCtrl.class);} else {throw new JsonParseException(parser, "Unexpected message '" + name + "'",parser.getCurrentLocation());}}return new ServerMessage(ctrl);}public interface FileHelperProgress {void onProgress(long sent, long size);}public Map<String,String> headers() {Map<String,String> headers = new HashMap<>();headers.put("X-Tinode-APIKey", mApiKey);headers.put("X-Tinode-Auth", "Token " + mAuthToken);return headers;} }

這樣,登錄就OK了;

4)設置服務器默認參數

將服務器的鏈接參數預先設置好為我們需要的:

4.1) 地址與端口:全文搜索“:6060”字樣,在資源文件res/strings.xml中更改:

<string name="emulator_host_name" translatable="false">119.0.0.1:6060</string>

同時,將build.gradle的相關位置做更改,自動生成相關的資源文件

buildTypes {debug {resValue "string", "default_host_name", '"119.0.0.0:6060"'}release {resValue "string", "default_host_name", '"api.tinode.co"'minifyEnabled trueshrinkResources trueproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'signingConfig signingConfigs.release}

在同樣在資源中更改為自己的地址和端口;

4.2)默認使用https,更改TindroidApp.java

將返回的默認的參數設置為true

public static boolean getDefaultTLS() {//return !isEmulator();return true;}

編譯好了就可以用了!

5) 還需要更改LargeFileHelper,

在完成第4步驟后,發送小文件正常,大文件比如5兆,就報錯了,?

java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

這說明大文件使用其他的方法發送時驗證證書失敗了,果然,SDK中使用了單獨的一個輔助類單獨開了一個鏈接POST發送大文件,參考如下鏈接:Trust anchor for certification path not found異常解決方法_HZYXN的博客-CSDN博客

更改后的代碼如下:

package co.tinode.tinodesdk;import com.fasterxml.jackson.core.JsonParseException; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonToken; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper;import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable;import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.security.SecureRandom; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CancellationException;import co.tinode.tinodesdk.model.MsgServerCtrl; import co.tinode.tinodesdk.model.ServerMessage; // import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.*; import java.net.Socket; import java.net.URI; import java.security.cert.CertificateException; import java.security.cert.X509Certificate;public class LargeFileHelper {private static final int BUFFER_SIZE = 65536;private static final String TWO_HYPHENS = "--";private static final String BOUNDARY = "*****" + System.currentTimeMillis() + "*****";private static final String LINE_END = "\r\n";private final URL mUrlUpload;private final String mHost;private final String mApiKey;private final String mAuthToken;private final String mUserAgent;private boolean mCanceled = false;private int mReqId = 1;public LargeFileHelper(URL urlUpload, String apikey, String authToken, String userAgent) {mUrlUpload = urlUpload;mHost = mUrlUpload.getHost();mApiKey = apikey;mAuthToken = authToken;mUserAgent = userAgent;handleSSLHandshake();}// robin add herepublic static void handleSSLHandshake() {try {TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}@Overridepublic void checkClientTrusted(X509Certificate[] certs, String authType) {}@Overridepublic void checkServerTrusted(X509Certificate[] certs, String authType) {}}};SSLContext sc = SSLContext.getInstance("TLS");// trustAllCerts信任所有的證書sc.init(null, trustAllCerts, new SecureRandom());HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {@Overridepublic boolean verify(String hostname, SSLSession session) {return true;}});} catch (Exception ignored) {}}// Upload file out of band. This should not be called on the UI thread.public ServerMessage upload(@NotNull InputStream in, @NotNull String filename, @NotNull String mimetype, long size,@Nullable String topic, @Nullable FileHelperProgress progress) throws IOException, CancellationException {mCanceled = false;HttpURLConnection conn = null;ServerMessage msg;try {conn = (HttpURLConnection) mUrlUpload.openConnection();conn.setDoOutput(true);conn.setUseCaches(false);conn.setRequestProperty("Connection", "Keep-Alive");conn.setRequestProperty("User-Agent", mUserAgent);conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);conn.setRequestProperty("X-Tinode-APIKey", mApiKey);if (mAuthToken != null) {// mAuthToken could be null when uploading avatar on sign up.conn.setRequestProperty("X-Tinode-Auth", "Token " + mAuthToken);}conn.setChunkedStreamingMode(0);DataOutputStream out = new DataOutputStream(new BufferedOutputStream(conn.getOutputStream()));// Write req ID.out.writeBytes(TWO_HYPHENS + BOUNDARY + LINE_END);out.writeBytes("Content-Disposition: form-data; name=\"id\"" + LINE_END);out.writeBytes(LINE_END);out.writeBytes(++mReqId + LINE_END);// Write topic.if (topic != null) {out.writeBytes(TWO_HYPHENS + BOUNDARY + LINE_END);out.writeBytes("Content-Disposition: form-data; name=\"topic\"" + LINE_END);out.writeBytes(LINE_END);out.writeBytes(topic + LINE_END);}// File section.out.writeBytes(TWO_HYPHENS + BOUNDARY + LINE_END);// Content-Disposition: form-data; name="file"; filename="1519014549699.pdf"out.writeBytes("Content-Disposition: form-data; name=\"file\"; filename=\"" + filename + "\"" + LINE_END);// Content-Type: application/pdfout.writeBytes("Content-Type: " + mimetype + LINE_END);out.writeBytes("Content-Transfer-Encoding: binary" + LINE_END);out.writeBytes(LINE_END);// File bytes.copyStream(in, out, size, progress);out.writeBytes(LINE_END);// End of form boundary.out.writeBytes(TWO_HYPHENS + BOUNDARY + TWO_HYPHENS + LINE_END);out.flush();out.close();if (conn.getResponseCode() != 200) {throw new IOException("Failed to upload: " + conn.getResponseMessage() +" (" + conn.getResponseCode() + ")");}InputStream resp = new BufferedInputStream(conn.getInputStream());msg = readServerResponse(resp);resp.close();} finally {if (conn != null) {conn.disconnect();}}return msg;}// Uploads the file using Runnable, returns PromisedReply. Safe to call on UI thread.public PromisedReply<ServerMessage> uploadFuture(final InputStream in,final String filename,final String mimetype,final long size,final String topic,final FileHelperProgress progress) {final PromisedReply<ServerMessage> result = new PromisedReply<>();new Thread(() -> {try {ServerMessage msg = upload(in, filename, mimetype, size, topic, progress);if (mCanceled) {throw new CancellationException("Cancelled");}result.resolve(msg);} catch (Exception ex) {try {result.reject(ex);} catch (Exception ignored) {}}}).start();return result;}// Download file from the given URL if the URL's host is the default host. Should not be called on the UI thread.public long download(String downloadFrom, OutputStream out, FileHelperProgress progress)throws IOException, CancellationException {URL url = new URL(downloadFrom);long size = 0;String scheme = url.getProtocol();if (!scheme.equals("http") && !scheme.equals("https")) {// As a security measure refuse to download using non-http(s) protocols.return size;}HttpURLConnection urlConnection = null;try {urlConnection = (HttpURLConnection) url.openConnection();if (url.getHost().equals(mHost)) {// Send authentication only if the host is known.urlConnection.setRequestProperty("X-Tinode-APIKey", mApiKey);urlConnection.setRequestProperty("X-Tinode-Auth", "Token " + mAuthToken);}InputStream in = new BufferedInputStream(urlConnection.getInputStream());return copyStream(in, out, urlConnection.getContentLength(), progress);} finally {if (urlConnection != null) {urlConnection.disconnect();}}}// Downloads the file using Runnable, returns PromisedReply. Safe to call on UI thread.public PromisedReply<Long> downloadFuture(final String downloadFrom,final OutputStream out,final FileHelperProgress progress) {final PromisedReply<Long> result = new PromisedReply<>();new Thread(() -> {try {Long size = download(downloadFrom, out, progress);if (mCanceled) {throw new CancellationException("Cancelled");}result.resolve(size);} catch (Exception ex) {try {result.reject(ex);} catch (Exception ignored) {}}}).start();return result;}// Try to cancel an ongoing upload or download.public void cancel() {mCanceled = true;}public boolean isCanceled() {return mCanceled;}private int copyStream(@NotNull InputStream in, @NotNull OutputStream out, long size, @Nullable FileHelperProgress p)throws IOException, CancellationException {byte[] buffer = new byte[BUFFER_SIZE];int len, sent = 0;while ((len = in.read(buffer)) != -1) {if (mCanceled) {throw new CancellationException("Cancelled");}sent += len;out.write(buffer, 0, len);if (mCanceled) {throw new CancellationException("Cancelled");}if (p != null) {p.onProgress(sent, size);}}return sent;}private ServerMessage readServerResponse(InputStream in) throws IOException {MsgServerCtrl ctrl = null;ObjectMapper mapper = Tinode.getJsonMapper();JsonParser parser = mapper.getFactory().createParser(in);if (parser.nextToken() != JsonToken.START_OBJECT) {throw new JsonParseException(parser, "Packet must start with an object",parser.getCurrentLocation());}if (parser.nextToken() != JsonToken.END_OBJECT) {String name = parser.getCurrentName();parser.nextToken();JsonNode node = mapper.readTree(parser);if (name.equals("ctrl")) {ctrl = mapper.readValue(node.traverse(), MsgServerCtrl.class);} else {throw new JsonParseException(parser, "Unexpected message '" + name + "'",parser.getCurrentLocation());}}return new ServerMessage(ctrl);}public interface FileHelperProgress {void onProgress(long sent, long size);}public Map<String,String> headers() {Map<String,String> headers = new HashMap<>();headers.put("X-Tinode-APIKey", mApiKey);headers.put("X-Tinode-Auth", "Token " + mAuthToken);return headers;} }

編譯后,發送大文件不再報錯了。

6)還需要發送圖片時,無法正常彈出圖片瀏覽器框

調試過程中發現,是由于我們更改了應用程序的ID,造成默認的路徑發生了變化,在讀取臨時文件時候發生了錯誤,所以需要更改一下相關的路徑:provider_paths.xml

<?xml version="1.0" encoding="utf-8"?> <paths><external-path name="tindroid_downloads" path="./Download" /><external-path name="tindroid_images" path="Android/data/com.birdschat.cn/files/Pictures" /> </paths>

這里應該與applicationId中設置的一樣才行。

其實這里的錯誤,經過分析代碼,是因為MessagesFragment.java 在試圖發送圖片時候,程序會嘗試新建一個文件用于保存照片,所以才會使用了臨時文件,而這里由于邏輯問題,在真機上每次即使不拍照也會產生垃圾數據,所以決定禁用了拍照,選擇時候反而更加流程;

7)聊天頭像丟失的問題

這個其實也是認證的問題,主要是在ChatsAdapter.java中bing函數設置avatar時候,使用了piccaso來下載對應的圖片,但是不能直接改;因為在TindroidApp.java中初始化時候設置了Okhttp3下載工具,并且設置了相對鏈接的轉換方式,

所以應該改TindroidApp.java

package co.tinode.tindroid;import android.Manifest; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AuthenticatorException; import android.accounts.OperationCanceledException; import android.annotation.SuppressLint; import android.app.Application; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.database.ContentObserver; import android.net.ConnectivityManager; import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.provider.ContactsContract; import android.text.TextUtils; import android.util.Log;import com.google.firebase.crashlytics.FirebaseCrashlytics; import com.squareup.picasso.OkHttp3Downloader; import com.squareup.picasso.Picasso;import java.io.File; import java.io.IOException; import java.net.URL; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Date; import java.util.Map;import androidx.annotation.NonNull; import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.ProcessLifecycleOwner; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.preference.PreferenceManager; import androidx.work.WorkManager;import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager;import co.tinode.tindroid.account.ContactsObserver; import co.tinode.tindroid.account.Utils; import co.tinode.tindroid.db.BaseDb; import co.tinode.tinodesdk.ComTopic; import co.tinode.tinodesdk.ServerResponseException; import co.tinode.tinodesdk.Storage; import co.tinode.tinodesdk.Tinode; import co.tinode.tinodesdk.model.MsgServerData; import co.tinode.tinodesdk.model.MsgServerInfo;import okhttp3.OkHttpClient; import okhttp3.Request;/*** A class for providing global context for database access*/ public class TindroidApp extends Application implements DefaultLifecycleObserver {private static final String TAG = "TindroidApp";// 256 MB.private static final int PICASSO_CACHE_SIZE = 1024 * 1024 * 256;private static TindroidApp sContext;private static ContentObserver sContactsObserver = null;// The Tinode cache is linked from here so it's never garbage collected.@SuppressWarnings({"FieldCanBeLocal", "unused"})private static Cache sCache;private static String sAppVersion = null;private static int sAppBuild = 0;//private static String sServerHost = null;//private static boolean sUseTLS = false;public TindroidApp() {sContext = this;}public static Context getAppContext() {return sContext;}public static String getAppVersion() {return sAppVersion;}public static int getAppBuild() {return sAppBuild;}public static String getDefaultHostName(Context context) {return context.getResources().getString(isEmulator() ?R.string.emulator_host_name :R.string.default_host_name);}public static boolean getDefaultTLS() {//return !isEmulator();return true;}public static void retainCache(Cache cache) {sCache = cache;}// Detect if the code is running in an emulator.// Used mostly for convenience to use correct server address i.e. 10.0.2.2:6060 vs sandbox.tinode.co and// to enable/disable Crashlytics. It's OK if it's imprecise.public static boolean isEmulator() {return Build.FINGERPRINT.startsWith("sdk_gphone_x86")|| Build.FINGERPRINT.startsWith("unknown")|| Build.MODEL.contains("google_sdk")|| Build.MODEL.contains("Emulator")|| Build.MODEL.contains("Android SDK built for x86")|| Build.MANUFACTURER.contains("Genymotion")|| (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))|| "google_sdk".equals(Build.PRODUCT)|| Build.PRODUCT.startsWith("sdk")|| Build.PRODUCT.startsWith("vbox");}static synchronized void startWatchingContacts(Context context, Account acc) {if (sContactsObserver == null) {// Check if we have already obtained contacts permissions.if (!UiUtils.isPermissionGranted(context, Manifest.permission.READ_CONTACTS)) {// No permissions, can't set up contacts sync.return;}// Create and start a new thread set up as a looper.HandlerThread thread = new HandlerThread("ContactsObserverHandlerThread");thread.start();sContactsObserver = new ContactsObserver(acc, new Handler(thread.getLooper()));// Observer which triggers sync when contacts change.sContext.getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI,true, sContactsObserver);}}static synchronized void stopWatchingContacts() {if (sContactsObserver != null) {sContext.getContentResolver().unregisterContentObserver(sContactsObserver);}}// robin addpublic static OkHttpClient getUnsafeOkHttpClient(Context context){try {final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {@Overridepublic void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}@Overridepublic void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}@Overridepublic X509Certificate[] getAcceptedIssuers() {return new X509Certificate[]{};}}};X509TrustManager x509TrustManager = (X509TrustManager) trustAllCerts[0];final SSLContext sslContext = SSLContext.getInstance("SSL");sslContext.init(null, trustAllCerts, new SecureRandom());final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();OkHttpClient.Builder builder = new OkHttpClient.Builder();builder.sslSocketFactory(sslSocketFactory, x509TrustManager);builder.hostnameVerifier(new HostnameVerifier() {@Overridepublic boolean verify(String s, SSLSession sslSession) {return true;}});builder.cache(new okhttp3.Cache(createDefaultCacheDir(context), PICASSO_CACHE_SIZE)).addInterceptor(chain -> {Tinode tinode = Cache.getTinode();Request picassoReq = chain.request();Map<String, String> headers;if (tinode.isTrustedURL(picassoReq.url().url())) {headers = tinode.getRequestHeaders();Request.Builder builder1 = picassoReq.newBuilder();for (Map.Entry<String, String> el : headers.entrySet()) {builder1 = builder1.addHeader(el.getKey(), el.getValue());}return chain.proceed(builder1.build());} else {return chain.proceed(picassoReq);}});return builder.build();} catch (NoSuchAlgorithmException e) {e.printStackTrace();}catch (KeyManagementException e){e.printStackTrace();}return null;}@Overridepublic void onCreate() {super.onCreate();try {PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0);sAppVersion = pi.versionName;if (TextUtils.isEmpty(sAppVersion)) {sAppVersion = BuildConfig.VERSION_NAME;}sAppBuild = pi.versionCode;if (sAppBuild <= 0) {sAppBuild = BuildConfig.VERSION_CODE;}} catch (PackageManager.NameNotFoundException e) {Log.w(TAG, "Failed to retrieve app version", e);}// Disable Crashlytics for debug builds.FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(!BuildConfig.DEBUG);BroadcastReceiver br = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String token = intent.getStringExtra("token");if (token != null && !token.equals("")) {Cache.getTinode().setDeviceToken(token);}}};LocalBroadcastManager.getInstance(this).registerReceiver(br, new IntentFilter("FCM_REFRESH_TOKEN"));createNotificationChannel();ProcessLifecycleOwner.get().getLifecycle().addObserver(this);// Check if preferences already exist. If not, create them.SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);if (TextUtils.isEmpty(pref.getString(Utils.PREFS_HOST_NAME, null))) {// No preferences found. Save default values.SharedPreferences.Editor editor = pref.edit();editor.putString(Utils.PREFS_HOST_NAME, getDefaultHostName(this));editor.putBoolean(Utils.PREFS_USE_TLS, getDefaultTLS());editor.apply();}// Event handlers for video calls.Cache.getTinode().addListener(new Tinode.EventListener() {@Overridepublic void onDataMessage(MsgServerData data) {if (Cache.getTinode().isMe(data.from)) {return;}String webrtc = data.getStringHeader("webrtc");if (MsgServerData.parseWebRTC(webrtc) != MsgServerData.WebRTC.STARTED) {return;}ComTopic topic = (ComTopic) Cache.getTinode().getTopic(data.topic);if (topic == null) {return;}// Check if we have a later version of the message (which means the call// has been not yet been either accepted or finished).Storage.Message msg = topic.getMessage(data.seq);if (msg != null) {webrtc = msg.getStringHeader("webrtc");if (webrtc != null && MsgServerData.parseWebRTC(webrtc) != MsgServerData.WebRTC.STARTED) {return;}}CallInProgress call = Cache.getCallInProgress();if (call == null) {// Call invite from the peer.Intent intent = new Intent();intent.setAction(CallActivity.INTENT_ACTION_CALL_INCOMING);intent.putExtra("topic", data.topic);intent.putExtra("seq", data.seq);intent.putExtra("from", data.from);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);TindroidApp.this.startActivity(intent);} else if (!call.equals(data.topic, data.seq)) {// Another incoming call. Decline.topic.videoCallHangUp(data.seq);}}@Overridepublic void onInfoMessage(MsgServerInfo info) {if (MsgServerInfo.parseWhat(info.what) != MsgServerInfo.What.CALL) {return;}if (MsgServerInfo.parseEvent(info.event) != MsgServerInfo.Event.ACCEPT) {return;}CallInProgress call = Cache.getCallInProgress();if (Tinode.TOPIC_ME.equals(info.topic) && Cache.getTinode().isMe(info.from) &&call != null && call.equals(info.src, info.seq)) {// Another client has accepted the call. Dismiss call notification.LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(TindroidApp.this);Intent intent = new Intent(CallActivity.INTENT_ACTION_CALL_CLOSE);intent.putExtra("topic", info.src);intent.putExtra("seq", info.seq);lbm.sendBroadcast(intent);}}});// Clear completed/failed upload tasks.WorkManager.getInstance(this).pruneWork();// Setting up Picasso with auth headers. // OkHttpClient client = new OkHttpClient.Builder() // .cache(new okhttp3.Cache(createDefaultCacheDir(this), PICASSO_CACHE_SIZE)) // .addInterceptor(chain -> { // Tinode tinode = Cache.getTinode(); // Request picassoReq = chain.request(); // Map<String, String> headers; // if (tinode.isTrustedURL(picassoReq.url().url())) { // headers = tinode.getRequestHeaders(); // Request.Builder builder = picassoReq.newBuilder(); // for (Map.Entry<String, String> el : headers.entrySet()) { // builder = builder.addHeader(el.getKey(), el.getValue()); // } // return chain.proceed(builder.build()); // } else { // return chain.proceed(picassoReq); // } // }) // .build();// note herePicasso.setSingletonInstance(new Picasso.Builder(this).requestTransformer(request -> {// Rewrite relative URIs to absolute.if (request.uri != null && Tinode.isUrlRelative(request.uri.toString())) {URL url = Cache.getTinode().toAbsoluteURL(request.uri.toString());if (url != null) {return request.buildUpon().setUri(Uri.parse(url.toString())).build();}}return request;}).downloader(new OkHttp3Downloader(getUnsafeOkHttpClient(this))).build());// Listen to connectivity changes.ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);if (cm == null) {return;}NetworkRequest req = new NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET).build();cm.registerNetworkCallback(req, new ConnectivityManager.NetworkCallback() {@Overridepublic void onAvailable(@NonNull Network network) {super.onAvailable(network);if (!TextUtils.isEmpty(BaseDb.getInstance().getUid())) {// Connect right away if UID is available.Cache.getTinode().reconnectNow(true, false, false);}}});}static File createDefaultCacheDir(Context context) {File cache = new File(context.getApplicationContext().getCacheDir(), "picasso-cache");if (!cache.exists()) {// noinspection ResultOfMethodCallIgnoredcache.mkdirs();}return cache;}@Overridepublic void onStart(@NonNull LifecycleOwner owner) {// Check if the app has an account already. If so, initialize the shared connection with the server.// Initialization may fail if device is not connected to the network.String uid = BaseDb.getInstance().getUid();if (!TextUtils.isEmpty(uid)) {new LoginWithSavedAccount().execute(uid);}}@Overridepublic void onStop(@NonNull LifecycleOwner owner) {// Disconnect now, so the connection does not wait for the timeout.if (Cache.getTinode() != null) {Cache.getTinode().maybeDisconnect(false);}}private void createNotificationChannel() {// Create the NotificationChannel on API 26+if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {NotificationChannel channel = new NotificationChannel("new_message",getString(R.string.notification_channel_name), NotificationManager.IMPORTANCE_DEFAULT);channel.setDescription(getString(R.string.notification_channel_description));NotificationManager nm = getSystemService(NotificationManager.class);if (nm != null) {nm.createNotificationChannel(channel);}}}// Read saved account credentials and try to connect to server using them.// Suppressed lint warning because TindroidApp won't leak: it must exist for the entire lifetime of the app.@SuppressLint("StaticFieldLeak")private class LoginWithSavedAccount extends AsyncTask<String, Void, Void> {@Overrideprotected Void doInBackground(String... uidWrapper) {final AccountManager accountManager = AccountManager.get(TindroidApp.this);final Account account = Utils.getSavedAccount(accountManager, uidWrapper[0]);if (account != null) {// Check if sync is enabled.if (ContentResolver.getMasterSyncAutomatically()) {if (!ContentResolver.getSyncAutomatically(account, Utils.SYNC_AUTHORITY)) {ContentResolver.setSyncAutomatically(account, Utils.SYNC_AUTHORITY, true);}}// Account found, establish connection to the server and use save account credentials for login.String token = null;Date expires = null;try {token = accountManager.blockingGetAuthToken(account, Utils.TOKEN_TYPE, false);String strExp = accountManager.getUserData(account, Utils.TOKEN_EXPIRATION_TIME);// FIXME: remove this check when all clients are updated; Apr 8, 2020.if (!TextUtils.isEmpty(strExp)) {expires = new Date(Long.parseLong(strExp));}} catch (OperationCanceledException e) {Log.i(TAG, "Request to get an existing account was canceled.", e);} catch (AuthenticatorException e) {Log.e(TAG, "No access to saved account", e);} catch (Exception e) {Log.e(TAG, "Failure to login with saved account", e);}// Must instantiate tinode cache even if token == null. Otherwise logout won't work.final Tinode tinode = Cache.getTinode();if (!TextUtils.isEmpty(token) && expires != null && expires.after(new Date())) {// Connecting with synchronous calls because this is not the UI thread.tinode.setAutoLoginToken(token);// Connect and login.try {SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(TindroidApp.this);// Sync call throws on error.tinode.connect(pref.getString(Utils.PREFS_HOST_NAME, getDefaultHostName(TindroidApp.this)),pref.getBoolean(Utils.PREFS_USE_TLS, getDefaultTLS()),false).getResult();if (!tinode.isAuthenticated()) {// The connection may already exist but not yet authenticated.tinode.loginToken(token).getResult();}Cache.attachMeTopic(null);// Logged in successfully. Save refreshed token for future use.accountManager.setAuthToken(account, Utils.TOKEN_TYPE, tinode.getAuthToken());accountManager.setUserData(account, Utils.TOKEN_EXPIRATION_TIME,String.valueOf(tinode.getAuthTokenExpiration().getTime()));startWatchingContacts(TindroidApp.this, account);// Trigger sync to be sure contacts are up to date.UiUtils.requestImmediateContactsSync(account);} catch (IOException ex) {Log.d(TAG, "Network failure during login", ex);// Do not invalidate token on network failure.} catch (ServerResponseException ex) {Log.w(TAG, "Server rejected login sequence", ex);int code = ex.getCode();// 401: Token expired or invalid login.// 404: 'me' topic is not found (user deleted, but token is still valid).if (code == 401 || code == 404) {// Another try-catch because some users revoke needed permission after granting it.try {// Login failed due to invalid (expired) token or missing/disabled account.accountManager.invalidateAuthToken(Utils.ACCOUNT_TYPE, null);accountManager.setUserData(account, Utils.TOKEN_EXPIRATION_TIME, null);} catch (SecurityException ex2) {Log.e(TAG, "Unable to access android account", ex2);}// Force new login.UiUtils.doLogout(TindroidApp.this);}// 409 Already authenticated should not be possible here.} catch (Exception ex) {Log.e(TAG, "Other failure during login", ex);}} else {Log.i(TAG, "No token or expired token. Forcing re-login");try {if (!TextUtils.isEmpty(token)) {accountManager.invalidateAuthToken(Utils.ACCOUNT_TYPE, null);}accountManager.setUserData(account, Utils.TOKEN_EXPIRATION_TIME, null);} catch (SecurityException ex) {Log.e(TAG, "Unable to access android account", ex);}// Force new login.UiUtils.doLogout(TindroidApp.this);}} else {Log.i(TAG, "Account not found or no permission to access accounts");// Force new login in case account existed before but was deleted.UiUtils.doLogout(TindroidApp.this);}return null;}} }

頭像已經可以出現了,

?8) 還剩下一個問題,就是有時候某些圖片發送好像失敗了,

待查

結束。

備注:編譯好的apk?https://download.csdn.net/download/robinfoxnan/87300700

總結

以上是生活随笔為你收集整理的tinode客户端安卓版编译手账的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

久久久久黄色 | 欧美人交a欧美精品 | 精品亚洲视频在线观看 | 国产毛片久久 | 日韩免费播放 | 国产精品福利av | 美女福利视频在线 | 久久美女电影 | 日韩网站一区二区 | av在线播放快速免费阴 | 91.精品高清在线观看 | 91看片淫黄大片在线播放 | 在线观看中文字幕2021 | 人人要人人澡人人爽人人dvd | 精品久久1 | 日本最新高清不卡中文字幕 | 91精品免费在线观看 | 丝袜+亚洲+另类+欧美+变态 | 九九热精品视频在线播放 | 四虎永久国产精品 | 国产成人精品av久久 | 久久嗨| 免费看三级 | 天天干国产 | 五月婷婷中文字幕 | 1024在线看片 | 成人a视频片观看免费 | 久草影视在线 | 日韩肉感妇bbwbbwbbw | 国产手机在线观看 | 狠狠狠狠狠狠操 | 人人爱人人添 | 婷婷亚洲综合五月天小说 | 少妇精品久久久一区二区免费 | 一本一本久久a久久精品综合 | 色噜噜狠狠色综合中国 | 成人在线观看资源 | 国产又黄又猛又粗 | 国产特黄色片 | 深爱开心激情 | 成年人在线 | 亚洲色图色 | 色综合久久综合网 | 九色激情网 | 久久精品看片 | 一二三区av | 色综合久久五月 | 国产男女爽爽爽免费视频 | 热久久影视 | av电影免费观看 | 日韩av在线免费播放 | 豆豆色资源网xfplay | 久久永久视频 | 成人9ⅰ免费影视网站 | 欧美一二区在线 | 韩国av电影在线观看 | 97国产在线观看 | 国产综合视频在线观看 | 成人在线播放网站 | 中文字幕av全部资源www中文字幕在线观看 | 超级碰碰碰免费视频 | 久草在线高清视频 | 国产亚洲va综合人人澡精品 | 在线色网站 | www.xxxx欧美| 国产99久久久久久免费看 | 国产精品国产自产拍高清av | 人人爽久久久噜噜噜电影 | 国产成人黄色av | 亚洲一级片| 九月婷婷综合网 | 免费影视大全推荐 | 99久国产| 国产精品久久久久久久久大全 | 日韩午夜精品 | 麻豆国产精品va在线观看不卡 | 九九热1| 91精品久久久久久久久 | 欧美一级日韩免费不卡 | 中文字幕在线观看一区二区 | 99午夜| www夜夜操 | 欧美性成人 | 色吊丝在线永久观看最新版本 | 在线免费观看视频 | 欧美色888 | 色婷婷国产精品 | 香蕉网在线 | 国产中出在线观看 | 久久伊99综合婷婷久久伊 | 在线观看欧美成人 | 国产日韩欧美在线观看视频 | 91丨九色丨蝌蚪丰满 | 欧美色插 | 久久精品视频2 | 国产不卡一二三区 | 久久久久久久久影视 | 日韩精品一区二区三区免费观看 | 91视频亚洲| 日韩欧美一区二区三区在线观看 | 免费看91的网站 | 欧美日韩一区二区三区免费视频 | 人人揉人人揉人人揉人人揉97 | 久久久综合精品 | www中文在线 | 精品96久久久久久中文字幕无 | 91在线国产观看 | 亚洲天堂毛片 | 国产精品久久久久久久久久免费看 | 欧美一级片免费观看 | 亚洲精品国产综合99久久夜夜嗨 | 天天天天天天操 | 欧美亚洲精品一区 | 99视频偷窥在线精品国自产拍 | 日韩精品不卡 | 亚洲国产高清视频 | 国产999精品久久久久久绿帽 | 国产天天综合 | 亚洲精品综合一二三区在线观看 | 日韩av免费在线电影 | 日韩黄视频 | 三上悠亚一区二区在线观看 | 久久这里只有精品视频首页 | 91在线视频观看 | 91精品免费在线观看 | 日韩在线精品一区 | 国产999精品久久久影片官网 | 国产又粗又猛又色又黄网站 | 狠狠干天天色 | 婷婷av综合 | 成年人在线观看视频免费 | 香蕉成人在线视频 | 国产二区免费视频 | 亚洲乱码在线观看 | 九九热精品视频在线观看 | 9草在线 | 精品在线播放视频 | 91伊人影院 | 久久久久久久久久久久99 | 日韩a级黄色 | 色全色在线资源网 | 96亚洲精品久久久蜜桃 | 五月天婷婷免费视频 | 亚洲天堂免费视频 | 国产精品福利一区 | 在线观看亚洲电影 | 蜜臀久久99精品久久久无需会员 | 久久精品久久精品 | 精品国产成人 | 免费黄色在线网站 | 亚洲成人午夜av | 欧美二区在线播放 | 九9热这里真品2 | 亚洲免费公开视频 | 国产视频日韩视频欧美视频 | 五月婷婷在线观看 | 中文字幕av在线 | 国产精品无 | 91看片在线看片 | 午夜成人免费影院 | 黄色中文字幕在线 | 成人资源网 | 欧美日韩国产高清视频 | 正在播放一区 | 在线观看你懂的网址 | 美女精品久久久 | 久久精品aaa| 亚洲国产婷婷 | 在线视频 日韩 | 久久综合综合久久综合 | 亚洲一区美女视频在线观看免费 | 成人91av | 99视频在线免费播放 | 日韩免费看 | 毛片美女网站 | 亚洲涩涩涩 | 久久香蕉一区 | 国产精品video爽爽爽爽 | 91最新网址 | 亚洲成av人片在线观看香蕉 | 久久久999免费视频 日韩网站在线 | 国产亚洲精品bv在线观看 | 亚洲亚洲精品在线观看 | 黄色亚洲片| 亚洲国产视频在线 | 成人一区二区在线 | 日韩精品在线视频 | 国产美女主播精品一区二区三区 | 最近2019年日本中文免费字幕 | 中文字幕在线高清 | 丁香在线观看完整电影视频 | 久久免费播放视频 | 色综合久久久久久久久五月 | 国产精品一区二区久久精品爱涩 | 欧美日韩在线观看不卡 | 久久免费99| 天天综合狠狠精品 | 黄色一区二区在线观看 | 亚洲欧美婷婷六月色综合 | 欧美日韩精 | 久久乐九色婷婷综合色狠狠182 | 日本久久不卡视频 | 日韩一区二区免费在线观看 | 人人干人人干人人干 | 久久国产热视频 | 狠狠的操狠狠的干 | 亚洲视频精选 | 午夜精品久久久99热福利 | 亚洲精品久久久久久久不卡四虎 | 国产精品av在线 | 中文字幕中文中文字幕 | 婷婷六月丁 | 91av在| 成年人黄色免费看 | 国产亚洲在线视频 | 81国产精品久久久久久久久久 | 国色天香永久免费 | 综合网av | 美女在线黄| 美女网站免费福利视频 | 国产丝袜高跟 | 韩国在线一区二区 | 国产片免费在线观看视频 | 亚一亚二国产专区 | 成年人国产在线观看 | av一区二区三区在线 | 久久综合五月天 | 玖操| 综合五月婷婷 | 亚洲综合视频在线 | 久久久久久久国产精品视频 | 日本黄色免费播放 | 国产999在线观看 | 在线视频国产区 | 中文字幕在线视频一区二区 | 97国产在线视频 | 在线免费观看麻豆视频 | 久久午夜视频 | 国产成人一级电影 | 天天干天天草天天爽 | 久久av不卡 | 婷婷色吧 | 亚洲黄色在线播放 | 国产很黄很色的视频 | 午夜国产福利在线 | 91精品在线播放 | 国产偷在线 | 成人免费观看视频大全 | 久久久精品在线观看 | 九色免费视频 | 久久伊人五月天 | ,久久福利影视 | 人人澡人人干 | 久久不射影院 | 精品国产大片 | 国产免费高清 | 久久久久国产精品视频 | 日韩成人精品 | av中文在线| 色wwwww| 婷婷亚洲综合 | 青青河边草手机免费 | 日韩a级免费视频 | 欧美日韩超碰 | 久久视频在线观看 | 久久精品中文字幕少妇 | 中文字幕刺激在线 | 五月开心综合 | 亚洲在线日韩 | 国产99久久久久久免费看 | 国产精品久久久久久模特 | 国产男女免费完整视频 | 成人香蕉视频 | 碰天天操天天 | 偷拍视频一区 | 日本中文字幕网 | 久草观看视频 | 久久精品视频播放 | 一区二区三区免费网站 | 麻豆国产在线视频 | 黄色av电影在线观看 | 在线观看国产福利片 | 97精品国产97久久久久久 | 美女国产精品 | 国产群p| 中文字幕 婷婷 | 成人免费大片黄在线播放 | 我要色综合天天 | 国产黄色精品视频 | 国产精品免费成人 | 中文字幕人成一区 | 成人黄色片在线播放 | 四虎4hu永久免费 | 久久涩视频 | 久久在现 | 久久久久伦理电影 | 欧美福利网址 | 99久久精品日本一区二区免费 | 超碰999 | 国产三级视频在线 | 国产精品久久久一区二区三区网站 | 中文字幕视频三区 | 国产在线久草 | 黄色小说免费在线观看 | 天天爱天天操 | 欧日韩在线视频 | 婷婷综合成人 | 青青河边草免费观看完整版高清 | 久久婷婷精品视频 | 成人小电影在线看 | 午夜视频黄 | 激情小说网站亚洲综合网 | 免费在线观看av网址 | 久久国产高清 | 久久久久综合网 | 国产一区二区在线影院 | 国产精品成人免费精品自在线观看 | 久久久久久久国产精品视频 | 久久国内精品视频 | 九色一区二区 | 久久久999精品视频 国产美女免费观看 | 亚洲最新在线 | 日韩免费成人 | 亚洲精品毛片一级91精品 | 日韩两性视频 | 国产精品一区一区三区 | 久草在线手机观看 | 免费热情视频 | 色吧av色av | 国产中文字幕久久 | 久久免费片 | 在线观看国产www | 色香蕉网 | 午夜久久精品 | 在线免费观看视频你懂的 | 99自拍视频在线观看 | 久久五月婷婷综合 | 亚洲国产精品传媒在线观看 | 国产精品久久久久久久久久久免费 | 亚洲天天在线 | 日日夜夜狠狠干 | 久久久久久久久久久黄色 | 麻豆视频免费在线观看 | 欧美日性视频 | 日本女人的性生活视频 | 国产亚洲人成网站在线观看 | 日韩av女优视频 | 久久久不卡影院 | www色com| 亚洲精品免费在线观看 | 激情小说网站亚洲综合网 | 99国产精品一区二区 | 亚洲精品中文字幕在线观看 | 婷婷在线精品视频 | 国产伦精品一区二区三区在线 | 亚洲 欧洲 国产 日本 综合 | 一区二区三区高清在线观看 | 日韩精品一区二区在线 | 免费涩涩网站 | 成年人av在线播放 | 亚洲精品视频免费在线 | 99精品视频免费在线观看 | 国内免费的中文字幕 | 日韩高清一二区 | 日日操天天爽 | 欧美一区二区日韩一区二区 | 色婷婷中文 | 久久99精品国产一区二区三区 | www日日| 欧美日韩国产精品一区 | 13日本xxxxxⅹxxx20 | 91久色蝌蚪 | 日韩av影视在线 | av免费网站观看 | 中文字幕第一页在线视频 | 久久99热久久99精品 | 亚洲欧美视频在线 | 日韩二区在线 | 国产精品免费观看久久 | 玖玖视频精品 | 97精品超碰一区二区三区 | 日韩精品一区二区三区在线播放 | 中文字幕中文字幕 | av中文国产 | 国产中文字幕一区二区 | 在线观看视频你懂 | 久久艹中文字幕 | 毛片网站在线观看 | 91视频免费网站 | 久久y| 91精品免费 | 一区二区不卡视频在线观看 | 免费色视频网站 | 手机成人在线电影 | 久久久国产精品网站 | 欧美人人 | 欧美另类成人 | 国产精品久久久久久一区二区三区 | 欧美成人理伦片 | 91热精品| 亚洲国产欧美一区二区三区丁香婷 | 久久激情婷婷 | a在线免费 | 欧美另类z0zx| 精品夜夜嗨av一区二区三区 | 欧美成人精品三级在线观看播放 | 99久久精品网 | 欧美日韩在线观看一区二区 | 国内外成人免费在线视频 | 久久精品直播 | 国产精品久久视频 | 一区二区三区四区不卡 | 日韩欧美在线高清 | 国产a国产a国产a | 热re99久久精品国产99热 | 国产精品乱码久久久 | 日本中文在线观看 | 一级片观看 | 麻花豆传媒一二三产区 | 亚洲国产精品久久久久婷婷884 | 免费国产在线观看 | 97在线观看免费观看 | 91av视频导航 | 免费网站在线观看成人 | 国产高清在线免费观看 | 久久综合五月天婷婷伊人 | 天天爱天天色 | 99 国产精品| 亚洲精品视频二区 | 四虎在线观看精品视频 | 在线精品一区二区 | 久久久精品在线观看 | av一级一片 | 人人爽人人澡 | 精品国产99 | 在线观看国产永久免费视频 | 色av色av色av| 国产剧情一区二区 | 久久久午夜剧场 | 在线观看蜜桃视频 | 香蕉影院在线播放 | 亚洲日日射 | 国产亚洲成人网 | 色中色综合 | 五月的婷婷 | 91精品第一页 | 成人黄色一级视频 | 激情动态 | 91在线精品播放 | 久久亚洲福利视频 | 久久看免费视频 | 色av网站 | 久久免费大片 | 丁香花中文字幕 | 亚洲播播| 亚洲黄色成人网 | 天天爽人人爽夜夜爽 | 国产一区二区在线免费视频 | 久草网站在线 | 欧美日韩免费看 | 国产在线中文 | 精品久久久久久久久中文字幕 | 婷婷综合伊人 | 久久久久久国产精品久久 | 日韩不卡高清视频 | 中文字幕资源站 | 日韩免费中文字幕 | 免费福利视频网站 | 黄网站app在线观看免费视频 | 九九久久精品视频 | 亚一亚二国产专区 | 天天色视频 | 国产精品视频你懂的 | 亚洲国产网址 | 成人精品影视 | 日韩欧美在线第一页 | 在线免费观看视频a | 日韩最新在线视频 | 夜夜爽夜夜操 | 色婷婷www| 丁香花在线视频观看免费 | 热久久最新地址 | 久久五月网 | 欧美日韩久 | 91精品国产欧美一区二区成人 | 亚洲成人999 | 天天鲁天天干天天射 | 一区二区三区在线免费观看视频 | 色a资源在线 | 国产精品五月天 | 二区三区视频 | 日韩精品一区二区三区在线播放 | 人交video另类hd | a√天堂中文在线 | 久久久久久久久爱 | 中文视频在线播放 | 少妇bbbb| 视频在线一区二区三区 | 久久免费视频4 | 日韩网站在线看片你懂的 | 99热最新| 美女网站在线观看 | 麻花豆传媒mv在线观看 | 欧美淫视频 | 国产精品成人久久久久 | 国产精品亚洲片在线播放 | 色综合 久久精品 | 久久视频这里有久久精品视频11 | 91视频免费看 | 国产视频日韩 | 日韩中文字幕国产 | a级国产乱理论片在线观看 伊人宗合网 | 男女日麻批 | 精品福利网 | 干狠狠| 亚洲在线日韩 | 国产手机在线观看视频 | 麻豆超碰 | 国产美女视频免费 | 国产视频黄 | 久久久电影网站 | 夜夜躁日日躁狠狠久久av | 久久精品国产99 | 91成年视频 | 色综合婷婷| 日韩在线播放av | 91精品免费视频 | 99精品热视频只有精品10 | 中文字幕在线字幕中文 | 国产成人精品在线 | 综合色亚洲 | 亚洲久草在线视频 | 午夜国产一区二区三区四区 | 国产91精品看黄网站在线观看动漫 | 一区二区亚洲精品 | 欧美精品久久久久久久久免 | 国产在线精品一区 | 91成年人在线观看 | 99精品一区二区三区 | 国产a视频免费观看 | 香蕉一区| 天天草天天干天天射 | 96香蕉视频| 99热这里只有精品免费 | 99久久精品日本一区二区免费 | 国产高清久久久久 | 国产一区在线免费观看视频 | 视频在线观看入口黄最新永久免费国产 | 成人一区影院 | 色五月成人 | 日韩试看 | 91av综合| 午夜精品一区二区三区四区 | av色网站 | 91porny九色在线播放 | 久久精品9 | 亚洲三级在线播放 | 成人动漫一区二区三区 | 91c网站色版视频 | 日韩亚洲在线视频 | 成人av久久 | 天天干夜夜夜操天 | 91污污| 奇米影视8888在线观看大全免费 | 日韩精品免费在线观看视频 | 人人爽人人射 | 亚洲精品欧洲精品 | www黄色com | 69久久久 | 国产亚洲婷婷 | а天堂中文最新一区二区三区 | 国产色网站 | 欧美一区,二区 | 久久精品国产亚洲aⅴ | 91精品成人久久 | 欧美成人基地 | 精品国产乱码久久久久久1区二区 | 免费在线观看日韩 | 免费视频18| 国产成人精品日本亚洲999 | 欧美精品一区二区在线播放 | 成人午夜毛片 | 91麻豆精品91久久久久同性 | 免费看黄色毛片 | 99r在线精品| 日p视频在线观看 | 97色在线观看免费视频 | 亚洲国产伊人 | 久久99精品一区二区三区三区 | 91福利区一区二区三区 | 91视视频在线直接观看在线看网页在线看 | 久久免费视频在线观看6 | 黄色av观看| 99久久日韩精品视频免费在线观看 | 久久免费毛片 | 在线看片91| 99c视频高清免费观看 | 国产破处精品 | 91av电影在线 | 91亚洲精品国偷拍自产在线观看 | 免费观看国产成人 | 精品女同一区二区三区在线观看 | 国产精品久久久久久久久久99 | 91精品在线视频观看 | 狠狠操欧美 | 国产99自拍| 欧美一区二区三区免费看 | 国产91丝袜在线播放动漫 | 国产打女人屁股调教97 | 亚洲国产精品999 | 欧美激情精品久久久久久免费印度 | 色久天 | 成人免费一区二区三区在线观看 | 99热在线这里只有精品 | 日韩在线欧美在线 | 波多野结衣在线中文字幕 | 久久免费精品 | 黄污污网站| 亚洲精品乱码久久久久久蜜桃欧美 | 97超碰人人| 91久久国产露脸精品国产闺蜜 | 视频成人 | 亚洲色图色 | 中文字幕欧美日韩va免费视频 | 成人av电影免费 | 国内精品久久久久久中文字幕 | 国产在线国产 | 免费av片在线 | 国产精品精品视频 | 美女网站视频一区 | 97在线观看视频免费 | 五月婷婷影视 | 国产aaa毛片| 国产五月天婷婷 | www黄在线| 国产视频午夜 | 午夜视频播放 | 国产精品99在线播放 | 香蕉免费在线 | 欧美动漫一区二区三区 | 6080yy精品一区二区三区 | 日韩亚洲在线 | 在线免费观看国产精品 | 国产高清中文字幕 | 深爱激情综合 | 久久在线精品视频 | 成人午夜电影在线 | 2018好看的中文在线观看 | 亚洲精品乱码久久久久v最新版 | 在线免费黄色av | 婷婷激情小说网 | 日韩视频在线播放 | 日韩视频在线不卡 | 国产精品久久久久久久久软件 | 日韩 精品 一区 国产 麻豆 | 日韩精品中字 | 国产亚洲aⅴaaaaaa毛片 | av资源免费看 | 久久y| 国产精品精品 | 欧美日韩一区二区在线观看 | 毛片1000部免费看 | 精品亚洲男同gayvideo网站 | 精品国产免费av | 色五月色开心色婷婷色丁香 | 正在播放国产91 | 欧美a在线看 | 日韩精品极品视频 | 国产午夜精品一区二区三区嫩草 | 丁香在线观看完整电影视频 | 久久久久久久久久久国产精品 | 国产中文字幕一区二区 | 99精品乱码国产在线观看 | 精品播放 | 精品久久国产一区 | 1024久久| 国产在线观看你懂的 | 日韩久久精品一区二区 | 久久久免费高清视频 | 欧美最猛性xxxxx亚洲精品 | 久久久久久网址 | 四虎在线视频免费观看 | 午夜少妇一区二区三区 | 91传媒在线观看 | 久久国产免| 国产一级免费播放 | www.色午夜.com | 精品久久久久久久久久国产 | 一级α片 | 国产亚州精品视频 | 人人精品| 国产视频1区2区 | 欧美日韩精品免费观看视频 | 97在线看片| 在线免费高清 | 色视频在线免费观看 | 国产一区电影在线观看 | 国产1区在线观看 | www.伊人网 | 国产精品美女久久久久久久 | 美女精品 | 国产你懂的在线 | 精品亚洲视频在线观看 | 91手机视频 | 日韩欧美视频在线观看免费 | 九月婷婷人人澡人人添人人爽 | 懂色av懂色av粉嫩av分享吧 | 亚洲免费观看在线视频 | 天堂在线一区 | 中文字幕一区二区在线播放 | 久久婷婷视频 | 狠狠成人| 亚洲精品高清在线观看 | 国产又黄又猛又粗 | 在线免费精品视频 | 欧美在线aa| 欧美不卡视频在线 | 欧美日韩精品在线免费观看 | 天天草天天插 | 国产精品成人自产拍在线观看 | 久久99亚洲网美利坚合众国 | 国产中文| 不卡视频在线看 | 国产原创91| 在线看成人片 | 亚洲精品免费在线 | 国产精品久久久亚洲 | 99产精品成人啪免费网站 | 色在线亚洲 | 午夜精品久久久久久 | 91亚洲国产| 久久福利 | 91视频专区| 99久久精品久久亚洲精品 | 日韩高清成人在线 | www.天天干| 精品一区二区免费视频 | 四虎国产精 | 亚洲午夜电影网 | 日日夜夜操操 | 97精品国产97久久久久久粉红 | 国产精品久久久久久久久久久久 | 成人av网站在线播放 | 天堂av色婷婷一区二区三区 | 99久e精品热线免费 99国产精品久久久久久久久久 | 九九精品毛片 | 成人黄色电影在线播放 | 国产日韩欧美中文 | 一区二区三区动漫 | 91视频高清| a级一a一级在线观看 | 国产精久久久 | 在线观看岛国片 | www.亚洲黄| 伊人婷婷激情 | 日本成人免费在线观看 | 97电院网手机版 | 91综合色 | 久久黄色精品视频 | 久久高清免费视频 | 热精品| 99视频精品全部免费 在线 | 婷婷激情综合五月天 | 国产色网站 | 手机在线看永久av片免费 | 精品黄色片| 国产一区视频在线 | 国产精品99久久久精品 | 久久精品一区二区三区四区 | 欧美日韩另类在线 | 一级黄色片在线播放 | 成人国产精品入口 | 人人爽人人澡 | 91精品推荐| 成人精品久久久 | 四虎成人精品永久免费av | 免费看一及片 | 色悠悠久久综合 | 国产成人av网站 | 久久久网址 | 五月天电影免费在线观看一区 | www.天天成人国产电影 | 欧美久久99 | 九色激情网 | 美女视频黄免费网站 | 中文字幕4| 美女黄频网站 | 97视频免费播放 | 中文字幕一区二区三区四区在线视频 | 黄色小说网站在线 | 深爱激情亚洲 | 日韩午夜大片 | 在线观看视频黄 | 亚洲国产日韩欧美在线 | 91探花视频 | 天天操天天添 | 久久av高清 | 日韩手机在线 | 中文字幕在线高清 | 开心色插| 久久久免费毛片 | 91精品久久久久 | www.狠狠插.com | av免费观看在线 | 亚洲国产精品日韩 | 丁香在线 | 国内精品久久久久久 | 亚洲天堂在线观看完整版 | 九九热视频在线播放 | 狠狠色噜噜狠狠 | 欧美国产亚洲精品久久久8v | 色婷婷综合成人av | 久久影视中文字幕 | 免费成人黄色av | 69国产精品视频 | 国产在线精品播放 | 在线欧美日韩 | 欧美黑人性爽 | 99久久精品国产免费看不卡 | www.亚洲视频.com| 伊人天堂av | 欧美大片mv免费 | av网站在线免费观看 | 97视频人人| 又黄又爽又刺激 | 午夜视频免费播放 | 午夜精品久久久久久久久久久久 | 色婷婷av一区 | 国产系列在线观看 | 亚州五月| 久久激情小视频 | 亚洲免费国产视频 | 国产91影院| 欧美日韩国产xxx | 国产区精品视频 | 日韩美女黄色片 | 国产美女精品人人做人人爽 | 久久综合九色综合久久久精品综合 | 国产高清在线不卡 | 国产精品久久久久久久久免费看 | 超碰在线最新地址 | 视频二区在线 | 免费亚洲一区二区 | 免费久久久久久久 | 九草视频在线 | 五月天久久久久久 | 色吊丝在线永久观看最新版本 | 久久久久久久综合色一本 | 国产亚洲精品久久久久动 | 亚洲一级片在线观看 | 亚洲一级国产 | 欧美亚洲精品在线观看 | 狠狠操狠狠干天天操 | 婷婷丁香花 | 天天操狠狠操网站 | 中文字幕在线观看第三页 | 激情欧美一区二区三区免费看 | 国产成人免费av电影 | 丁香六月天| av在线亚洲天堂 | 在线免费看黄色 | 人人艹人人 | 久草久草在线观看 | 日韩免费av网址 | 手机看片中文字幕 | 国产成人a亚洲精品v | 亚洲专区免费观看 | 亚洲精品视频在线 | 久草在线中文视频 | 色综合久久精品 | 中文字幕视频播放 | 国产一级黄色av | 国产精品99久久久久久小说 | 国产亚洲精品av | 久久精品网站免费观看 | 91.dizhi永久地址最新 | 国产精品国产三级国产不产一地 | a级国产乱理论片在线观看 伊人宗合网 | 97超碰人人澡人人 | 欧美亚洲国产一卡 | a级片在线播放 | 91av99| 久草在线费播放视频 | 四虎影院在线观看av | 久久免费av电影 | 激情小说网站亚洲综合网 | 久久99精品国产99久久 | 欧美一级专区免费大片 | 国产黄色免费看 | 91九色视频观看 | 久久视频这里有久久精品视频11 | 国产精品福利在线 | 色婷婷亚洲 | 欧美网址在线观看 | 国产精品美女久久久免费 | 热久久99这里有精品 | 97人人超碰在线 | 在线成人中文字幕 | 在线高清一区 | 69国产盗摄一区二区三区五区 | 日本最新高清不卡中文字幕 | 久草在线视频看看 | 国产三级国产精品国产专区50 | 亚洲精品自在在线观看 | 中文字幕黄色网址 | 日日操夜 | 黄色app网站在线观看 | 99久久婷婷国产综合精品 | 欧美成人影音 | 色全色在线资源网 | 人人操日日干 | 一区二区三区在线视频111 | mm1313亚洲精品国产 | 伊人官网 | 日日干网址 | 欧美一二三区在线播放 | 国产一级视频在线 | 久久亚洲免费 | 欧美精品视 | 久久国产成人午夜av影院潦草 | 欧美激情综合五月色丁香 | 中文字幕在线观看第三页 | 国产麻豆精品传媒av国产下载 | 精品国产一区二区久久 | 国产精品网址在线观看 | 久久久久久毛片 | 亚洲91视频 | 久久精品第一页 | 成人在线免费观看网站 | 一区二区三区中文字幕在线观看 | 在线观看精品 | 亚洲另类视频在线 | 久久久久久久久久久久av | 亚洲精品小视频 | 色婷婷伊人 | 国产中文字幕在线视频 | 欧美成人性网 | 字幕网av | 国产日产精品一区二区三区四区 | 欧美日本不卡视频 | 色噜噜日韩精品一区二区三区视频 | 国产高清不卡一区二区三区 | 国产一区免费观看 | 国产精品一区二区电影 | 欧美成年人在线观看 | 国产精品久久久久久高潮 | 亚洲精区二区三区四区麻豆 | www.在线看片.com | 九色91在线视频 | 日韩在线视频一区二区三区 | 三级在线视频观看 | 99久久国产免费,99久久国产免费大片 | 亚洲 欧洲 国产 日本 综合 | 中文av在线天堂 | 精品国产一区二区三区不卡 | 麻豆94tv免费版 | 亚洲国产高清在线 | 国产精品18久久久久久vr | 免费看黄在线看 | 亚洲精品日韩在线观看 | 亚洲激情小视频 | 韩日在线一区 | 91高清在线看 | 欧美乱码精品一区 | 免费十分钟| 免费日韩 精品中文字幕视频在线 | 久久免费视频2 | 国产高清在线一区 | 精品一区二区免费视频 | 精品久久久久免费极品大片 | av电影免费在线播放 | av电影一区二区三区 | 在线天堂v | 精品国偷自产国产一区 | 国产手机在线观看 | 免费看成人片 | 日韩有码欧美 | 国产亚洲在线视频 | 中文字幕一区av | 亚洲国产激情 | 国产又粗又硬又爽的视频 | 久久精品激情 | 深爱激情五月婷婷 | 最新中文字幕在线资源 | 狠狠伊人| 日韩网站在线播放 | 毛片网在线播放 | 91成人免费在线 | 99热这里只有精品久久 | 婷婷伊人网 | 超碰人人超 | 成人午夜免费福利 | 中文字幕一区二区三区视频 | 国产一级淫片免费看 | 日日狠狠| 欧美日韩一区二区免费在线观看 | 久久午夜鲁丝片 | 六月色婷 |