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
versionCode = 38
versionName = "2.8.2"
multiDexEnabled = true
}
buildTypes {
@ -70,6 +71,9 @@ android {
sourceCompatibility = 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 {
jvmTarget = "1.8"
@ -105,6 +109,8 @@ dependencies {
implementation("com.github.delight-im:Android-AdvancedWebView:v3.2.1")
implementation(project(":library"))
implementation(project(":utils"))
// implementation ("org.apache.tika:tika-parsers:1.12")
implementation ("androidx.media:media:1.7.0")
// implementation ("me.everything:providers-android:1.0.1")
// implementation ("me.everything:providers-core:1.0.1")

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

View File

@ -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)
}
}
}
}

View File

@ -41,6 +41,7 @@ dependencies {
implementation ("androidx.annotation:annotation:1.9.1")
implementation ("androidx.appcompat:appcompat:1.7.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 ("androidx.core:core-ktx:1.15.0")
implementation(project(":utils"))

View File

@ -99,6 +99,7 @@ import kotlin.math.abs
open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
Handler.Callback {
var loadWithIntent : Boolean = false
var mediaUrls = arrayListOf<String>()
protected var key: Int = 0
@ -258,7 +259,7 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
// protected var back: AppCompatImageButton? = null
// protected var forward: AppCompatImageButton? = null
// protected var more: AppCompatImageButton? = null
protected var webView: WebView? = null
protected var webView: VideoEnabledWebView? = null
protected var webChromeClient: WebChromeClient? = null
protected var webViewClient: WebViewClient? = null
// protected var gradient: View? = null
@ -577,7 +578,7 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
fun fast() {
chechHandler.removeCallbacks(cancelSearch)
chechHandler.postDelayed(cancelSearch, 6000L)
chechHandler.postDelayed(cancelSearch, 90000L)
}
fun registCancelSearch() {
@ -799,12 +800,14 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
LogUtil.e("hitTestResult.type >>> ${hitTestResult.type}")
LogUtil.e("hitTestResult.extra >>> ${hitTestResult.extra}")
LogUtil.e("hitTestResult >>> ${mediaUrls.size}")
if(hitTestResult.type==0) {
webView?.evaluateJavascript("document.getElementsByTagName('iframe')",
webView?.evaluateJavascript("document.documentElement.outerHTML",
object : ValueCallback<String> {
override fun onReceiveValue(value: String?) {
val html = value?.replace("\\u003C", "<")
onHtml(html)
onHtml(html, true)
}
})
}
@ -839,7 +842,7 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
webView!!.url,
cookie,
object : DownFinishListener {
override fun onDownFinish(path: String) {
override fun onDownFinish(url : String ,path: String) {
if (showToastPhotoSavedOrFailed) {
Toast.makeText(
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)
}
@ -1874,10 +1885,10 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
view: WebView,
request: WebResourceRequest
): WebResourceResponse? {
var skipResource =
(host != null) && ((request.url?.host?.contains(host!!) ?: true) == false)
if (skipResource && request.url.toString().contains("gif")) {
LogUtil.e("shouldInterceptRequest request block gif resource >>> ${request.url.toString()}")
return WebResourceResponse(
"text/plain", "utf-8",
ByteArrayInputStream("".toByteArray())
@ -1885,13 +1896,27 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
}
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) {
hasYoutubePlayer = url.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)
.contains("ads".toLowerCase(Locale.ROOT))) {
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
return if(adblock) {
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()))
} catch (e : Exception) {
super.shouldInterceptRequest(view, url)
@ -1909,7 +1934,7 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
try {
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 {
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) {
super.shouldInterceptRequest(view, url)
@ -1918,7 +1943,7 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
try {
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 {
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) {
super.shouldInterceptRequest(view, url)
@ -1950,6 +1975,7 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
// }
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
mediaUrls.clear()
BroadCastManager.onPageStarted(this@AwesomeWebViewActivity, key, url)
if (!url.contains("docs.google.com") && url.endsWith(".pdf")) {
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)
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()
if (view.canGoBack() || view.canGoForward()) {
@ -2006,7 +2034,7 @@ open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener,
handler.sendEmptyMessage(MSG_CLICK_ON_URL)
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 ")
val alertDialog = AlertDialog.Builder(view.context).create()
alertDialog.setCancelable(false)

View File

@ -4,9 +4,14 @@ package kr.lunaticbum.awesomewebview.helpers;
* 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.Environment;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
@ -19,6 +24,11 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
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);
} else {
File file = new File(path);
if (!file.exists()) {
file.mkdirs();
}
loadPic(path, url, userAgent, referer, cookie, downFinishListener, false);
}
}
@ -59,24 +73,53 @@ public class DownPicUtil {
}
loadPic(file.getPath(), url, userAgent, referer, cookie, downFinishListener, true);
} else {
File file = new File(path);
if (!file.exists()) {
file.mkdirs();
}
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) {
new AsyncTask<Void,Void,String>(){
String fileName;
InputStream is;
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
protected String doInBackground(Void... voids) {
// 原文件名
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());
@ -90,7 +133,13 @@ public class DownPicUtil {
byte[] image = base64ImgHelper.decode();
is = new ByteArrayInputStream(image); //处理服务器的响应结果
} 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.setConnectTimeout(10000); //设置连接超时时间
@ -99,13 +148,26 @@ public class DownPicUtil {
httpURLConnection.setDoOutput(false); //Get请求不需要DoOutPut
httpURLConnection.setRequestMethod("GET"); //设置以Get方式请求数据
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");
if (!TextUtils.isEmpty(userAgent)) {
httpURLConnection.setRequestProperty("User-Agent", userAgent);
}
if (!TextUtils.isEmpty(referer)) {
httpURLConnection.setRequestProperty("Referer", referer);
println("referer>>>> " + referer);
}
if (!TextUtils.isEmpty(cookie)) {
httpURLConnection.setRequestProperty("Cookie", cookie);
@ -148,9 +210,7 @@ public class DownPicUtil {
// 提取文件格式真实拓展名
String extension = FormatHelper.getExtension(picFile);
if (isMp4) {
extension = "mp4";
}
// 提取不包含拓展名的原文件名
String[] extensions = fileName.split("\\.");
int splitLength = extensions.length;
@ -160,6 +220,9 @@ public class DownPicUtil {
} else {
newFileNameNoExtension = fileName;
}
if (isVideoFileByMetadata(picFile) && extension == null && isMp4) {
extension = "mp4";
}
// 重命名文件
if (extension == null) {
@ -171,21 +234,24 @@ public class DownPicUtil {
// 无拓展名整个文件名递增重命名
return renamePic(picFile, filePath, newFileNameNoExtension, null, MODE.MODE_INCREMENT);
}
} else {
return renamePic(picFile, filePath, newFileNameNoExtension, extension, MODE.MODE_IGNORE);
}
// 支持解析的格式使用md5文件名真实拓展名
String md5 = Md5Helper.getFileMD5ToString(picFile);
if (TextUtils.isEmpty(md5)) {
return renamePic(picFile, filePath, newFileNameNoExtension, extension, MODE.MODE_INCREMENT);
} else {
return renamePic(picFile, filePath, md5.substring(0, 16), extension, MODE.MODE_IGNORE);
}
// return picFile.getPath();
// String md5 = Md5Helper.getFileMD5ToString(picFile);
// if (TextUtils.isEmpty(md5)) {
// return renamePic(picFile, filePath, newFileNameNoExtension, extension, MODE.MODE_INCREMENT);
// } else {
// return renamePic(picFile, filePath, md5.substring(0, 16), extension, MODE.MODE_IGNORE);
// }
}
@Override
protected void onPostExecute(String path) {
super.onPostExecute(path);
if(path!=null){
downFinishListener.onDownFinish(path);
downFinishListener.onDownFinish(url,path);
} else {
downFinishListener.onError();
}
@ -208,6 +274,26 @@ public class DownPicUtil {
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) {
String extensionWithPoint = TextUtils.isEmpty(extension)? "": "." + extension;
String newFileName = newFileNameNoExtension + extensionWithPoint;
@ -262,7 +348,7 @@ public class DownPicUtil {
//下载完成回调的接口
public interface DownFinishListener{
void onDownFinish(String path);
void onDownFinish(String srcUrl ,String path);
void onError();
}
}

View File

@ -6,6 +6,8 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import kr.lunaticbum.utils.log.LogUtil;
public class FormatHelper {
private FormatHelper() {
@ -13,6 +15,7 @@ public class FormatHelper {
public static String getExtension(File file) {
try {
// long fileLength = file.length();
FileInputStream fileInputStream = new FileInputStream(file);
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
@ -81,7 +84,34 @@ public class FormatHelper {
start[5] == (byte) 0x61) {
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();
fileInputStream.close();
} catch (FileNotFoundException e) {

View File

@ -97,5 +97,54 @@ public class VideoEnabledWebView extends WebView
videoJsHelper.addJavascriptInterface(this);
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" /> -->
</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>
</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);
}
}