This commit is contained in:
lunaticbum 2024-12-20 18:06:54 +09:00
parent 4d8b9b728b
commit 1169b159e8
12 changed files with 692 additions and 444 deletions

View File

@ -17,6 +17,7 @@ android {
targetSdk = 34 targetSdk = 34
versionCode = 38 versionCode = 38
versionName = "2.8.2" versionName = "2.8.2"
multiDexEnabled = true
} }
buildTypes { buildTypes {
@ -70,6 +71,9 @@ android {
sourceCompatibility = JavaVersion.VERSION_1_8 sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8
} }
packagingOptions.resources.excludes.add("META-INF/*")
packagingOptions.resources.excludes.add("mozilla/*")
packagingOptions.resources.excludes.add("META-INF/*/*")
kotlinOptions { kotlinOptions {
jvmTarget = "1.8" jvmTarget = "1.8"
@ -105,6 +109,8 @@ dependencies {
implementation("com.github.delight-im:Android-AdvancedWebView:v3.2.1") implementation("com.github.delight-im:Android-AdvancedWebView:v3.2.1")
implementation(project(":library")) implementation(project(":library"))
implementation(project(":utils")) implementation(project(":utils"))
// implementation ("org.apache.tika:tika-parsers:1.12")
implementation ("androidx.media:media:1.7.0") implementation ("androidx.media:media:1.7.0")
// implementation ("me.everything:providers-android:1.0.1") // implementation ("me.everything:providers-android:1.0.1")
// implementation ("me.everything:providers-core:1.0.1") // implementation ("me.everything:providers-core:1.0.1")

View File

@ -72,7 +72,7 @@
android:enableOnBackInvokedCallback="true" android:enableOnBackInvokedCallback="true"
android:largeHeap="true" android:largeHeap="true"
android:networkSecurityConfig="@xml/network_security_config" android:networkSecurityConfig="@xml/network_security_config"
android:hardwareAccelerated="true" android:hardwareAccelerated="false"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
android:screenOrientation="nosensor" android:screenOrientation="nosensor"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize"
@ -191,11 +191,22 @@
<activity <activity
android:name=".home.RssViewerActivity" android:name=".home.RssViewerActivity"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:hardwareAccelerated="true" android:hardwareAccelerated="false"
android:launchMode="singleTask" android:launchMode="singleTask"
android:exported="true"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:theme="@style/FinestWebViewTheme.Fullscreen" > android:theme="@style/FinestWebViewTheme.Fullscreen" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="text/html"/>
<data android:mimeType="text/plain"/>
<data android:mimeType="text/xml"/>
<data android:mimeType="application/xhtml+xml"/>
<data android:mimeType="application/vnd.wap.xhtml+xml"/>
</intent-filter>
</activity> </activity>
<provider <provider

View File

@ -1,11 +1,11 @@
package bums.lunatic.launcher.home package bums.lunatic.launcher.home
import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.os.Bundle
import android.os.Environment import android.os.Environment
import android.print.PDFPrint import android.print.PDFPrint
import android.util.Base64
import android.view.KeyEvent import android.view.KeyEvent
import android.view.KeyEvent.ACTION_UP import android.view.KeyEvent.ACTION_UP
import android.view.KeyEvent.KEYCODE_BUTTON_A import android.view.KeyEvent.KEYCODE_BUTTON_A
@ -22,34 +22,41 @@ import android.view.MotionEvent
import android.view.MotionEvent.ACTION_MOVE import android.view.MotionEvent.ACTION_MOVE
import android.view.View import android.view.View
import android.webkit.CookieManager import android.webkit.CookieManager
import android.webkit.CookieSyncManager
import android.webkit.ValueCallback
import android.widget.Toast import android.widget.Toast
import androidx.core.content.FileProvider
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.view.contains
import androidx.core.view.drawToBitmap
import bums.lunatic.launcher.LauncherActivity.Companion.lActivity
import bums.lunatic.launcher.model.RssData import bums.lunatic.launcher.model.RssData
import bums.lunatic.launcher.utils.Blog import bums.lunatic.launcher.utils.Blog
import bums.lunatic.launcher.workers.WorkersDb import bums.lunatic.launcher.workers.WorkersDb
import io.realm.kotlin.UpdatePolicy
import io.realm.kotlin.ext.query import io.realm.kotlin.ext.query
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kr.lunaticbum.awesomewebview.AwesomeWebView import kr.lunaticbum.awesomewebview.AwesomeWebView
import kr.lunaticbum.awesomewebview.AwesomeWebView.Builder
import kr.lunaticbum.awesomewebview.AwesomeWebViewActivity import kr.lunaticbum.awesomewebview.AwesomeWebViewActivity
import kr.lunaticbum.awesomewebview.helpers.DownPicUtil import kr.lunaticbum.awesomewebview.helpers.DownPicUtil
import kr.lunaticbum.awesomewebview.helpers.DownPicUtil.DownFinishListener import kr.lunaticbum.awesomewebview.helpers.DownPicUtil.DownFinishListener
import kr.lunaticbum.awesomewebview.listeners.BroadCastManager import kr.lunaticbum.awesomewebview.listeners.BroadCastManager
import kr.lunaticbum.awesomewebview.objects.CustomMenu
import kr.lunaticbum.utils.content.ContextUtil import kr.lunaticbum.utils.content.ContextUtil
import kr.lunaticbum.utils.log.LogUtil import kr.lunaticbum.utils.log.LogUtil
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.UnsupportedMimeTypeException import org.jsoup.UnsupportedMimeTypeException
import org.jsoup.nodes.Element import java.io.BufferedWriter
import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
import java.lang.Exception import java.io.FileInputStream
import java.io.FileWriter
import java.io.IOException
import java.io.InputStream
import java.lang.Thread.sleep
import java.net.URL import java.net.URL
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.Date
import kotlin.text.Charsets.UTF_8
class RssViewBuilder(context: Context) : AwesomeWebView.Builder(context) { class RssViewBuilder(context: Context) : AwesomeWebView.Builder(context) {
var rssId : String = "" var rssId : String = ""
@ -87,11 +94,34 @@ class RssViewerActivity : AwesomeWebViewActivity(), View.OnGenericMotionListene
var double = false var double = false
var rssList: MutableList<String> = ArrayList() var rssList: MutableList<String> = ArrayList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LogUtil.e("intent >>>> ${intent}")
if (intent.action.equals(Intent.ACTION_VIEW)) {
intent.data.toString()?.let {
loadWithIntent = true
load(it)
}
} else {
loadWithIntent = false
}
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
webView?.setOnGenericMotionListener(this) webView?.setOnGenericMotionListener(this)
} }
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
if (intent?.action?.equals(Intent.ACTION_VIEW) == true) {
intent?.data?.toString()?.let {
loadWithIntent = true
load(it)
}
} else {}
}
var lasteventTime = 0L var lasteventTime = 0L
override fun onGenericMotion(p0: View?, ev: MotionEvent?): Boolean { override fun onGenericMotion(p0: View?, ev: MotionEvent?): Boolean {
if (ev?.device?.name?.contains("SM-031N Mouse") == true) { if (ev?.device?.name?.contains("SM-031N Mouse") == true) {
@ -172,15 +202,30 @@ class RssViewerActivity : AwesomeWebViewActivity(), View.OnGenericMotionListene
} }
fun vote(){ fun vote(){
Blog.LOGE("Arrow Center Click") if (!loadWithIntent) {
WorkersDb.getRealm().apply {
writeBlocking { Blog.LOGE("Arrow Center Click")
val result = query<RssData>().query("originPage == $0", rssId).find() WorkersDb.getRealm().apply {
if(result.size > 0) { writeBlocking {
result.forEach { it.vote = true } val result = query<RssData>().query("originPage == $0", rssId).find()
if (result.size > 0) {
result.forEach { it.vote = true }
}
} }
} }
webView?.evaluateJavascript("document.documentElement.outerHTML",
object : ValueCallback<String> {
override fun onReceiveValue(value: String?) {
val html = value?.replace("\\u003C", "<")
onHtml(html, false)
}
})
} else {
finish()
} }
}
fun doNextPage() {
rssList.remove(rssId) rssList.remove(rssId)
if (currentIdx < rssList.size - 1) { if (currentIdx < rssList.size - 1) {
currentIdx += 1 currentIdx += 1
@ -198,31 +243,21 @@ class RssViewerActivity : AwesomeWebViewActivity(), View.OnGenericMotionListene
} }
registCancelSearch() registCancelSearch()
} }
fun hideRss() { fun hideRss() {
Blog.LOGE("make no show") if (!loadWithIntent) {
WorkersDb.getRealm().apply { Blog.LOGE("make no show")
writeBlocking { WorkersDb.getRealm().apply {
val result = writeBlocking {
query<RssData>().query("originPage == $0", rssId) val result =
.find() query<RssData>().query("originPage == $0", rssId)
if (result.size > 0) { .find()
result.forEach { it.read += 5 } if (result.size > 0) {
result.forEach { it.read += 5 }
}
} }
} }
} doNextPage()
rssList.remove(rssId)
if (currentIdx < rssList.size - 1) {
currentIdx += 1
rssId = rssList.get(currentIdx)
Blog.LOGE("Arrow Right Click ${currentIdx} ${rssId}")
load(rssId)
registCancelSearch()
} else if (currentIdx > 0) {
currentIdx -= 1
rssId = rssList.get(currentIdx)
Blog.LOGE("Arrow Left Click ${currentIdx} ${rssId}")
load(rssId)
registCancelSearch()
} else { } else {
finish() finish()
} }
@ -265,13 +300,23 @@ class RssViewerActivity : AwesomeWebViewActivity(), View.OnGenericMotionListene
override var pdfListner : PDFPrint.OnPDFPrintListener? = object : PDFPrint.OnPDFPrintListener { override var pdfListner : PDFPrint.OnPDFPrintListener? = object : PDFPrint.OnPDFPrintListener {
override fun onSuccess(file: File?) { override fun onSuccess(file: File?) {
LogUtil.e("file.absolutePath >>> ${file?.absolutePath}") LogUtil.e("file.absolutePath >>> ${file?.absolutePath}")
Toast.makeText(
applicationContext,
resources.getString(
stringResPhotoSavedTo
) + file?.absolutePath,
Toast.LENGTH_LONG
).apply {
}.show()
} }
override fun onError(exception: Exception?) { override fun onError(exception: Exception?) {
} }
} }
// val tika: Tika = Tika()
override fun dispatchGenericMotionEvent(ev: MotionEvent?): Boolean { override fun dispatchGenericMotionEvent(ev: MotionEvent?): Boolean {
if (ev?.device?.name?.contains("BLE-M3") == true) { if (ev?.device?.name?.contains("BLE-M3") == true) {
Blog.LOGE("keyEvent >>>>> dispatchGenericMotionEvent ${ev}") Blog.LOGE("keyEvent >>>>> dispatchGenericMotionEvent ${ev}")
@ -344,6 +389,7 @@ class RssViewerActivity : AwesomeWebViewActivity(), View.OnGenericMotionListene
registCancelSearch() registCancelSearch()
} }
protected fun load(newUrl: String) { protected fun load(newUrl: String) {
LogUtil.e("newUrl >>> ${newUrl}")
newUrl.toUri().host?.let { newUrl.toUri().host?.let {
val splits = it.replace("http://","").replace("https://","").split(".") val splits = it.replace("http://","").replace("https://","").split(".")
when(splits.size) { when(splits.size) {
@ -380,81 +426,332 @@ class RssViewerActivity : AwesomeWebViewActivity(), View.OnGenericMotionListene
} }
} }
override fun onHtml(value: String?) { fun ByteArray.toHex() = joinToString(separator = "") { byte -> "%02x".format(byte) }
fun hashString(str: String, algorithm: String): ByteArray =
MessageDigest.getInstance(algorithm).digest(str.toByteArray(UTF_8))
fun getBase64ImageData(fileName : String) : String {
val inputStream: InputStream =
FileInputStream(fileName) // You can get an inputStream using any I/O API
val bytes: ByteArray
val buffer = ByteArray(8192)
var bytesRead: Int
val output = ByteArrayOutputStream()
try {
while ((inputStream.read(buffer).also { bytesRead = it }) != -1) {
output.write(buffer, 0, bytesRead)
}
} catch (e: IOException) {
e.printStackTrace()
}
bytes = output.toByteArray()
return Base64.encodeToString(bytes, Base64.DEFAULT)
}
fun run(value : String, autoCheck : Boolean) {
webView!!.setOnScrollChangeListener(null)
var mediaUrls = arrayListOf<String>()
mediaUrls.addAll(this.mediaUrls)
CookieSyncManager.createInstance(applicationContext)
CookieSyncManager.getInstance().sync()
val cookieManager =
CookieManager.getInstance()
val cookie =
cookieManager.getCookie(webView!!.url)
val agent = webView!!.settings.userAgentString val agent = webView!!.settings.userAgentString
val current = webView!!.url val current = webView!!.url
val newPath = hashString(current!!, "MD5").toHex()
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
mediaUrls.forEach { LogUtil.e("onHtml value >> ${value}")
val path = File(
Environment.getExternalStorageDirectory(),
"bums"
)
val filePath = "index.html"
val newFolder = File(path, newPath).apply { mkdirs() }
var htmlString = trimHtnl(value!!)
val targetFile = File(newFolder, filePath)
var reqCount = 0
var endOfLooPCheck = false
var urlPathMap = hashMapOf<String, String>()
var newFile = File(
path,
newPath.plus(SimpleDateFormat("yyyMMdd").format(Date())).plus(".html")
)
fun moveFile() {
indexSave(htmlString, targetFile)
targetFile.copyTo(newFile)
if (targetFile.parentFile.isDirectory) {
targetFile.parentFile.listFiles().forEach {
it.delete()
}
targetFile.parentFile.delete()
}
targetFile.delete()
}
fun enofLoop() {
CoroutineScope(Dispatchers.IO).launch {
LogUtil.e("on it enofLoop")
urlPathMap.forEach { t, u ->
val file = File(u)
var contentsUriString = FileProvider.getUriForFile(
this@RssViewerActivity,
"${this@RssViewerActivity.packageName}.fileprovider",
file
).toString()
// try {
// val detectedType: String = tika.detect(file)
// e("Detected type: $detectedType")
// } catch (e: IOException) {
// e("Error detecting type: " + e.message)
// }
var targetString = t
var targetIdx = htmlString.indexOf(targetString)
if (targetIdx > 0) {
htmlString?.replace(
targetIdx,
targetIdx.plus(targetString.length),
contentsUriString
)
}
targetString = t.replace("&", "&amp;")
targetIdx = htmlString.indexOf(targetString)
if (targetIdx > 0) {
htmlString?.replace(
targetIdx,
targetIdx.plus(targetString.length),
contentsUriString
)
}
}
if (autoCheck) {
LogUtil.e("on it enofLoop autoCheck ${autoCheck}")
webView?.postDelayed({
hideBlock()
webView?.setOnScrollChangeListener(null)
moveFile()
startActivity(Intent(this@RssViewerActivity,RssViewerActivity::class.java).apply {
action = Intent.ACTION_VIEW
data = FileProvider.getUriForFile(
this@RssViewerActivity,
"${this@RssViewerActivity.packageName}.fileprovider",
newFile
)
})
}, defaultTime.times(4))
} else {
runOnUiThread {
moveFile()
Toast.makeText(
applicationContext,
resources.getString(
stringResPhotoSavedTo
) + targetFile.absolutePath,
Toast.LENGTH_LONG
).show()
hideBlock()
doNextPage()
LogUtil.e("on it enofLoop autoCheck ${autoCheck}")
}
}
}
}
val lDownFinishListener = object : DownFinishListener {
override fun onDownFinish(url: String, path: String) {
LogUtil.e("Url >> ${url} path >> ${path}")
urlPathMap.put(url, path)
reqCount -= 1
if (reqCount == 0 && endOfLooPCheck) {
enofLoop()
}
}
override fun onError() {
if (showToastPhotoSavedOrFailed) {
Toast.makeText(
applicationContext,
resources.getString(
stringResPhotoSaveFailed
),
Toast.LENGTH_LONG
).show()
}
reqCount -= 1
if (reqCount == 0 && endOfLooPCheck) {
enofLoop()
}
}
}
mediaUrls.forEach { url ->
var downPic = false
try { try {
LogUtil.e("try Jsoup.parse ${it}") LogUtil.e("try Jsoup.parse ${url}")
Jsoup.parse(URL(it), 3000)?.let { Jsoup.parse(URL(url), defaultTime.times(4).toInt()).let {
try { try {
LogUtil.e("onit Jsoup.parse ${it.title()}") LogUtil.e("onit Jsoup.parse ${it.title()}")
it.getElementsByTag("video")?.forEach { if (it.select("svg").size > 0) {
it.attribute("src").value?.let { try {
var url = if (it.startsWith("http")) { downPic(url, agent, current!!, cookie!!, lDownFinishListener)
it reqCount += 1
} else { } catch (e : Exception) {
"https:".plus(it) e.printStackTrace()
}
} else {
it.getElementsByTag("video")?.forEach {
it.attribute("src").value?.let {
var url = if (it.startsWith("http")) {
it
} else {
"https:".plus(it)
}
try {
downMp4(url, agent, current!!, cookie!!, lDownFinishListener)
reqCount += 1
} catch (e : Exception) {
e.printStackTrace()
}
} }
val cookieManager =
CookieManager.getInstance()
val cookie =
cookieManager.getCookie(current)
DownPicUtil.downMp4(
File(
Environment.getExternalStorageDirectory(),
"bums"
).path,
url,
agent,
current,
cookie,
object : DownFinishListener {
override fun onDownFinish(path: String) {
if (showToastPhotoSavedOrFailed) {
Toast.makeText(
applicationContext,
resources.getString(
stringResPhotoSavedTo
) + path,
Toast.LENGTH_LONG
).show()
}
// 最后通知图库更新
applicationContext.sendBroadcast(
Intent(
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
Uri.parse("file://$path")
)
)
}
override fun onError() {
if (showToastPhotoSavedOrFailed) {
Toast.makeText(
applicationContext,
resources.getString(
stringResPhotoSaveFailed
),
Toast.LENGTH_LONG
).show()
}
}
})
} }
} }
} catch (e : UnsupportedMimeTypeException) { } catch (e: UnsupportedMimeTypeException) {
LogUtil.e("e.message3 ${e.message}")
} catch (e : Exception){} } catch (e: Exception) {
LogUtil.e("e.message4 ${e.message}")
}
} }
} catch (e : UnsupportedMimeTypeException) { } catch (e: UnsupportedMimeTypeException) {
LogUtil.e("e.message ${e.message}")
LogUtil.e("e.message ${e.localizedMessage}")
if (e.message?.contains("Must be text") == true) {
downPic = true
}
} catch (e: Exception) {
if (url.contains("dcimg") == true) {
downPic = true
}
LogUtil.e("e.message2 ${e.message}")
} finally {
if (downPic) {
try {
downPic(url, agent, current!!, cookie!!, lDownFinishListener)
reqCount += 1
} catch (e : Exception) {
e.printStackTrace()
}
} catch (e : Exception){} }
}
sleep(defaultTime)
}
LogUtil.e("END OF LOOP ${reqCount}")
endOfLooPCheck = true
if (reqCount <= 0) {
webView?.postDelayed({
enofLoop()
},defaultTime.times(4))
} }
} }
} }
val defaultTime = 200L
override fun onHtml(value: String?, autoCheck : Boolean) {
chechHandler.removeCallbacks(cancelSearch)
if (loadWithIntent){
return
}
showBlock()
var count = (webView!!.contentHeight / (webView!!.height * 0.3).toInt())
LogUtil.e("count >>>> ${count} webView!!.contentHeight >>>> ${webView!!.contentHeight} , webView!!.height >>>> ${webView!!.height} :: ${(webView!!.height * 0.3).toInt()}")
webView!!.postDelayed({
webView!!.pageDown(false)
},defaultTime)
webView!!.setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY ->
val measuredHeight: Int = v.measuredHeight
if(measuredHeight + scrollY == webView!!.computeVerticalScrollRange()){
webView!!.postDelayed({
webView!!.evaluateJavascript("document.documentElement.outerHTML",object : ValueCallback<String> {
override fun onReceiveValue(value: String?) {
val html = value?.replace("\\u003C", "<")
this@RssViewerActivity.run(html!!, autoCheck)
}
})
},defaultTime.times(2))
} else {
webView!!.postDelayed({
webView!!.pageDown(false)
},defaultTime)
}
}
}
fun trimHtnl(target : String) : StringBuffer {
var result = target.replace("\\\"","\"").replace("\\n\\t\\t\\t","").replace("\\n\\t\\t","").replace("\\n\\t","").replace("\\t", "").replace("\\n", "").replace("\"\"\"","").replace("href=\"//","href=\"https://").replace("src=\"//","src=\"https://").replace("url(\"//","url(\"https://").replace("url(&quot;//","url(&quot;https://")
if (host?.contains("clien") == true) {
result = result.replace("href=\"/service","href=\"https://m.clien.net/service").replace("href=\"\\&quot;/service","href=\"\\&quot;https://m.clien.net/service")
}
return StringBuffer(result)
}
fun indexSave(htmlString:StringBuffer, targetFile :File) {
BufferedWriter(FileWriter(targetFile)).use { writer ->
trimHtnl(htmlString.toString())?.let { result ->
writer.write(result.toString())
}
}
}
fun downMp4(url : String, agent : String, current : String,cookie : String, listner :DownFinishListener) {
LogUtil.e("try imageFile down ${url}")
val path = File(
Environment.getExternalStorageDirectory(),
"bums"
)
DownPicUtil.downMp4(
File(
path,"private_mp4"
).path,
url,
agent,
current,
cookie,
listner
)
}
fun downPic( url : String, agent : String, current : String,cookie : String, listner :DownFinishListener) {
LogUtil.e("try imageFile down ${url}")
val cookieManager =
CookieManager.getInstance()
val cookie =
cookieManager.getCookie(current)
val path = File(
Environment.getExternalStorageDirectory(),
"bums"
)
DownPicUtil.downPic(
File(path, "private_img").path,
url,
agent,
current,
cookie,
listner)
}
override fun webviewOnPageFinished() { override fun webviewOnPageFinished() {
double = false double = false
if(hasYoutubePlayer) { if(hasYoutubePlayer) {
@ -473,14 +770,35 @@ class RssViewerActivity : AwesomeWebViewActivity(), View.OnGenericMotionListene
} }
} }
registCancelSearch() registCancelSearch()
//<meta name="viewport" content="initial-scale=1.0">
if (loadWithIntent) {
webView?.evaluateJavascript(
"try{document.querySelector('meta[name=viewport]').setAttribute('content','initial-scale=1.0')}catch(e){}",
null
)
webView?.evaluateJavascript(
"try{document.querySelector('meta[name=viewport]').setAttribute('content','initial-scale=1.0')}catch(e){}",
null
)
webView?.settings?.useWideViewPort = true
}
if (webView?.url?.contains("dcinside") == true){ if (webView?.url?.contains("dcinside") == true){
webView?.evaluateJavascript("try{document.querySelector('#div_adnmore_area').hidden = true;}catch(e){}",null) webView?.evaluateJavascript("try{document.querySelector('#div_adnmore_area').hidden = true;}catch(e){}",null)
} else if(webView?.url?.contains("fmkorea") == true) { } else if(webView?.url?.contains("fmkorea") == true) {
webView?.postDelayed({ if (loadWithIntent) {
webView?.evaluateJavascript("try{document.querySelector('.m_rd_nav_side').hidden = true;}catch(e){}",null)
webView?.evaluateJavascript("try{document.querySelector(\"[class*='bd bd_mobile ']\").remove();}catch(e){}",null) } else {
},500L) webView?.postDelayed({
webView?.evaluateJavascript(
"try{document.querySelector('.m_rd_nav_side').hidden = true;}catch(e){}",
null
)
webView?.evaluateJavascript(
"try{document.querySelector('[class*='bd bd_mobile ']').remove();}catch(e){}",
null
)
}, 500L)
}
} }
} }
} }

View File

@ -41,6 +41,7 @@ dependencies {
implementation ("androidx.annotation:annotation:1.9.1") implementation ("androidx.annotation:annotation:1.9.1")
implementation ("androidx.appcompat:appcompat:1.7.0") implementation ("androidx.appcompat:appcompat:1.7.0")
implementation ("com.google.android.material:material:1.12.0") implementation ("com.google.android.material:material:1.12.0")
// implementation ("org.apache.tika:tika-parsers:1.24")
// implementation ("com.nineoldandroids:library:2.4.0") // implementation ("com.nineoldandroids:library:2.4.0")
implementation ("androidx.core:core-ktx:1.15.0") implementation ("androidx.core:core-ktx:1.15.0")
implementation(project(":utils")) implementation(project(":utils"))

View File

@ -99,6 +99,7 @@ import kotlin.math.abs
open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener, open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
Handler.Callback { Handler.Callback {
var loadWithIntent : Boolean = false
var mediaUrls = arrayListOf<String>() var mediaUrls = arrayListOf<String>()
protected var key: Int = 0 protected var key: Int = 0
@ -258,7 +259,7 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
// protected var back: AppCompatImageButton? = null // protected var back: AppCompatImageButton? = null
// protected var forward: AppCompatImageButton? = null // protected var forward: AppCompatImageButton? = null
// protected var more: AppCompatImageButton? = null // protected var more: AppCompatImageButton? = null
protected var webView: WebView? = null protected var webView: VideoEnabledWebView? = null
protected var webChromeClient: WebChromeClient? = null protected var webChromeClient: WebChromeClient? = null
protected var webViewClient: WebViewClient? = null protected var webViewClient: WebViewClient? = null
// protected var gradient: View? = null // protected var gradient: View? = null
@ -577,7 +578,7 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
fun fast() { fun fast() {
chechHandler.removeCallbacks(cancelSearch) chechHandler.removeCallbacks(cancelSearch)
chechHandler.postDelayed(cancelSearch, 6000L) chechHandler.postDelayed(cancelSearch, 90000L)
} }
fun registCancelSearch() { fun registCancelSearch() {
@ -799,12 +800,14 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
LogUtil.e("hitTestResult.type >>> ${hitTestResult.type}") LogUtil.e("hitTestResult.type >>> ${hitTestResult.type}")
LogUtil.e("hitTestResult.extra >>> ${hitTestResult.extra}") LogUtil.e("hitTestResult.extra >>> ${hitTestResult.extra}")
LogUtil.e("hitTestResult >>> ${mediaUrls.size}")
if(hitTestResult.type==0) { if(hitTestResult.type==0) {
webView?.evaluateJavascript("document.getElementsByTagName('iframe')",
webView?.evaluateJavascript("document.documentElement.outerHTML",
object : ValueCallback<String> { object : ValueCallback<String> {
override fun onReceiveValue(value: String?) { override fun onReceiveValue(value: String?) {
val html = value?.replace("\\u003C", "<") val html = value?.replace("\\u003C", "<")
onHtml(html) onHtml(html, true)
} }
}) })
} }
@ -839,7 +842,7 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
webView!!.url, webView!!.url,
cookie, cookie,
object : DownFinishListener { object : DownFinishListener {
override fun onDownFinish(path: String) { override fun onDownFinish(url : String ,path: String) {
if (showToastPhotoSavedOrFailed) { if (showToastPhotoSavedOrFailed) {
Toast.makeText( Toast.makeText(
applicationContext, applicationContext,
@ -1243,11 +1246,19 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
} }
} }
open protected fun onHtml(value: String?) { open protected fun onHtml(value: String?, autoCheck : Boolean) {
} }
protected fun buildWebView(): WebView { protected fun showBlock() {
binding.blocking.visibility = View.VISIBLE
}
protected fun hideBlock() {
binding.blocking.visibility = View.GONE
}
protected fun buildWebView(): VideoEnabledWebView {
return VideoEnabledWebView(this) return VideoEnabledWebView(this)
} }
@ -1874,10 +1885,10 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
view: WebView, view: WebView,
request: WebResourceRequest request: WebResourceRequest
): WebResourceResponse? { ): WebResourceResponse? {
var skipResource = var skipResource =
(host != null) && ((request.url?.host?.contains(host!!) ?: true) == false) (host != null) && ((request.url?.host?.contains(host!!) ?: true) == false)
if (skipResource && request.url.toString().contains("gif")) { if (skipResource && request.url.toString().contains("gif")) {
LogUtil.e("shouldInterceptRequest request block gif resource >>> ${request.url.toString()}")
return WebResourceResponse( return WebResourceResponse(
"text/plain", "utf-8", "text/plain", "utf-8",
ByteArrayInputStream("".toByteArray()) ByteArrayInputStream("".toByteArray())
@ -1885,13 +1896,27 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
} }
val url = request.url.toString() val url = request.url.toString()
if (url.contains("streamable.com") ||
url.contains("img-cdn.theqoo") ||
url.contains("embed/player/") ||
url.contains("dcinside.co.kr/viewimage") ||
url.contains("daumcdn.net/cafeattach") ||
url.toLowerCase(Locale.ROOT).contains(".jpg") ||
url.toLowerCase(Locale.ROOT).contains(".png") ||
url.toLowerCase(Locale.ROOT).contains(".gif") ||
url.toLowerCase(Locale.ROOT).contains(".svg") ||
url.toLowerCase(Locale.ROOT).contains(".webp")) {
mediaUrls.add(url)
LogUtil.e("mediaUrls >>>>> add(${url})")
}
if(!hasYoutubePlayer) { if(!hasYoutubePlayer) {
hasYoutubePlayer = url.toLowerCase(Locale.ROOT) hasYoutubePlayer = url.toLowerCase(Locale.ROOT)
.contains("https://www.youtube.com/s/player".toLowerCase(Locale.ROOT)) .contains("https://www.youtube.com/s/player".toLowerCase(Locale.ROOT))
} }
if (url.contains("streamable.com")) {
mediaUrls.add(url) //https://t1.daumcdn.net/cafeattach/mEr9/adc6e81a386099c4cc06f77f8b7eea15675de0d4
}
if (url.toLowerCase(Locale.ROOT) if (url.toLowerCase(Locale.ROOT)
.contains("ads".toLowerCase(Locale.ROOT))) { .contains("ads".toLowerCase(Locale.ROOT))) {
LogUtil.e("shouldInterceptRequest request url contains ads >>> ${request.url.toString()}") LogUtil.e("shouldInterceptRequest request url contains ads >>> ${request.url.toString()}")
@ -1900,7 +1925,7 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
val adblock = adblockKeyWords.filter { url.toLowerCase(Locale.ROOT).contains(it.toLowerCase(Locale.ROOT)) }.size > 0 val adblock = adblockKeyWords.filter { url.toLowerCase(Locale.ROOT).contains(it.toLowerCase(Locale.ROOT)) }.size > 0
return if(adblock) { return if(adblock) {
try { try {
LogUtil.e("shouldInterceptRequest request block adblockKeyWords resource >>> ${request.url.toString()}") // LogUtil.e("shouldInterceptRequest request block adblockKeyWords resource >>> ${request.url.toString()}")
WebResourceResponse("text/plain", "utf-8", ByteArrayInputStream("".toByteArray())) WebResourceResponse("text/plain", "utf-8", ByteArrayInputStream("".toByteArray()))
} catch (e : Exception) { } catch (e : Exception) {
super.shouldInterceptRequest(view, url) super.shouldInterceptRequest(view, url)
@ -1909,7 +1934,7 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
try { try {
val bitmap = Glide.with(this@AwesomeWebViewActivity).asBitmap().timeout(30000).diskCacheStrategy(DiskCacheStrategy.ALL).load(url).submit().get() val bitmap = Glide.with(this@AwesomeWebViewActivity).asBitmap().timeout(30000).diskCacheStrategy(DiskCacheStrategy.ALL).load(url).submit().get()
WebResourceResponse("image/jpg", "UTF-8",getBitmapInputStream(bitmap,Bitmap.CompressFormat.JPEG)).apply { WebResourceResponse("image/jpg", "UTF-8",getBitmapInputStream(bitmap,Bitmap.CompressFormat.JPEG)).apply {
LogUtil.e("shouldInterceptRequest request url down from Glide >>> ${request.url.toString()}") // LogUtil.e("shouldInterceptRequest request url down from Glide >>> ${request.url.toString()}")
} }
} catch (e : Exception) { } catch (e : Exception) {
super.shouldInterceptRequest(view, url) super.shouldInterceptRequest(view, url)
@ -1918,7 +1943,7 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
try { try {
val bitmap = Glide.with(this@AwesomeWebViewActivity).asBitmap().timeout(30000).diskCacheStrategy(DiskCacheStrategy.ALL).load(url).submit().get() val bitmap = Glide.with(this@AwesomeWebViewActivity).asBitmap().timeout(30000).diskCacheStrategy(DiskCacheStrategy.ALL).load(url).submit().get()
WebResourceResponse("image/png", "UTF-8",getBitmapInputStream(bitmap,Bitmap.CompressFormat.PNG)).apply { WebResourceResponse("image/png", "UTF-8",getBitmapInputStream(bitmap,Bitmap.CompressFormat.PNG)).apply {
LogUtil.e("shouldInterceptRequest request url down from Glide >>> ${request.url.toString()}") // LogUtil.e("shouldInterceptRequest request url down from Glide >>> ${request.url.toString()}")
} }
} catch (e : Exception) { } catch (e : Exception) {
super.shouldInterceptRequest(view, url) super.shouldInterceptRequest(view, url)
@ -1950,6 +1975,7 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
// } // }
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) { override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
mediaUrls.clear()
BroadCastManager.onPageStarted(this@AwesomeWebViewActivity, key, url) BroadCastManager.onPageStarted(this@AwesomeWebViewActivity, key, url)
if (!url.contains("docs.google.com") && url.endsWith(".pdf")) { if (!url.contains("docs.google.com") && url.endsWith(".pdf")) {
webView!!.loadUrl("http://docs.google.com/gview?embedded=true&url=$url") webView!!.loadUrl("http://docs.google.com/gview?embedded=true&url=$url")
@ -1973,7 +1999,9 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
BroadCastManager.onPageFinished(this@AwesomeWebViewActivity, key, url) BroadCastManager.onPageFinished(this@AwesomeWebViewActivity, key, url)
if (updateTitleFromHtml) binding.toolbarContent.title.text = view.title if (updateTitleFromHtml) binding.toolbarContent.title.text = view.title
binding.toolbarContent.url.text = UrlParser.getHost(url) try {
binding.toolbarContent.url.text = UrlParser.getHost(url)
}catch (e :Exception) {e.printStackTrace()}
requestCenterLayout() requestCenterLayout()
if (view.canGoBack() || view.canGoForward()) { if (view.canGoBack() || view.canGoForward()) {
@ -2006,7 +2034,7 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
handler.sendEmptyMessage(MSG_CLICK_ON_URL) handler.sendEmptyMessage(MSG_CLICK_ON_URL)
var skipResource = host!= null && ((url.contains(host!!) ?: true) == false) && (host!!.contains("google") == false) var skipResource = host!= null && ((url.contains(host!!) ?: true) == false) && (host!!.contains("google") == false)
if (skipResource) { if (skipResource || loadWithIntent) {
LogUtil.e("shouldOverrideUrlLoading block url $url , host >>>> $host ") LogUtil.e("shouldOverrideUrlLoading block url $url , host >>>> $host ")
val alertDialog = AlertDialog.Builder(view.context).create() val alertDialog = AlertDialog.Builder(view.context).create()
alertDialog.setCancelable(false) alertDialog.setCancelable(false)

View File

@ -4,9 +4,14 @@ package kr.lunaticbum.awesomewebview.helpers;
* Created by wuzongheng on 2018/2/18. * Created by wuzongheng on 2018/2/18.
*/ */
import static java.sql.DriverManager.println;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Environment; import android.os.Environment;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -19,6 +24,11 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import kr.lunaticbum.utils.log.LogUtil;
/** /**
* 图片下载的工具类 * 图片下载的工具类
@ -46,6 +56,10 @@ public class DownPicUtil {
} }
loadPic(file.getPath(), url, userAgent, referer, cookie, downFinishListener, false); loadPic(file.getPath(), url, userAgent, referer, cookie, downFinishListener, false);
} else { } else {
File file = new File(path);
if (!file.exists()) {
file.mkdirs();
}
loadPic(path, url, userAgent, referer, cookie, downFinishListener, false); loadPic(path, url, userAgent, referer, cookie, downFinishListener, false);
} }
} }
@ -59,24 +73,53 @@ public class DownPicUtil {
} }
loadPic(file.getPath(), url, userAgent, referer, cookie, downFinishListener, true); loadPic(file.getPath(), url, userAgent, referer, cookie, downFinishListener, true);
} else { } else {
File file = new File(path);
if (!file.exists()) {
file.mkdirs();
}
loadPic(path, url, userAgent, referer, cookie, downFinishListener, true); loadPic(path, url, userAgent, referer, cookie, downFinishListener, true);
} }
} }
public static String toHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
public static byte[] hashString(String str, String algorithm) {
try {
MessageDigest digest = MessageDigest.getInstance(algorithm);
return digest.digest(str.getBytes(StandardCharsets.UTF_8));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
private static void loadPic(final String filePath, final String url, final String userAgent, final String referer, final String cookie, final DownFinishListener downFinishListener, boolean isMp4) { private static void loadPic(final String filePath, final String url, final String userAgent, final String referer, final String cookie, final DownFinishListener downFinishListener, boolean isMp4) {
new AsyncTask<Void,Void,String>(){ new AsyncTask<Void,Void,String>(){
String fileName; String fileName;
InputStream is; InputStream is;
OutputStream out; OutputStream out;
private String replaceDcUrl(String origin) {
String result = origin;
for (int i = 0; i < 20; i++) {
result = result.replace(String.format("dcimg%d.",i),"dcimg2.");
}
return result;
}
@Override @Override
protected String doInBackground(Void... voids) { protected String doInBackground(Void... voids) {
// 原文件名 // 原文件名
String[] split = url.split("/"); String[] split = url.split("/");
fileName = split[split.length - 1]; byte[] dd = hashString(url, "MD5");
fileName = dd == null ? split[split.length - 1] : toHex(dd);
// 创建目标文件使用时间戳作为临时文件名确保可以不重复 // 创建目标文件使用时间戳作为临时文件名确保可以不重复
String now = String.valueOf(System.currentTimeMillis()); String now = String.valueOf(System.currentTimeMillis());
@ -90,7 +133,13 @@ public class DownPicUtil {
byte[] image = base64ImgHelper.decode(); byte[] image = base64ImgHelper.decode();
is = new ByteArrayInputStream(image); //处理服务器的响应结果 is = new ByteArrayInputStream(image); //处理服务器的响应结果
} else { } else {
URL picUrl = new URL(url); URL picUrl = null;
if (url.contains("dcimg")) {
picUrl = new URL(replaceDcUrl(url));
} else {
picUrl = new URL(url);
}
//通过图片的链接打开输入流 //通过图片的链接打开输入流
HttpURLConnection httpURLConnection = (HttpURLConnection) picUrl.openConnection(); HttpURLConnection httpURLConnection = (HttpURLConnection) picUrl.openConnection();
httpURLConnection.setConnectTimeout(10000); //设置连接超时时间 httpURLConnection.setConnectTimeout(10000); //设置连接超时时间
@ -99,13 +148,26 @@ public class DownPicUtil {
httpURLConnection.setDoOutput(false); //Get请求不需要DoOutPut httpURLConnection.setDoOutput(false); //Get请求不需要DoOutPut
httpURLConnection.setRequestMethod("GET"); //设置以Get方式请求数据 httpURLConnection.setRequestMethod("GET"); //设置以Get方式请求数据
httpURLConnection.setUseCaches(false); //不使用缓存 httpURLConnection.setUseCaches(false); //不使用缓存
//设置请求体的类型是文本类型 //设置请求体的类型是文本类型
// if (url.contains("dcimg")) {
//// httpURLConnection.setRequestProperty("authority", Uri.parse(url).getHost());
// httpURLConnection.setRequestProperty("Accept", "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8");
// httpURLConnection.setRequestProperty("Accept-encoding", "gzip, deflate, br, zstd");
// httpURLConnection.setRequestProperty("Sec-Fetch-Dest","image");
// httpURLConnection.setRequestProperty("Sec-Fetch-Mode","no-cors");
// httpURLConnection.setRequestProperty("Sec-Fetch-Site","cross-site");
// }
httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
if (!TextUtils.isEmpty(userAgent)) { if (!TextUtils.isEmpty(userAgent)) {
httpURLConnection.setRequestProperty("User-Agent", userAgent); httpURLConnection.setRequestProperty("User-Agent", userAgent);
} }
if (!TextUtils.isEmpty(referer)) { if (!TextUtils.isEmpty(referer)) {
httpURLConnection.setRequestProperty("Referer", referer); httpURLConnection.setRequestProperty("Referer", referer);
println("referer>>>> " + referer);
} }
if (!TextUtils.isEmpty(cookie)) { if (!TextUtils.isEmpty(cookie)) {
httpURLConnection.setRequestProperty("Cookie", cookie); httpURLConnection.setRequestProperty("Cookie", cookie);
@ -148,9 +210,7 @@ public class DownPicUtil {
// 提取文件格式真实拓展名 // 提取文件格式真实拓展名
String extension = FormatHelper.getExtension(picFile); String extension = FormatHelper.getExtension(picFile);
if (isMp4) {
extension = "mp4";
}
// 提取不包含拓展名的原文件名 // 提取不包含拓展名的原文件名
String[] extensions = fileName.split("\\."); String[] extensions = fileName.split("\\.");
int splitLength = extensions.length; int splitLength = extensions.length;
@ -160,6 +220,9 @@ public class DownPicUtil {
} else { } else {
newFileNameNoExtension = fileName; newFileNameNoExtension = fileName;
} }
if (isVideoFileByMetadata(picFile) && extension == null && isMp4) {
extension = "mp4";
}
// 重命名文件 // 重命名文件
if (extension == null) { if (extension == null) {
@ -171,21 +234,24 @@ public class DownPicUtil {
// 无拓展名整个文件名递增重命名 // 无拓展名整个文件名递增重命名
return renamePic(picFile, filePath, newFileNameNoExtension, null, MODE.MODE_INCREMENT); return renamePic(picFile, filePath, newFileNameNoExtension, null, MODE.MODE_INCREMENT);
} }
} else {
return renamePic(picFile, filePath, newFileNameNoExtension, extension, MODE.MODE_IGNORE);
} }
// 支持解析的格式使用md5文件名真实拓展名 // 支持解析的格式使用md5文件名真实拓展名
String md5 = Md5Helper.getFileMD5ToString(picFile); // return picFile.getPath();
if (TextUtils.isEmpty(md5)) { // String md5 = Md5Helper.getFileMD5ToString(picFile);
return renamePic(picFile, filePath, newFileNameNoExtension, extension, MODE.MODE_INCREMENT); // if (TextUtils.isEmpty(md5)) {
} else { // return renamePic(picFile, filePath, newFileNameNoExtension, extension, MODE.MODE_INCREMENT);
return renamePic(picFile, filePath, md5.substring(0, 16), extension, MODE.MODE_IGNORE); // } else {
} // return renamePic(picFile, filePath, md5.substring(0, 16), extension, MODE.MODE_IGNORE);
// }
} }
@Override @Override
protected void onPostExecute(String path) { protected void onPostExecute(String path) {
super.onPostExecute(path); super.onPostExecute(path);
if(path!=null){ if(path!=null){
downFinishListener.onDownFinish(path); downFinishListener.onDownFinish(url,path);
} else { } else {
downFinishListener.onError(); downFinishListener.onError();
} }
@ -208,6 +274,26 @@ public class DownPicUtil {
MODE_OVERRIDE MODE_OVERRIDE
} }
private static Boolean isVideoFileByMetadata(File file) {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource(file.getAbsolutePath());
println("retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_MIMETYPE) >>> " + retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_MIMETYPE));
String duration = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
return duration != null;
} catch (Exception e) {
return false;
} finally {
try {
retriever.release();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static String renamePic(File picFile, String filePath, String newFileNameNoExtension, String extension, MODE mode) { private static String renamePic(File picFile, String filePath, String newFileNameNoExtension, String extension, MODE mode) {
String extensionWithPoint = TextUtils.isEmpty(extension)? "": "." + extension; String extensionWithPoint = TextUtils.isEmpty(extension)? "": "." + extension;
String newFileName = newFileNameNoExtension + extensionWithPoint; String newFileName = newFileNameNoExtension + extensionWithPoint;
@ -262,7 +348,7 @@ public class DownPicUtil {
//下载完成回调的接口 //下载完成回调的接口
public interface DownFinishListener{ public interface DownFinishListener{
void onDownFinish(String path); void onDownFinish(String srcUrl ,String path);
void onError(); void onError();
} }
} }

View File

@ -6,6 +6,8 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import kr.lunaticbum.utils.log.LogUtil;
public class FormatHelper { public class FormatHelper {
private FormatHelper() { private FormatHelper() {
@ -13,6 +15,7 @@ public class FormatHelper {
public static String getExtension(File file) { public static String getExtension(File file) {
try { try {
// long fileLength = file.length(); // long fileLength = file.length();
FileInputStream fileInputStream = new FileInputStream(file); FileInputStream fileInputStream = new FileInputStream(file);
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
@ -81,7 +84,34 @@ public class FormatHelper {
start[5] == (byte) 0x61) { start[5] == (byte) 0x61) {
return "gif"; return "gif";
} }
bufferedInputStream.reset();
start = new byte[6];
bufferedInputStream.read(start);
if (start[0] == (byte) 0x47 &&
start[1] == (byte) 0x49 &&
start[2] == (byte) 0x46 &&
start[3] == (byte) 0x38 &&
start[4] == (byte) 0x37 &&
start[5] == (byte) 0x61) {
return "gif";
}
bufferedInputStream.reset();
start = new byte[100];
bufferedInputStream.read(start);
String xmlDeclaration = new String(start, 0, 100, "UTF-8");
LogUtil.INSTANCE.e("Path " + file.getAbsolutePath() + " ::: HEADE" + xmlDeclaration + ";");
if (xmlDeclaration.contains("<?xml version=\"1.0\" encoding=\"UTF-8\"?>") ||
xmlDeclaration.contains("http://www.w3.org/2000/svg")) {
return "svg";
}else if (xmlDeclaration.contains("iso") &&
xmlDeclaration.contains("mp4")) {
return "mp4";
}
bufferedInputStream.reset();
LogUtil.INSTANCE.e("Path " + file.getAbsolutePath() + " ::: HEADE" + start[0] + "," + start[1] + "," + start[2] + "," + start[3] + "," + start[4] + "," + start[5] + ";");
bufferedInputStream.close(); bufferedInputStream.close();
fileInputStream.close(); fileInputStream.close();
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {

View File

@ -97,5 +97,54 @@ public class VideoEnabledWebView extends WebView
videoJsHelper.addJavascriptInterface(this); videoJsHelper.addJavascriptInterface(this);
super.loadUrl(url, additionalHttpHeaders); super.loadUrl(url, additionalHttpHeaders);
} }
private boolean topReached = false;
private boolean bottomReached = false;
@Override
public int computeVerticalScrollRange() {
int readerViewHeight = getMeasuredHeight();
int verticalScrollRange = super.computeVerticalScrollRange();
if (readerViewHeight >= verticalScrollRange) {
topReached = true;
bottomReached = true;
}
return verticalScrollRange;
}
@Override
public void onScrollChanged(int newLeft, int newTop, int oldLeft, int oldTop) {
topReached = false;
bottomReached = false;
int readerViewHeight = getMeasuredHeight();
int contentHeight = getContentHeight();
if (newTop == 0) {
topReached = true;
} else if (newTop + readerViewHeight >= contentHeight) {
bottomReached = true;
}
super.onScrollChanged(newLeft, newTop, oldLeft, oldTop);
}
} }

View File

@ -82,6 +82,31 @@
android:visibility="invisible" /> --> android:visibility="invisible" /> -->
</RelativeLayout> </RelativeLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/blocking"
android:visibility="gone"
android:background="#EE000000"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.progressindicator.CircularProgressIndicator
android:visibility="visible"
app:trackThickness="10dp"
android:layout_margin="20dp"
app:trackColor="@color/finestWhite"
app:indicatorColor="@color/Color_FireBrick"
android:layout_gravity="center_horizontal"
android:progress="80"
app:trackCornerRadius="8dp"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView
android:gravity="center"
android:textColor="#FFFFFF"
android:textSize="30dp"
android:text="저장을 위해\n리소스 모으는중..."
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout> </layout>

View File

@ -1,13 +0,0 @@
package com.thefinestartist.utils;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@ -1,278 +0,0 @@
package com.thefinestartist.utils.etc;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
import kr.lunaticbum.Base;
import kr.lunaticbum.utils.preferences.PreferencesUtil;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
/**
* Tests of the {@link PreferencesUtil} class.
*
* @author Robin Gustafsson
*/
public class PreferencesUtilTest extends AndroidTestCase {
@Override
public void setUp() throws Exception {
super.setUp();
Base.initialize(getContext());
}
@SmallTest
public void testSetGetDefaultName() {
final String expected = "TEST_DEFAULT_NAME";
PreferencesUtil.setDefaultName(expected);
String actual = PreferencesUtil.getDefaultName();
assertEquals(expected, actual);
}
@SmallTest
public void testDifferentNames() {
final String name1 = "TEST_DIFFERENTNAMES_NAME1";
final String name2 = "TEST_DIFFERENTNAMES_NAME2";
final String key = "TEST_DIFFERENTNAMES_KEY";
final boolean value = true;
final boolean expected = false;
PreferencesUtil.put(name1, key, value);
boolean actual = PreferencesUtil.get(name2, key, expected);
assertEquals(expected, actual);
}
@SmallTest
public void testStoreBoolean() {
final String key = "TEST_BOOLEAN";
final boolean expected = true;
final boolean defValue = false;
PreferencesUtil.put(key, expected);
boolean actual = PreferencesUtil.get(key, defValue);
assertEquals(expected, actual);
}
@SmallTest
public void testStoreBooleanNamed() {
final String name = "TEST_NAMED";
final String key = "TEST_BOOLEAN";
final boolean expected = true;
final boolean defValue = false;
PreferencesUtil.put(name, key, expected);
boolean actual = PreferencesUtil.get(name, key, defValue);
assertEquals(expected, actual);
}
@SmallTest
public void testStoreInt() {
final String key = "TEST_INT";
final int expected = 321;
final int defValue = 0;
PreferencesUtil.put(key, expected);
int actual = PreferencesUtil.get(key, defValue);
assertEquals(expected, actual);
}
@SmallTest
public void testStoreIntNamed() {
final String name = "TEST_NAMED";
final String key = "TEST_INT";
final int expected = 321;
final int defValue = 0;
PreferencesUtil.put(name, key, expected);
int actual = PreferencesUtil.get(name, key, defValue);
assertEquals(expected, actual);
}
@SmallTest
public void testStoreFloat() {
final String key = "TEST_FLOAT";
final float expected = 12.3f;
final float defValue = 0.0f;
PreferencesUtil.put(key, expected);
float actual = PreferencesUtil.get(key, defValue);
assertEquals(expected, actual);
}
@SmallTest
public void testStoreFloatNamed() {
final String name = "TEST_NAMED";
final String key = "TEST_FLOAT";
final float expected = 12.3f;
final float defValue = 0.0f;
PreferencesUtil.put(name, key, expected);
float actual = PreferencesUtil.get(name, key, defValue);
assertEquals(expected, actual);
}
@SmallTest
public void testStoreLong() {
final String key = "TEST_LONG";
final long expected = 321L;
final long defValue = 0L;
PreferencesUtil.put(key, expected);
long actual = PreferencesUtil.get(key, defValue);
assertEquals(expected, actual);
}
@SmallTest
public void testStoreLongNamed() {
final String name = "TEST_NAMED";
final String key = "TEST_LONG";
final long expected = 321L;
final long defValue = 0L;
PreferencesUtil.put(name, key, expected);
long actual = PreferencesUtil.get(name, key, defValue);
assertEquals(expected, actual);
}
@SmallTest
public void testStoreString() {
final String key = "TEST_STRING";
final String expected = "Lorem ipsum";
final String defValue = null;
PreferencesUtil.put(key, expected);
String actual = PreferencesUtil.get(key, defValue);
assertEquals(expected, actual);
}
@SmallTest
public void testStoreStringNamed() {
final String name = "TEST_NAMED";
final String key = "TEST_STRING";
final String expected = "Lorem ipsum";
final String defValue = null;
PreferencesUtil.put(name, key, expected);
String actual = PreferencesUtil.get(name, key, defValue);
assertEquals(expected, actual);
}
@SmallTest
public void testStoreStringSet() {
final String key = "TEST_STRINGSET";
final Set<String> expected = new HashSet<>();
expected.add("Lorem ipsum");
expected.add("dolor sit amet");
expected.add("consectetur adipiscing elit");
final Set<String> defValue = null;
PreferencesUtil.put(key, expected);
Set<String> actual = PreferencesUtil.get(key, defValue);
assertEquals(expected, actual);
}
@SmallTest
public void testStoreStringSetNamed() {
final String name = "TEST_NAMED";
final String key = "TEST_STRINGSET";
final Set<String> expected = new HashSet<>();
expected.add("Lorem ipsum");
expected.add("dolor sit amet");
expected.add("consectetur adipiscing elit");
final Set<String> defValue = null;
PreferencesUtil.put(name, key, expected);
Set<String> actual = PreferencesUtil.get(name, key, defValue);
assertEquals(expected, actual);
}
@MediumTest
public void testStoreSerializable() {
final String key = "TEST_SERIALIZABLE";
final ArrayList<String> expected = new ArrayList<>();
expected.add("Lorem ipsum");
expected.add("dolor sit amet");
expected.add("consectetur adipiscing elit");
final ArrayList<String> defValue = new ArrayList<>();
defValue.add("Proin mollis dictum");
PreferencesUtil.put(key, expected);
ArrayList<String> actual = PreferencesUtil.get(key, defValue);
assertEquals(expected, actual);
}
@MediumTest
public void testStoreSerializableNamed() {
final String name = "TEST_NAMED";
final String key = "TEST_SERIALIZABLE";
final ArrayList<String> expected = new ArrayList<>();
expected.add("Lorem ipsum");
expected.add("dolor sit amet");
expected.add("consectetur adipiscing elit");
final ArrayList<String> defValue = new ArrayList<>();
defValue.add("Proin mollis dictum");
PreferencesUtil.put(name, key, expected);
ArrayList<String> actual = PreferencesUtil.get(name, key, defValue);
assertEquals(expected, actual);
}
@SmallTest
public void testRemove() {
final String key = "TEST_REMOVE";
final String expected = null;
PreferencesUtil.put(key, "Lorem ipsum");
PreferencesUtil.remove(key);
String actual = PreferencesUtil.get(key, expected);
assertEquals(expected, actual);
}
@SmallTest
public void testRemoveNamed() {
final String name = "TEST_NAMED";
final String key = "TEST_REMOVE";
final String expected = null;
PreferencesUtil.put(name, key, "Lorem ipsum");
PreferencesUtil.remove(name, key);
String actual = PreferencesUtil.get(name, key, expected);
assertEquals(expected, actual);
}
@SmallTest
public void testClear() {
final String[] keys = {"TEST_REMOVE_1", "TEST_REMOVE_2", "TEST_REMOVE_2"};
final String expected = null;
for (String key : keys) {
PreferencesUtil.put(key, "Lorem ipsum");
}
PreferencesUtil.clear();
for (String key : keys) {
String actual = PreferencesUtil.get(key, expected);
assertEquals(expected, actual);
}
}
@SmallTest
public void testClearNamed() {
final String name = "TEST_NAMED";
final String[] keys = {"TEST_REMOVE_1", "TEST_REMOVE_2", "TEST_REMOVE_2"};
final String expected = null;
for (String key : keys) {
PreferencesUtil.put(name, key, "Lorem ipsum");
}
PreferencesUtil.clear(name);
for (String key : keys) {
String actual = PreferencesUtil.get(name, key, expected);
assertEquals(expected, actual);
}
}
}

View File

@ -1,15 +0,0 @@
package com.thefinestartist.utils;
import static org.junit.Assert.*;
import org.junit.Test;
/**
* To work on unit tests, switch the Test Artifact in the Build Variants view.
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}