From 1169b159e81d31929c6bd00756e93cd6983e7c7e Mon Sep 17 00:00:00 2001 From: lunaticbum Date: Fri, 20 Dec 2024 18:06:54 +0900 Subject: [PATCH] ... --- app/build.gradle.kts | 6 + app/src/main/AndroidManifest.xml | 17 +- .../launcher/home/RssViewerActivity.kt | 528 ++++++++++++++---- library/build.gradle.kts | 1 + .../awesomewebview/AwesomeWebViewActivity.kt | 60 +- .../awesomewebview/helpers/DownPicUtil.java | 114 +++- .../awesomewebview/helpers/FormatHelper.java | 30 + .../views/VideoEnabledWebView.java | 49 ++ .../src/main/res/layout/awesome_web_view.xml | 25 + .../utils/ApplicationTest.java | 13 - .../utils/etc/PreferencesUtilTest.java | 278 --------- .../helpers/ExampleUnitTest.java | 15 - 12 files changed, 692 insertions(+), 444 deletions(-) delete mode 100644 utils/src/androidTest/java/com/thefinestartist/utils/ApplicationTest.java delete mode 100755 utils/src/androidTest/java/com/thefinestartist/utils/etc/PreferencesUtilTest.java delete mode 100644 utils/src/test/java/com/thefinestartist/helpers/ExampleUnitTest.java diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6090c84..f97b914 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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") diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c68d50b..ded19f3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -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 @@ - + + + + + + + + + + + = 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().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().query("originPage == $0", rssId).find() + if (result.size > 0) { + result.forEach { it.vote = true } + } } } + webView?.evaluateJavascript("document.documentElement.outerHTML", + object : ValueCallback { + 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().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().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() + 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() + 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("&", "&") + 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 { + 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("//","url("https://") + if (host?.contains("clien") == true) { + result = result.replace("href=\"/service","href=\"https://m.clien.net/service").replace("href=\"\\"/service","href=\"\\"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() - + // + 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) + } } } } \ No newline at end of file diff --git a/library/build.gradle.kts b/library/build.gradle.kts index 892b769..73490f7 100644 --- a/library/build.gradle.kts +++ b/library/build.gradle.kts @@ -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")) diff --git a/library/src/main/java/kr/lunaticbum/awesomewebview/AwesomeWebViewActivity.kt b/library/src/main/java/kr/lunaticbum/awesomewebview/AwesomeWebViewActivity.kt index 03412f9..050f327 100644 --- a/library/src/main/java/kr/lunaticbum/awesomewebview/AwesomeWebViewActivity.kt +++ b/library/src/main/java/kr/lunaticbum/awesomewebview/AwesomeWebViewActivity.kt @@ -99,6 +99,7 @@ import kotlin.math.abs open class AwesomeWebViewActivity : AppCompatActivity(), View.OnClickListener, Handler.Callback { + var loadWithIntent : Boolean = false var mediaUrls = arrayListOf() 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 { 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) diff --git a/library/src/main/java/kr/lunaticbum/awesomewebview/helpers/DownPicUtil.java b/library/src/main/java/kr/lunaticbum/awesomewebview/helpers/DownPicUtil.java index f1ad551..e4f84d4 100644 --- a/library/src/main/java/kr/lunaticbum/awesomewebview/helpers/DownPicUtil.java +++ b/library/src/main/java/kr/lunaticbum/awesomewebview/helpers/DownPicUtil.java @@ -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(){ 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(); } } \ No newline at end of file diff --git a/library/src/main/java/kr/lunaticbum/awesomewebview/helpers/FormatHelper.java b/library/src/main/java/kr/lunaticbum/awesomewebview/helpers/FormatHelper.java index 58d166a..d3b317b 100644 --- a/library/src/main/java/kr/lunaticbum/awesomewebview/helpers/FormatHelper.java +++ b/library/src/main/java/kr/lunaticbum/awesomewebview/helpers/FormatHelper.java @@ -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("") || + 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) { diff --git a/library/src/main/java/kr/lunaticbum/awesomewebview/views/VideoEnabledWebView.java b/library/src/main/java/kr/lunaticbum/awesomewebview/views/VideoEnabledWebView.java index eb3e4ff..f7b665e 100644 --- a/library/src/main/java/kr/lunaticbum/awesomewebview/views/VideoEnabledWebView.java +++ b/library/src/main/java/kr/lunaticbum/awesomewebview/views/VideoEnabledWebView.java @@ -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); + + } } diff --git a/library/src/main/res/layout/awesome_web_view.xml b/library/src/main/res/layout/awesome_web_view.xml index 1c96e60..6a0d9d1 100644 --- a/library/src/main/res/layout/awesome_web_view.xml +++ b/library/src/main/res/layout/awesome_web_view.xml @@ -82,6 +82,31 @@ android:visibility="invisible" /> --> + + + + \ No newline at end of file diff --git a/utils/src/androidTest/java/com/thefinestartist/utils/ApplicationTest.java b/utils/src/androidTest/java/com/thefinestartist/utils/ApplicationTest.java deleted file mode 100644 index 89235ec..0000000 --- a/utils/src/androidTest/java/com/thefinestartist/utils/ApplicationTest.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.thefinestartist.utils; - -import android.app.Application; -import android.test.ApplicationTestCase; - -/** - * Testing Fundamentals - */ -public class ApplicationTest extends ApplicationTestCase { - public ApplicationTest() { - super(Application.class); - } -} \ No newline at end of file diff --git a/utils/src/androidTest/java/com/thefinestartist/utils/etc/PreferencesUtilTest.java b/utils/src/androidTest/java/com/thefinestartist/utils/etc/PreferencesUtilTest.java deleted file mode 100755 index 959a050..0000000 --- a/utils/src/androidTest/java/com/thefinestartist/utils/etc/PreferencesUtilTest.java +++ /dev/null @@ -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 expected = new HashSet<>(); - expected.add("Lorem ipsum"); - expected.add("dolor sit amet"); - expected.add("consectetur adipiscing elit"); - final Set defValue = null; - - PreferencesUtil.put(key, expected); - Set actual = PreferencesUtil.get(key, defValue); - assertEquals(expected, actual); - } - - @SmallTest - public void testStoreStringSetNamed() { - final String name = "TEST_NAMED"; - final String key = "TEST_STRINGSET"; - final Set expected = new HashSet<>(); - expected.add("Lorem ipsum"); - expected.add("dolor sit amet"); - expected.add("consectetur adipiscing elit"); - final Set defValue = null; - - PreferencesUtil.put(name, key, expected); - Set actual = PreferencesUtil.get(name, key, defValue); - assertEquals(expected, actual); - } - - @MediumTest - public void testStoreSerializable() { - final String key = "TEST_SERIALIZABLE"; - final ArrayList expected = new ArrayList<>(); - expected.add("Lorem ipsum"); - expected.add("dolor sit amet"); - expected.add("consectetur adipiscing elit"); - final ArrayList defValue = new ArrayList<>(); - defValue.add("Proin mollis dictum"); - - PreferencesUtil.put(key, expected); - ArrayList actual = PreferencesUtil.get(key, defValue); - assertEquals(expected, actual); - } - - @MediumTest - public void testStoreSerializableNamed() { - final String name = "TEST_NAMED"; - final String key = "TEST_SERIALIZABLE"; - final ArrayList expected = new ArrayList<>(); - expected.add("Lorem ipsum"); - expected.add("dolor sit amet"); - expected.add("consectetur adipiscing elit"); - final ArrayList defValue = new ArrayList<>(); - defValue.add("Proin mollis dictum"); - - PreferencesUtil.put(name, key, expected); - ArrayList 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); - } - } - -} diff --git a/utils/src/test/java/com/thefinestartist/helpers/ExampleUnitTest.java b/utils/src/test/java/com/thefinestartist/helpers/ExampleUnitTest.java deleted file mode 100644 index 99a0022..0000000 --- a/utils/src/test/java/com/thefinestartist/helpers/ExampleUnitTest.java +++ /dev/null @@ -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); - } -} \ No newline at end of file