preface

There are a lot of webViews in the app I wrote, and many new functions have been added during the loading process, such as

When loading H5 link encounter interesting picture want to save long press whether can

Answer: "Definitely yes"

There are many examples on the Internet, but many of them failed. I don’t know whether I didn’t give the right permission or the writing method is wrong. In short, I can’t save the picture to the local

Implementation effect

Train of thought

The basic idea is to hold down the url of the image and then download the URL to the local through the IO stream

Code implementation

It’s easy to solve problems when you have ideas

First listen for webView long press events

/ / WebView. Long press surveillance mWebView setOnLongClickListener (v - > {final WebView. HitTestResult HitTestResult = mWebView.getHitTestResult(); / / if the image type or with the type of image links if (hitTestResult. GetType () = = WebView. HitTestResult. IMAGE_TYPE | | hitTestResult. GetType () = = WebView. HitTestResult. SRC_IMAGE_ANCHOR_TYPE) {/ / new AlertDialog pop-up store image dialog box. The Builder (getActivity ()). The setItems (new String[]{" save image "}, (dialog, which) -> {String PIC = hitTestresult.getextra (); Log. E (TAG, "TAG:" + PIC); MyDownloadManager myDownloadManager = new MyDownloadManager(); switch (which) { case 0: myDownloadManager.addDownloadTask(pic); Toastutils.showshort (getActivity(), "Saved successfully "); break; } }) .show(); return true; } return false; // Hold long press to copy text});Copy the code

WebViewLong time UI renderings display

Where hitTestResult is to view the image type and the image link

Take a look at the image address type obtained

Here is the long press save trigger below and then click events suggests a saved state Real have already begun to download myDownloadManager. AddDownloadTask (PIC) is encapsulated to download class

switch (which) { case 0: myDownloadManager.addDownloadTask(pic); Toastutils.showshort (getActivity(), "Saved successfully "); break; }Copy the code

MyDownloadManager wrapper class

The MyDownloadManager wrapper class enables the thread pool queue to download saved images

public class MyDownloadManager { public static final int EXTERNAL_STORAGE_REQ_CODE = 10; private static final int REQUEST_EXTERNAL_STORAGE = 1; private static String[] PERMISSIONS_STORAGE = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}; /** * Checks if the app has permission to write to device storage * <p> * If the app does not has permission then the user will be prompted to * grant permissions * * @param activity */ public static void verifyStoragePermissions(Activity  activity) { // Check if we have write permission int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE); if (permission ! = PackageManager.PERMISSION_GRANTED) { // We don't have permission so prompt the user ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE); } } private static final String TAG = "MyDownloadManager"; private File downloadDir; // File saving path private static MyDownloadManager instance; // singleton // single thread task queue public static Executor Executor; private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) { return new Thread(r, "MyDownloadManager #" + mCount.getAndIncrement()); }}; Private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<>(128); public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); Public MyDownloadManager() {// Initializes the download path downloadDir = new File("/sdcard/"); if (! downloadDir.exists()) { downloadDir.mkdirs(); } executor = new SerialExecutor(); */ private static class SerialExecutor implements Executor {final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); Runnable mActive; public synchronized void execute(final Runnable r) { mTasks.offer(() -> { try { r.run(); } finally { scheduleNext(); }}); if (mActive == null) { scheduleNext(); } } protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) ! = null) { THREAD_POOL_EXECUTOR.execute(mActive); Public static MyDownloadManager getInstance() {if (instance == null) {instance = new  MyDownloadManager(); } return instance; } /** * @param path */ public void addDownloadTask(final String path) {log. e(TAG, "enter run:" + path); Executor.execute (new Runnable() {@override public void run() {log.e (TAG, "run"); download(path); }}); } /** * download file ** @param path */ private void download(String path) {String fileName = MD5(path); File savePath = new File(downloadDir, fileName); //File finallyPath = new File(downloadDir, fileName + ".png"); If (savepath.exists ()) {log.e (TAG, "file is existed"); return; } // If (savePath.exists() && savePath.delete()) {// If a file exists before, the download is not complete. SavePath = new File(downloadDir, fileName); Log.e(TAG, "download 111111"); } Log.e(TAG, "download start"); try { byte[] bs = new byte[1024]; int len; URL url = new URL(path); InputStream is = url.openStream(); OutputStream os = new FileOutputStream(savePath, true); while ((len = is.read(bs)) ! = -1) { os.write(bs, 0, len); } is.close(); os.close(); } catch (IOException e) { e.printStackTrace(); }} public void addDeleteTask(final String path) {executor.execute(new Runnable() { @Override public void run() { delete(path); }}); } /** * delete local file ** @param path */ private void delete(String path) {String fileName = MD5(path); File savePath = new File(downloadDir, fileName + ".png"); Log.i(TAG, savePath.getPath()); if (savePath.exists()) { if (savePath.delete()) { Log.i(TAG, "file is deleted"); }} /** * public File getDownloadDir() {return downloadDir; } public String getfileName(String path) { String f = MD5(path); return f; } public static String MD5(String path) { // URL url = new URL(path); //String url = path; //String path = Environment.getExternalStorageDirectory().getAbsolutePath(); //final long startTime = System.currentTimeMillis(); //String filename=url.substring(url.lastIndexOf("/") + 1); SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss"); String time = df.format(new Date()); String filename = time + ".jpg"; return filename; }}Copy the code

Permission to apply for

Dynamic application / / read/write permission to SD card int permission. = ActivityCompat checkSelfPermission (getActivity (), Manifest.permission.WRITE_EXTERNAL_STORAGE); if (permission ! = PackageManager. PERMISSION_GRANTED) {/ / request permissions ActivityCompat. RequestPermissions (getActivity (), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, EXTERNAL_STORAGE_REQ_CODE); }Copy the code

If it is android10 androidQ version Remember to add more in the manifest a android: requestLegacyExternalStorage = “true”

AndroidQ refers to another blog post

Open failed: EACCES (Permission denied

2021 continue to come on