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
@@ -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)
@@ -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();
}
}
@@ -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) {
@@ -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);
}
}
@@ -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>