I managed to cache audio files in the local storage in one of my apps which uses a WebView. This means that the audio files are intercepted and then if not available in the cache they are downloaded and then served from the cache. The cache survives the phone being restarted on Android 10.
These are the settings I used for the WebView:
@SuppressLint("SetJavaScriptEnabled")
public static void setupWebview(WebView webView) {
WebSettings settings = webView.getSettings();
settings.setLoadWithOverviewMode(true);
settings.setUseWideViewPort(true);
settings.setAllowFileAccess(true);
settings.setAllowContentAccess(true);
settings.setAllowFileAccessFromFileURLs(true);
settings.setAllowUniversalAccessFromFileURLs(true);
settings.setBlockNetworkImage(false);
settings.setBlockNetworkLoads(false);
settings.setLoadsImagesAutomatically(true);
settings.setMediaPlaybackRequiresUserGesture(false);
settings.setDomStorageEnabled(true);
settings.setLoadWithOverviewMode(true);
settings.setJavaScriptEnabled(true);
settings.setAllowUniversalAccessFromFileURLs(true);
settings.setDatabaseEnabled(true);
settings.setAppCacheEnabled(true);
settings.setAppCachePath("/beezone");
}
And I have used a customized WebClient
for the same WebView
:
So basically the custom WebClient
looks like this:
mWebView.setWebViewClient(new WebViewClient() {
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Nullable
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
return cacheM4a(request, context);
}
@Nullable
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
return cacheM4a(Uri.parse(url), context);
}
});
The relevant code for method cacheM4a
looks like this:
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Nullable
public static WebResourceResponse cacheM4a(WebResourceRequest request, Context context) {
Uri source = request.getUrl();
return cacheM4a(source, context);
}
@Nullable
public static WebResourceResponse cacheM4a(Uri source, Context context) {
String sourceStr = source.toString();
if (sourceStr.endsWith(CacheHelper.MAIN_AUDIO_FORMAT)) {
String language = LanguageHelper.INSTANCE.getLanguage();
String absolutePath = context.getCacheDir().getAbsolutePath();
String langPath = String.format("%s/%s", absolutePath, language);
boolean folderExists = createLanguageCachePath(langPath);
if(!folderExists) {
try {
return new WebResourceResponse("audio/mp4", "binary", new URL(sourceStr).openStream());
} catch (IOException e) {
Log.e(TAG, "Failed to read directly from the web", e);
return null;
}
}
String targetPath = String.format("%s/%s", langPath, source.getLastPathSegment());
File file = new File(targetPath);
if (!file.exists()) {
if (saveToCache(sourceStr, targetPath)) return null;
}
// Always read from cache.
try {
FileInputStream fis = new FileInputStream(new File(targetPath));
WebResourceResponse response = new WebResourceResponse("audio/mp4", "binary", fis);
return response;
} catch (IOException e) {
Log.e(TAG, "Failed to read cache", e);
return null;
}
}
return null;
}
private static boolean saveToCache(String sourceStr, String targetPath) {
// File is not present in cache and thus needs to be downloaded
try (InputStream in = new URL(sourceStr).openStream();
FileOutputStream fos = new FileOutputStream(new File(targetPath))) {
ByteStreams.copy(in, fos);
} catch (IOException e) {
Log.e(TAG, "Failed to save in cache", e);
return true;
}
return false;
}
private static boolean createLanguageCachePath(String langPath) {
File langPathFile = new File(langPath);
if(!langPathFile.exists()) {
if(!langPathFile.mkdirs()) {
Log.e(TAG, String.format("Could not create %s", langPathFile));
return false;
};
}
return true;
}
These are the permissions I have used:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />