This commit is contained in:
2024-12-20 18:06:54 +09:00
parent 4d8b9b728b
commit 1169b159e8
12 changed files with 692 additions and 444 deletions
+14 -3
View File
@@ -72,7 +72,7 @@
android:enableOnBackInvokedCallback="true"
android:largeHeap="true"
android:networkSecurityConfig="@xml/network_security_config"
android:hardwareAccelerated="true"
android:hardwareAccelerated="false"
android:usesCleartextTraffic="true"
android:screenOrientation="nosensor"
android:windowSoftInputMode="adjustResize"
@@ -191,11 +191,22 @@
<activity
android:name=".home.RssViewerActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:hardwareAccelerated="true"
android:hardwareAccelerated="false"
android:launchMode="singleTask"
android:exported="true"
android:excludeFromRecents="true"
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>
<provider
@@ -1,11 +1,11 @@
package bums.lunatic.launcher.home
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.Environment
import android.print.PDFPrint
import android.util.Base64
import android.view.KeyEvent
import android.view.KeyEvent.ACTION_UP
import android.view.KeyEvent.KEYCODE_BUTTON_A
@@ -22,34 +22,41 @@ import android.view.MotionEvent
import android.view.MotionEvent.ACTION_MOVE
import android.view.View
import android.webkit.CookieManager
import android.webkit.CookieSyncManager
import android.webkit.ValueCallback
import android.widget.Toast
import androidx.core.content.FileProvider
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.utils.Blog
import bums.lunatic.launcher.workers.WorkersDb
import io.realm.kotlin.UpdatePolicy
import io.realm.kotlin.ext.query
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kr.lunaticbum.awesomewebview.AwesomeWebView
import kr.lunaticbum.awesomewebview.AwesomeWebView.Builder
import kr.lunaticbum.awesomewebview.AwesomeWebViewActivity
import kr.lunaticbum.awesomewebview.helpers.DownPicUtil
import kr.lunaticbum.awesomewebview.helpers.DownPicUtil.DownFinishListener
import kr.lunaticbum.awesomewebview.listeners.BroadCastManager
import kr.lunaticbum.awesomewebview.objects.CustomMenu
import kr.lunaticbum.utils.content.ContextUtil
import kr.lunaticbum.utils.log.LogUtil
import org.jsoup.Jsoup
import org.jsoup.UnsupportedMimeTypeException
import org.jsoup.nodes.Element
import java.io.BufferedWriter
import java.io.ByteArrayOutputStream
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.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.Date
import kotlin.text.Charsets.UTF_8
class RssViewBuilder(context: Context) : AwesomeWebView.Builder(context) {
var rssId : String = ""
@@ -87,11 +94,34 @@ class RssViewerActivity : AwesomeWebViewActivity(), View.OnGenericMotionListene
var double = false
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() {
super.onResume()
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
override fun onGenericMotion(p0: View?, ev: MotionEvent?): Boolean {
if (ev?.device?.name?.contains("SM-031N Mouse") == true) {
@@ -172,15 +202,30 @@ class RssViewerActivity : AwesomeWebViewActivity(), View.OnGenericMotionListene
}
fun vote(){
Blog.LOGE("Arrow Center Click")
WorkersDb.getRealm().apply {
writeBlocking {
val result = query<RssData>().query("originPage == $0", rssId).find()
if(result.size > 0) {
result.forEach { it.vote = true }
if (!loadWithIntent) {
Blog.LOGE("Arrow Center Click")
WorkersDb.getRealm().apply {
writeBlocking {
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)
if (currentIdx < rssList.size - 1) {
currentIdx += 1
@@ -198,31 +243,21 @@ class RssViewerActivity : AwesomeWebViewActivity(), View.OnGenericMotionListene
}
registCancelSearch()
}
fun hideRss() {
Blog.LOGE("make no show")
WorkersDb.getRealm().apply {
writeBlocking {
val result =
query<RssData>().query("originPage == $0", rssId)
.find()
if (result.size > 0) {
result.forEach { it.read += 5 }
if (!loadWithIntent) {
Blog.LOGE("make no show")
WorkersDb.getRealm().apply {
writeBlocking {
val result =
query<RssData>().query("originPage == $0", rssId)
.find()
if (result.size > 0) {
result.forEach { it.read += 5 }
}
}
}
}
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()
doNextPage()
} else {
finish()
}
@@ -265,13 +300,23 @@ class RssViewerActivity : AwesomeWebViewActivity(), View.OnGenericMotionListene
override var pdfListner : PDFPrint.OnPDFPrintListener? = object : PDFPrint.OnPDFPrintListener {
override fun onSuccess(file: File?) {
LogUtil.e("file.absolutePath >>> ${file?.absolutePath}")
Toast.makeText(
applicationContext,
resources.getString(
stringResPhotoSavedTo
) + file?.absolutePath,
Toast.LENGTH_LONG
).apply {
}.show()
}
override fun onError(exception: Exception?) {
}
}
// val tika: Tika = Tika()
override fun dispatchGenericMotionEvent(ev: MotionEvent?): Boolean {
if (ev?.device?.name?.contains("BLE-M3") == true) {
Blog.LOGE("keyEvent >>>>> dispatchGenericMotionEvent ${ev}")
@@ -344,6 +389,7 @@ class RssViewerActivity : AwesomeWebViewActivity(), View.OnGenericMotionListene
registCancelSearch()
}
protected fun load(newUrl: String) {
LogUtil.e("newUrl >>> ${newUrl}")
newUrl.toUri().host?.let {
val splits = it.replace("http://","").replace("https://","").split(".")
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 current = webView!!.url
val newPath = hashString(current!!, "MD5").toHex()
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 {
LogUtil.e("try Jsoup.parse ${it}")
Jsoup.parse(URL(it), 3000)?.let {
LogUtil.e("try Jsoup.parse ${url}")
Jsoup.parse(URL(url), defaultTime.times(4).toInt()).let {
try {
LogUtil.e("onit Jsoup.parse ${it.title()}")
it.getElementsByTag("video")?.forEach {
it.attribute("src").value?.let {
var url = if (it.startsWith("http")) {
it
} else {
"https:".plus(it)
if (it.select("svg").size > 0) {
try {
downPic(url, agent, current!!, cookie!!, lDownFinishListener)
reqCount += 1
} catch (e : Exception) {
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 : Exception){}
} catch (e: UnsupportedMimeTypeException) {
LogUtil.e("e.message3 ${e.message}")
} 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() {
double = false
if(hasYoutubePlayer) {
@@ -473,14 +770,35 @@ class RssViewerActivity : AwesomeWebViewActivity(), View.OnGenericMotionListene
}
}
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){
webView?.evaluateJavascript("try{document.querySelector('#div_adnmore_area').hidden = true;}catch(e){}",null)
} else if(webView?.url?.contains("fmkorea") == true) {
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)
if (loadWithIntent) {
} else {
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)
}
}
}
}