mirror of
https://github.com/VIPyinzhiwei/Eyepetizer.git
synced 2026-03-13 07:59:55 +08:00
更新 Gradle 配置,升级依赖库版本,移除 VasSonic 相关代码,以支持新的依赖管理方式。
This commit is contained in:
@@ -80,7 +80,6 @@ Through this project, I hope to help you better learn Jetpack and MVVM architect
|
||||
- [PhotoView][25] Support gesture zoom picture
|
||||
- [Circleimageview][26] Round image
|
||||
- [GSYVideoPlayer][27] Video player
|
||||
- [VasSonic][28] Improve H5 first screen loading speed
|
||||
- [Leakcanary][29] Memory leak detection
|
||||
- [Kotlinx Coroutines][30] Simplify code management background threads and callbacks
|
||||
|
||||
@@ -132,7 +131,6 @@ limitations under the License.
|
||||
[25]:https://github.com/chrisbanes/PhotoView
|
||||
[26]:https://github.com/hdodenhof/CircleImageView
|
||||
[27]:https://github.com/CarGuo/GSYVideoPlayer
|
||||
[28]:https://github.com/Tencent/VasSonic
|
||||
[29]:https://github.com/square/leakcanary
|
||||
[30]:https://github.com/Kotlin/kotlinx.coroutines
|
||||
[31]:https://m.apkpure.com/cn/%E5%BC%80%E7%9C%BC/com.wandoujia.eyepetizer/versions
|
||||
|
||||
@@ -80,7 +80,6 @@
|
||||
- [PhotoView][25] 支持手势缩放图片
|
||||
- [Circleimageview][26] 圆形图像
|
||||
- [GSYVideoPlayer][27] 视频播放器
|
||||
- [VasSonic][28] 提升H5首屏加载速度
|
||||
- [Leakcanary][29] 内存泄漏检测
|
||||
- [Kotlinx Coroutines][30] 简化代码管理后台线程与回调
|
||||
|
||||
@@ -132,7 +131,6 @@ limitations under the License.
|
||||
[25]:https://github.com/chrisbanes/PhotoView
|
||||
[26]:https://github.com/hdodenhof/CircleImageView
|
||||
[27]:https://github.com/CarGuo/GSYVideoPlayer
|
||||
[28]:https://github.com/Tencent/VasSonic
|
||||
[29]:https://github.com/square/leakcanary
|
||||
[30]:https://github.com/Kotlin/kotlinx.coroutines
|
||||
[31]:https://m.apkpure.com/cn/%E5%BC%80%E7%9C%BC/com.wandoujia.eyepetizer/versions
|
||||
|
||||
@@ -7,13 +7,12 @@ apply plugin: 'kotlin-parcelize'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
android {
|
||||
compileSdkVersion 32
|
||||
buildToolsVersion "32.0.0"
|
||||
compileSdk 34
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.eyepetizer.android"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 32
|
||||
targetSdkVersion 34
|
||||
versionCode 7
|
||||
versionName "1.0.6"
|
||||
multiDexEnabled true
|
||||
@@ -74,50 +73,49 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation 'androidx.appcompat:appcompat:1.4.2'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
|
||||
//常用依赖库
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||
implementation 'com.google.android.material:material:1.6.1'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.3.2'
|
||||
implementation 'com.google.android.material:material:1.10.0'
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
||||
implementation 'com.squareup.retrofit2:converter-scalars:2.5.0'
|
||||
implementation 'com.github.bumptech.glide:glide:4.13.2'
|
||||
kapt 'com.github.bumptech.glide:compiler:4.11.0'
|
||||
implementation 'com.github.bumptech.glide:okhttp3-integration:4.9.0'
|
||||
implementation 'jp.wasabeef:glide-transformations:4.1.0'
|
||||
implementation 'com.github.bumptech.glide:glide:4.16.0'
|
||||
kapt 'com.github.bumptech.glide:compiler:4.16.0'
|
||||
implementation 'com.github.bumptech.glide:okhttp3-integration:4.16.0'
|
||||
implementation 'jp.wasabeef:glide-transformations:4.3.0'
|
||||
implementation 'org.greenrobot:eventbus:3.1.1'
|
||||
implementation 'androidx.viewpager2:viewpager2:1.0.0'
|
||||
implementation 'com.permissionx.guolindev:permissionx:1.2.2'
|
||||
implementation 'com.guolindev.permissionx:permissionx:1.7.1'
|
||||
|
||||
//Android Jetpack 组件
|
||||
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
|
||||
implementation 'androidx.work:work-runtime:2.7.1'
|
||||
implementation "androidx.startup:startup-runtime:1.1.1"
|
||||
implementation "androidx.datastore:datastore-preferences:1.0.0"
|
||||
implementation "androidx.paging:paging-runtime:3.1.1"
|
||||
implementation 'androidx.work:work-runtime:2.8.1'
|
||||
implementation 'androidx.startup:startup-runtime:1.1.1'
|
||||
implementation 'androidx.datastore:datastore-preferences:1.0.0'
|
||||
implementation 'androidx.paging:paging-runtime:3.2.1'
|
||||
|
||||
//Android KTX
|
||||
implementation 'androidx.core:core-ktx:1.8.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1'
|
||||
implementation "androidx.fragment:fragment-ktx:1.5.1"
|
||||
implementation 'androidx.core:core-ktx:1.12.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.2'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.6.2'
|
||||
|
||||
//特定功能依赖库
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'
|
||||
implementation 'com.flyco.tablayout:FlycoTabLayout_Lib:2.1.2@aar'
|
||||
implementation 'com.scwang.smart:refresh-header-material:2.0.0'
|
||||
implementation 'com.scwang.smart:refresh-layout-kernel:2.0.0'
|
||||
implementation 'io.github.h07000223:flycoTabLayout:3.0.0'
|
||||
implementation 'io.github.scwang90:refresh-header-material:3.0.0-alpha'
|
||||
implementation 'io.github.scwang90:refresh-layout-kernel:3.0.0-alpha'
|
||||
implementation 'com.github.zhpanvip:BannerViewPager:3.1.2'
|
||||
implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
|
||||
implementation 'com.geyifeng.immersionbar:immersionbar:3.2.2'
|
||||
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
|
||||
implementation 'de.hdodenhof:circleimageview:3.1.0'
|
||||
implementation 'com.android.support:multidex:1.0.3'
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation 'com.shuyu:GSYVideoPlayer:8.1.2'
|
||||
implementation 'com.tencent.sonic:sdk:3.1.0'
|
||||
implementation 'com.umeng.umsdk:common:9.3.8'
|
||||
implementation 'com.umeng.umsdk:asms:1.2.2'
|
||||
|
||||
|
||||
@@ -21,9 +21,7 @@ import android.app.Application
|
||||
import android.content.Context
|
||||
import androidx.multidex.MultiDex
|
||||
import androidx.work.WorkManager
|
||||
import com.eyepetizer.android.extension.preCreateSession
|
||||
import com.eyepetizer.android.ui.SplashActivity
|
||||
import com.eyepetizer.android.ui.common.ui.WebViewActivity
|
||||
import com.eyepetizer.android.ui.common.view.NoStatusFooter
|
||||
import com.eyepetizer.android.util.DialogAppraiseTipsWorker
|
||||
import com.eyepetizer.android.util.GlobalUtil
|
||||
@@ -73,7 +71,6 @@ class EyepetizerApplication : Application() {
|
||||
super.onCreate()
|
||||
context = this
|
||||
IjkPlayerManager.setLogLevel(if (BuildConfig.DEBUG) IjkMediaPlayer.IJK_LOG_WARN else IjkMediaPlayer.IJK_LOG_SILENT)
|
||||
WebViewActivity.DEFAULT_URL.preCreateSession()
|
||||
if (!SplashActivity.isFirstEntryApp && DialogAppraiseTipsWorker.isNeedShowDialog) {
|
||||
WorkManager.getInstance(this).enqueue(DialogAppraiseTipsWorker.showDialogWorkRequest)
|
||||
}
|
||||
|
||||
@@ -18,10 +18,6 @@ package com.eyepetizer.android.extension
|
||||
|
||||
import android.widget.Toast
|
||||
import com.eyepetizer.android.EyepetizerApplication
|
||||
import com.eyepetizer.android.ui.common.ui.vassonic.SonicRuntimeImpl
|
||||
import com.tencent.sonic.sdk.SonicConfig
|
||||
import com.tencent.sonic.sdk.SonicEngine
|
||||
import com.tencent.sonic.sdk.SonicSessionConfig
|
||||
|
||||
/**
|
||||
* 弹出Toast提示。
|
||||
@@ -30,18 +26,4 @@ import com.tencent.sonic.sdk.SonicSessionConfig
|
||||
*/
|
||||
fun CharSequence.showToast(duration: Int = Toast.LENGTH_SHORT) {
|
||||
Toast.makeText(EyepetizerApplication.context, this, duration).show()
|
||||
}
|
||||
|
||||
/**
|
||||
* VasSonic预加载session。
|
||||
*
|
||||
* @param CharSequence 预加载url
|
||||
*/
|
||||
fun CharSequence.preCreateSession(): Boolean {
|
||||
if (!SonicEngine.isGetInstanceAllowed()) {
|
||||
SonicEngine.createInstance(SonicRuntimeImpl(EyepetizerApplication.context), SonicConfig.Builder().build())
|
||||
}
|
||||
val sessionConfigBuilder = SonicSessionConfig.Builder().apply { setSupportLocalServer(true) }
|
||||
val preloadSuccess = SonicEngine.getInstance().preCreateSession(this.toString(), sessionConfigBuilder.build())
|
||||
return preloadSuccess
|
||||
}
|
||||
@@ -23,19 +23,10 @@ import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.webkit.*
|
||||
import com.eyepetizer.android.databinding.ActivityWebViewBinding
|
||||
import com.eyepetizer.android.extension.logD
|
||||
import com.eyepetizer.android.extension.preCreateSession
|
||||
import com.eyepetizer.android.extension.visible
|
||||
import com.eyepetizer.android.ui.common.ui.vassonic.OfflinePkgSessionConnection
|
||||
import com.eyepetizer.android.ui.common.ui.vassonic.SonicJavaScriptInterface
|
||||
import com.eyepetizer.android.ui.common.ui.vassonic.SonicRuntimeImpl
|
||||
import com.eyepetizer.android.ui.common.ui.vassonic.SonicSessionClientImpl
|
||||
import com.eyepetizer.android.util.GlobalUtil
|
||||
import com.tencent.sonic.sdk.*
|
||||
|
||||
|
||||
/**
|
||||
* 展示网页共通界面。
|
||||
@@ -58,16 +49,11 @@ class WebViewActivity : BaseActivity() {
|
||||
|
||||
private var isTitleFixed: Boolean = false
|
||||
|
||||
private var sonicSession: SonicSession? = null
|
||||
|
||||
private var sonicSessionClient: SonicSessionClientImpl? = null
|
||||
|
||||
private var mode: Int = MODE_DEFAULT
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
initParams()
|
||||
preloadInitVasSonic()
|
||||
_binding = ActivityWebViewBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
}
|
||||
@@ -76,12 +62,7 @@ class WebViewActivity : BaseActivity() {
|
||||
super.setupViews()
|
||||
initTitleBar()
|
||||
initWebView()
|
||||
if (sonicSessionClient != null) {
|
||||
sonicSessionClient?.bindWebView(binding.webView)
|
||||
sonicSessionClient?.clientReady()
|
||||
} else {
|
||||
binding.webView.loadUrl(linkUrl)
|
||||
}
|
||||
binding.webView.loadUrl(linkUrl)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
@@ -94,8 +75,6 @@ class WebViewActivity : BaseActivity() {
|
||||
|
||||
override fun onDestroy() {
|
||||
binding.webView.destroy()
|
||||
sonicSession?.destroy()
|
||||
sonicSession = null
|
||||
_binding = null
|
||||
super.onDestroy()
|
||||
}
|
||||
@@ -120,12 +99,9 @@ class WebViewActivity : BaseActivity() {
|
||||
mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
|
||||
javaScriptEnabled = true
|
||||
binding.webView.removeJavascriptInterface("searchBoxJavaBridge_")
|
||||
intent.putExtra(SonicJavaScriptInterface.PARAM_LOAD_URL_TIME, System.currentTimeMillis())
|
||||
binding.webView.addJavascriptInterface(SonicJavaScriptInterface(sonicSessionClient, intent), "sonic")
|
||||
allowContentAccess = true
|
||||
databaseEnabled = true
|
||||
domStorageEnabled = true
|
||||
setAppCacheEnabled(true)
|
||||
savePassword = false
|
||||
saveFormData = false
|
||||
useWideViewPort = true
|
||||
@@ -143,49 +119,6 @@ class WebViewActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用VasSonic框架提升H5首屏加载速度。
|
||||
*/
|
||||
private fun preloadInitVasSonic() {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)
|
||||
|
||||
// init sonic engine if necessary, or maybe u can do this when application created
|
||||
if (!SonicEngine.isGetInstanceAllowed()) {
|
||||
SonicEngine.createInstance(SonicRuntimeImpl(application), SonicConfig.Builder().build())
|
||||
}
|
||||
|
||||
// if it's sonic mode , startup sonic session at first time
|
||||
if (MODE_DEFAULT != mode) { // sonic mode
|
||||
val sessionConfigBuilder = SonicSessionConfig.Builder()
|
||||
sessionConfigBuilder.setSupportLocalServer(true)
|
||||
|
||||
// if it's offline pkg mode, we need to intercept the session connection
|
||||
if (MODE_SONIC_WITH_OFFLINE_CACHE == mode) {
|
||||
sessionConfigBuilder.setCacheInterceptor(object : SonicCacheInterceptor(null) {
|
||||
override fun getCacheData(session: SonicSession): String? {
|
||||
return null // offline pkg does not need cache
|
||||
}
|
||||
})
|
||||
sessionConfigBuilder.setConnectionInterceptor(object : SonicSessionConnectionInterceptor() {
|
||||
override fun getConnection(session: SonicSession, intent: Intent): SonicSessionConnection {
|
||||
return OfflinePkgSessionConnection(this@WebViewActivity, session, intent)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// create sonic session and run sonic flow
|
||||
sonicSession = SonicEngine.getInstance().createSession(linkUrl, sessionConfigBuilder.build())
|
||||
if (null != sonicSession) {
|
||||
sonicSession?.bindClient(SonicSessionClientImpl().also { sonicSessionClient = it })
|
||||
} else {
|
||||
// this only happen when a same sonic session is already running,
|
||||
// u can comment following codes to feedback as a default mode.
|
||||
// throw new UnknownError("create session fail!");
|
||||
logD(TAG, "${title},${linkUrl}:create sonic session fail!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class UIWebViewClient(val binding: ActivityWebViewBinding, val activity: WebViewActivity) : WebViewClient() {
|
||||
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
|
||||
activity.linkUrl = url
|
||||
@@ -195,15 +128,10 @@ class WebViewActivity : BaseActivity() {
|
||||
|
||||
override fun onPageFinished(view: WebView, url: String) {
|
||||
super.onPageFinished(view, url)
|
||||
activity.sonicSession?.sessionClient?.pageFinish(url)
|
||||
binding.progressBar.visibility = View.INVISIBLE
|
||||
}
|
||||
|
||||
override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {
|
||||
if (activity.sonicSession != null) {
|
||||
val requestResponse = activity.sonicSessionClient?.requestResource(request?.url.toString())
|
||||
if (requestResponse is WebResourceResponse) return requestResponse
|
||||
}
|
||||
return super.shouldInterceptRequest(view, request)
|
||||
}
|
||||
}
|
||||
@@ -253,14 +181,12 @@ class WebViewActivity : BaseActivity() {
|
||||
* @param mode 加载模式:MODE_DEFAULT 默认使用WebView加载;MODE_SONIC 使用VasSonic框架加载; MODE_SONIC_WITH_OFFLINE_CACHE 使用VasSonic框架离线加载
|
||||
*/
|
||||
fun start(context: Context, title: String, url: String, isShare: Boolean = true, isTitleFixed: Boolean = true, mode: Int = MODE_SONIC) {
|
||||
url.preCreateSession() //预加载url
|
||||
val intent = Intent(context, WebViewActivity::class.java).apply {
|
||||
putExtra(TITLE, title)
|
||||
putExtra(LINK_URL, url)
|
||||
putExtra(IS_SHARE, isShare)
|
||||
putExtra(IS_TITLE_FIXED, isTitleFixed)
|
||||
putExtra(PARAM_MODE, mode)
|
||||
putExtra(SonicJavaScriptInterface.PARAM_CLICK_TIME, System.currentTimeMillis())
|
||||
}
|
||||
context.startActivity(intent)
|
||||
}
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.eyepetizer.android.ui.common.ui.vassonic
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.tencent.sonic.sdk.SonicConstants
|
||||
import com.tencent.sonic.sdk.SonicSession
|
||||
import com.tencent.sonic.sdk.SonicSessionConnection
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.IOException
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
|
||||
class OfflinePkgSessionConnection(context: Context, session: SonicSession?, intent: Intent?) : SonicSessionConnection(session, intent) {
|
||||
|
||||
private val context: WeakReference<Context>
|
||||
|
||||
override fun internalConnect(): Int {
|
||||
val ctx = context.get()
|
||||
if (null != ctx) {
|
||||
try {
|
||||
val offlineHtmlInputStream = ctx.assets.open("sonic-demo-index.html")
|
||||
responseStream = BufferedInputStream(offlineHtmlInputStream)
|
||||
return SonicConstants.ERROR_CODE_SUCCESS
|
||||
} catch (e: Throwable) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
return SonicConstants.ERROR_CODE_UNKNOWN
|
||||
}
|
||||
|
||||
override fun internalGetResponseStream(): BufferedInputStream {
|
||||
return responseStream
|
||||
}
|
||||
|
||||
override fun internalGetCustomHeadFieldEtag(): String {
|
||||
return CUSTOM_HEAD_FILED_ETAG
|
||||
}
|
||||
|
||||
override fun disconnect() {
|
||||
if (null != responseStream) {
|
||||
try {
|
||||
responseStream.close()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getResponseCode(): Int {
|
||||
return 200
|
||||
}
|
||||
|
||||
override fun getResponseHeaderFields(): Map<String, List<String>> {
|
||||
return HashMap(0)
|
||||
}
|
||||
|
||||
override fun getResponseHeaderField(key: String?): String {
|
||||
return ""
|
||||
}
|
||||
|
||||
init {
|
||||
this.context = WeakReference(context)
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.eyepetizer.android.ui.common.ui.vassonic
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.webkit.JavascriptInterface
|
||||
import org.json.JSONObject
|
||||
|
||||
/**
|
||||
* Sonic javaScript Interface (Android API Level >= 17)
|
||||
*/
|
||||
class SonicJavaScriptInterface(private val sessionClient: SonicSessionClientImpl?, private val intent: Intent) {
|
||||
|
||||
// the callback function of demo page is hardcode as 'getDiffDataCallback'
|
||||
@get:JavascriptInterface
|
||||
val diffData: Unit
|
||||
get() {
|
||||
// the callback function of demo page is hardcode as 'getDiffDataCallback'
|
||||
getDiffData2("getDiffDataCallback")
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun getDiffData2(jsCallbackFunc: String) {
|
||||
sessionClient?.getDiffData { resultData ->
|
||||
val callbackRunnable = Runnable {
|
||||
val jsCode = "javascript:" + jsCallbackFunc + "('" + toJsString(resultData) + "')"
|
||||
sessionClient.webView?.loadUrl(jsCode)
|
||||
}
|
||||
if (Looper.getMainLooper() == Looper.myLooper()) {
|
||||
callbackRunnable.run()
|
||||
} else {
|
||||
Handler(Looper.getMainLooper()).post(callbackRunnable)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@get:JavascriptInterface
|
||||
val performance: String
|
||||
get() {
|
||||
val clickTime = intent.getLongExtra(PARAM_CLICK_TIME, -1)
|
||||
val loadUrlTime = intent.getLongExtra(PARAM_LOAD_URL_TIME, -1)
|
||||
try {
|
||||
val result = JSONObject()
|
||||
result.put(PARAM_CLICK_TIME, clickTime)
|
||||
result.put(PARAM_LOAD_URL_TIME, loadUrlTime)
|
||||
return result.toString()
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PARAM_CLICK_TIME = "clickTime"
|
||||
const val PARAM_LOAD_URL_TIME = "loadUrlTime"
|
||||
|
||||
/**
|
||||
* From RFC 4627, "All Unicode characters may be placed within the quotation marks except
|
||||
* for the characters that must be escaped: quotation mark,
|
||||
* reverse solidus, and the control characters (U+0000 through U+001F)."
|
||||
*/
|
||||
private fun toJsString(value: String?): String {
|
||||
if (value == null) {
|
||||
return "null"
|
||||
}
|
||||
val out = StringBuilder(1024)
|
||||
var i = 0
|
||||
val length = value.length
|
||||
while (i < length) {
|
||||
val c = value[i]
|
||||
when (c) {
|
||||
'"', '\\', '/' -> out.append('\\').append(c)
|
||||
'\t' -> out.append("\\t")
|
||||
'\b' -> out.append("\\b")
|
||||
'\n' -> out.append("\\n")
|
||||
'\r' -> out.append("\\r")
|
||||
'\u000C' -> out.append("\\f") //Kotlin 转义字符 - 换页符 "\f" 提示错误 Illegal escape: '\f' 解决方案:https://www.jianshu.com/p/a59a24f76c4b,使用 Kotlin "\u000C",使用 Java "\f"
|
||||
else -> if (c.toInt() <= 0x1F) {
|
||||
out.append(String.format("\\u%04x", c.toInt()))
|
||||
} else {
|
||||
out.append(c)
|
||||
}
|
||||
}
|
||||
i++
|
||||
}
|
||||
return out.toString()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.eyepetizer.android.ui.common.ui.vassonic
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.webkit.CookieManager
|
||||
import android.webkit.WebResourceResponse
|
||||
import com.eyepetizer.android.BuildConfig
|
||||
import com.tencent.sonic.sdk.SonicRuntime
|
||||
import com.tencent.sonic.sdk.SonicSessionClient
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
|
||||
/**
|
||||
* the sonic host application must implement SonicRuntime to do right things.
|
||||
*/
|
||||
class SonicRuntimeImpl(context: Context?) : SonicRuntime(context) {
|
||||
/**
|
||||
* 获取用户UA信息
|
||||
* @return
|
||||
*/
|
||||
override fun getUserAgent(): String {
|
||||
return System.getProperty("http.agent") ?: "unknown"
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户ID信息
|
||||
* @return
|
||||
*/
|
||||
override fun getCurrentUserAccount(): String {
|
||||
return ""
|
||||
}
|
||||
|
||||
override fun getCookie(url: String?): String? {
|
||||
val cookieManager = CookieManager.getInstance()
|
||||
return cookieManager.getCookie(url)
|
||||
}
|
||||
|
||||
override fun log(tag: String, level: Int, message: String) {
|
||||
when (level) {
|
||||
Log.ERROR -> Log.e(tag, message)
|
||||
Log.INFO -> Log.i(tag, message)
|
||||
else -> Log.d(tag, message)
|
||||
}
|
||||
}
|
||||
|
||||
override fun createWebResourceResponse(mimeType: String, encoding: String, data: InputStream, headers: Map<String, String>): Any {
|
||||
val resourceResponse = WebResourceResponse(mimeType, encoding, data)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
resourceResponse.responseHeaders = headers
|
||||
}
|
||||
return resourceResponse
|
||||
}
|
||||
|
||||
override fun showToast(text: CharSequence, duration: Int) {}
|
||||
|
||||
override fun notifyError(client: SonicSessionClient?, url: String?, errorCode: Int) {}
|
||||
|
||||
override fun isSonicUrl(url: String): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun setCookie(url: String?, cookies: List<String>?): Boolean {
|
||||
if (!TextUtils.isEmpty(url) && cookies != null && cookies.size > 0) {
|
||||
val cookieManager = CookieManager.getInstance()
|
||||
for (cookie in cookies) {
|
||||
cookieManager.setCookie(url, cookie)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun isNetworkValid(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun postTaskToThread(task: Runnable, delayMillis: Long) {
|
||||
val thread = Thread(task, "SonicThread")
|
||||
thread.start()
|
||||
}
|
||||
|
||||
override fun getSonicCacheDir(): File {
|
||||
if (BuildConfig.DEBUG) {
|
||||
val path = Environment.getExternalStorageDirectory().absolutePath + File.separator + "sonic/"
|
||||
val file = File(path.trim { it <= ' ' })
|
||||
if (!file.exists()) {
|
||||
file.mkdir()
|
||||
}
|
||||
return file
|
||||
}
|
||||
return super.getSonicCacheDir()
|
||||
}
|
||||
|
||||
override fun getHostDirectAddress(url: String): String? {
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.eyepetizer.android.ui.common.ui.vassonic
|
||||
|
||||
import android.os.Bundle
|
||||
import android.webkit.WebView
|
||||
import com.tencent.sonic.sdk.SonicSessionClient
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* a implement of SonicSessionClient which need to connect webview and content data.
|
||||
*/
|
||||
class SonicSessionClientImpl : SonicSessionClient() {
|
||||
|
||||
var webView: WebView? = null
|
||||
private set
|
||||
|
||||
fun bindWebView(webView: WebView?) {
|
||||
this.webView = webView
|
||||
}
|
||||
|
||||
override fun loadUrl(url: String, extraData: Bundle?) {
|
||||
webView?.loadUrl(url)
|
||||
}
|
||||
|
||||
override fun loadDataWithBaseUrl(baseUrl: String, data: String, mimeType: String, encoding: String, historyUrl: String) {
|
||||
webView?.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl)
|
||||
}
|
||||
|
||||
override fun loadDataWithBaseUrlAndHeader(baseUrl: String, data: String, mimeType: String, encoding: String, historyUrl: String, headers: HashMap<String, String>) {
|
||||
loadDataWithBaseUrl(baseUrl, data, mimeType, encoding, historyUrl)
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
webView?.destroy()
|
||||
webView = null
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,8 @@ import com.eyepetizer.android.extension.*
|
||||
import com.eyepetizer.android.logic.model.Author
|
||||
import com.eyepetizer.android.logic.model.Consumption
|
||||
import com.eyepetizer.android.logic.model.Cover
|
||||
import com.eyepetizer.android.logic.model.VideoDetail
|
||||
import com.eyepetizer.android.logic.model.VideoReplies
|
||||
import com.eyepetizer.android.logic.model.WebUrl
|
||||
import com.eyepetizer.android.ui.common.ui.BaseActivity
|
||||
import com.eyepetizer.android.ui.common.view.NoStatusFooter
|
||||
@@ -200,7 +202,7 @@ class NewDetailActivity : BaseActivity() {
|
||||
private fun observe() {
|
||||
//刷新,视频信息+相关推荐+评论
|
||||
if (!viewModel.videoDetailLiveData.hasObservers()) {
|
||||
viewModel.videoDetailLiveData.observe(this, Observer { result ->
|
||||
viewModel.videoDetailLiveData.observe(this, Observer<Result<VideoDetail>> { result ->
|
||||
val response = result.getOrNull()
|
||||
if (response == null) {
|
||||
ResponseHandler.getFailureTips(result.exceptionOrNull()).showToast()
|
||||
@@ -211,7 +213,9 @@ class NewDetailActivity : BaseActivity() {
|
||||
return@Observer
|
||||
}
|
||||
response.videoBeanForClient?.run {
|
||||
viewModel.videoInfoData = VideoInfo(id, playUrl, title, description, category, library, consumption, cover, author, webUrl)
|
||||
viewModel.videoInfoData = VideoInfo(videoId = id, playUrl = playUrl, title = title, description = description,
|
||||
category = category, library = library, consumption = consumption,
|
||||
cover = cover, author = author, webUrl = webUrl)
|
||||
startVideoPlayer()
|
||||
relatedAdapter.bindVideoInfo(viewModel.videoInfoData)
|
||||
}
|
||||
@@ -230,7 +234,7 @@ class NewDetailActivity : BaseActivity() {
|
||||
}
|
||||
//刷新,相关推荐+评论
|
||||
if (!viewModel.repliesAndRepliesLiveData.hasObservers()) {
|
||||
viewModel.repliesAndRepliesLiveData.observe(this, Observer { result ->
|
||||
viewModel.repliesAndRepliesLiveData.observe(this, Observer<Result<VideoDetail>> { result ->
|
||||
val response = result.getOrNull()
|
||||
if (response == null) {
|
||||
ResponseHandler.getFailureTips(result.exceptionOrNull()).showToast()
|
||||
@@ -256,7 +260,7 @@ class NewDetailActivity : BaseActivity() {
|
||||
}
|
||||
//上拉加载,评论
|
||||
if (!viewModel.repliesLiveData.hasObservers()) {
|
||||
viewModel.repliesLiveData.observe(this, Observer { result ->
|
||||
viewModel.repliesLiveData.observe(this, Observer<Result<VideoReplies>> { result ->
|
||||
val response = result.getOrNull()
|
||||
if (response == null) {
|
||||
ResponseHandler.getFailureTips(result.exceptionOrNull()).showToast()
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
package com.eyepetizer.android.ui.newdetail
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.liveData
|
||||
import androidx.lifecycle.switchMap
|
||||
import com.eyepetizer.android.logic.VideoRepository
|
||||
import com.eyepetizer.android.logic.model.VideoDetail
|
||||
import com.eyepetizer.android.logic.model.VideoRelated
|
||||
@@ -48,7 +48,7 @@ class NewDetailViewModel(repository: VideoRepository) : ViewModel() {
|
||||
|
||||
var nextPageUrl: String? = null
|
||||
|
||||
val videoDetailLiveData = Transformations.switchMap(videoDetailLiveData_) {
|
||||
val videoDetailLiveData = videoDetailLiveData_.switchMap {
|
||||
liveData {
|
||||
val resutlt = try {
|
||||
val videoDetail = repository.refreshVideoDetail(it.videoId, it.repliesUrl) //视频信息+相关推荐+评论
|
||||
@@ -60,7 +60,7 @@ class NewDetailViewModel(repository: VideoRepository) : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
val repliesAndRepliesLiveData = Transformations.switchMap(repliesAndRepliesLiveData_) {
|
||||
val repliesAndRepliesLiveData = repliesAndRepliesLiveData_.switchMap {
|
||||
liveData {
|
||||
val resutlt = try {
|
||||
val videoDetail = repository.refreshVideoRelatedAndVideoReplies(it.videoId, it.repliesUrl) //相关推荐+评论
|
||||
@@ -72,7 +72,7 @@ class NewDetailViewModel(repository: VideoRepository) : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
val repliesLiveData = Transformations.switchMap(repliesLiveData_) {
|
||||
val repliesLiveData = repliesLiveData_.switchMap {
|
||||
liveData {
|
||||
val resutlt = try {
|
||||
val videoDetail = repository.refreshVideoReplies(it) //评论
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
package com.eyepetizer.android.ui.search
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Transformations
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.liveData
|
||||
import androidx.lifecycle.switchMap
|
||||
import com.eyepetizer.android.logic.MainPageRepository
|
||||
|
||||
class SearchViewModel(val repository: MainPageRepository) : ViewModel() {
|
||||
@@ -28,7 +28,7 @@ class SearchViewModel(val repository: MainPageRepository) : ViewModel() {
|
||||
|
||||
private var requestParamLiveData = MutableLiveData<Any>()
|
||||
|
||||
val dataListLiveData = Transformations.switchMap(requestParamLiveData) {
|
||||
val dataListLiveData = requestParamLiveData.switchMap{
|
||||
liveData {
|
||||
val resutlt = try {
|
||||
val hotSearch = repository.refreshHotSearch()
|
||||
|
||||
@@ -84,7 +84,6 @@ class OpenSourceProjectsActivity : BaseActivity() {
|
||||
add(OpenSourceProject("PhotoView", "https://github.com/chrisbanes/PhotoView"))
|
||||
add(OpenSourceProject("Circleimageview", "https://github.com/hdodenhof/CircleImageView"))
|
||||
add(OpenSourceProject("GSYVideoPlayer", "https://github.com/CarGuo/GSYVideoPlayer"))
|
||||
add(OpenSourceProject("VasSonic", "https://github.com/Tencent/VasSonic"))
|
||||
add(OpenSourceProject("Leakcanary", "https://github.com/square/leakcanary"))
|
||||
add(OpenSourceProject("Kotlinx Coroutines", "https://github.com/Kotlin/kotlinx.coroutines"))
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import com.eyepetizer.android.ui.common.ui.WebViewActivity.Companion.MODE_SONIC_
|
||||
import com.eyepetizer.android.ui.login.LoginActivity
|
||||
import com.eyepetizer.android.util.DataStoreUtils
|
||||
import com.shuyu.gsyvideoplayer.GSYVideoManager
|
||||
import com.tencent.sonic.sdk.SonicEngine
|
||||
import com.umeng.analytics.MobclickAgent
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -86,9 +85,6 @@ class SettingViewModel : ViewModel() {
|
||||
Glide.get(context).clearMemory()
|
||||
withContext(Dispatchers.IO) {
|
||||
Glide.get(context).clearDiskCache()
|
||||
if (SonicEngine.isGetInstanceAllowed()) {
|
||||
SonicEngine.getInstance().cleanCache()
|
||||
}
|
||||
}
|
||||
R.string.clear_cache_succeed.showToast()
|
||||
}
|
||||
|
||||
10
build.gradle
10
build.gradle
@@ -4,11 +4,10 @@ buildscript {
|
||||
ext.kotlin_version = '1.7.10'
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven { url 'https://repo1.maven.org/maven2/' }
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.2.2'
|
||||
classpath 'com.android.tools.build:gradle:7.4.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
@@ -19,9 +18,10 @@ buildscript {
|
||||
allprojects {
|
||||
repositories {
|
||||
google()
|
||||
jcenter()
|
||||
maven { url 'https://repo1.maven.org/maven2/' }
|
||||
mavenCentral()
|
||||
maven { url 'https://jitpack.io' }
|
||||
maven { url 'https://maven.tencent.com/repository/maven-public/' }
|
||||
maven { url "https://maven.aliyun.com/repository/public" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user