(для ясности :) Я использую WebView . Идея состоит в том, что пользователи могут посещать любую страницу и скачивать любой файл. Идея состоит в том, чтобы поддерживать как можно больше базовых c функций, чтобы приблизиться к встроенному веб-браузеру.
Как видно из названия, я пытаюсь заставить мое приложение загружать файлы и открывать их после загрузки . Есть несколько пунктов, о которых я хочу упомянуть:
- пользователь должен иметь возможность загружать любой тип файла (я предполагаю, что он называется mimeType), как это было бы по умолчанию в inte rnet браузерное приложение
- , поскольку пользователи загружают элементы из веб-просмотра, было бы разумно, если бы загрузки появлялись в папке загрузок
- было бы замечательно, если бы каждое пользовательское устройство могло решить, какое приложение использовать для открытия загруженного файла, но, например, мой samsung s9 спрашивает, какое приложение использовать, это тоже неплохой вариант
- я столкнулся с некоторыми проблемами, такими как успешная загрузка, но файл был пуст / поврежден, загруженный файл отсутствовал в файловом браузере, даже если приложение сообщало мне, что загрузка завершена
Так что я был на не 2-й, не 3-й, а даже 5-й странице результатов поиска Google Итак, я много видел, это пара версий моего кода, каждая из которых не работает так, как я ожидал во сне.
Просто чтобы прояснить, код, который вы собираетесь увидеть, находится под webview setDownloadListener (new DownloadListener () ...
Итак, моя последняя версия, без открытия файла после загрузки:
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) {
//used to call a class to in previous code version to make some downloads
//new DownloadTask(webview_base.this, url, mimeType);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)).setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI |
DownloadManager.Request.NETWORK_MOBILE);
// in order for this if to run, you must use the android 3.2 to compile your app
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
}
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, url.substring(url.lastIndexOf('/')));
// get download service and enqueue file
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
}
});
проблема с ^ этой версией заключалась в том, что я мог загружать файлы, но он загружал страницы HTML вместо реального pdf или чего-то, к чему я стремился, это странно.
предыдущая версия было немного плавнее, я смог загрузить файлы, и я использовал диалоговое окно, чтобы предложить открыть файл. Все работало нормально, но документы были пустыми / повреждены, и мне предложили открыть их с помощью странных приложений, например, как только я Меня попросили открыть pdf с моим приложением smartwatch, но это, должно быть, прошло, так как я редактировал код время от времени. Проблема, с которой я столкнулся, заключалась в том, что даже после того, как несколько ctrl + zi не смогли вернуть мое приложение на ноги - я был застрял на экране загрузки и logcat кричал мне, что это не может штрафовать каталог загрузки. В этот момент я разозлился и запустил версию, которую вы можете смотри выше. В любом случае, вот весь класс:
package com.example.viaapp_v2;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.Handler;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.widget.Toast;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import androidx.core.content.FileProvider;
public class DownloadTask extends FileProvider {
private static final String TAG = "Download Task";
private Context context;
String mimeType;
private String downloadUrl = "", downloadFileName = "";
private ProgressDialog progressDialog;
public DownloadTask() {
//required empty constructor
}
public DownloadTask(Context context, String downloadUrl, String mime) {
this.context = context;
this.downloadUrl = downloadUrl;
this.mimeType = mime;
//Create file name by picking download file name from URL
downloadFileName = downloadUrl.substring(downloadUrl.lastIndexOf('/'));
//Start Downloading Task
new DownloadingTask().execute();
}
private class DownloadingTask extends AsyncTask<Void, Void, Void> {
File apkStorage = null;
File outputFile = null;
@Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog = new ProgressDialog(context);
progressDialog.setMessage("Downloading...");
progressDialog.setCancelable(false);
progressDialog.show();
}
@Override
protected void onPostExecute(Void result) {
try {
if (outputFile != null) {
progressDialog.dismiss();
final ContextThemeWrapper ctw = new ContextThemeWrapper( context, R.style.Theme_AppCompat_Light_Dialog);
final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ctw);
alertDialogBuilder.setTitle("Document ");
alertDialogBuilder.setMessage(downloadFileName+" Downloaded Successfully ");
alertDialogBuilder.setCancelable(false);
alertDialogBuilder.setPositiveButton("ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
}
});
alertDialogBuilder.setNegativeButton("Open file",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
try {
File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString()
+ downloadFileName);//name here is the name of any string you want to pass to the method
if (!file.isDirectory())
file.mkdir();
Intent testIntent = new Intent();
testIntent.setType(mimeType);
testIntent.setAction(Intent.ACTION_VIEW);
Uri uri = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", file);
testIntent.setDataAndType(uri, mimeType);
ctw.startActivity(testIntent);
} catch (Exception e) {
e.printStackTrace();
}
}
});
alertDialogBuilder.show();
} else {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
}
}, 3000);
Log.e(TAG, "Download Failed");
}
} catch (Exception e) {
e.printStackTrace();
//Change button text if exception occurs
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
}
}, 3000);
Log.e(TAG, "Download Failed with Exception - " + e.getLocalizedMessage());
}
super.onPostExecute(result);
}
@Override
protected Void doInBackground(Void... arg0) {
try {
URL url = new URL(downloadUrl);//Create Download URl
HttpURLConnection c = (HttpURLConnection) url.openConnection();//Open Url Connection
c.setRequestMethod("GET");//Set Request Method to "GET" since we are grtting data
c.connect();//connect the URL Connection
//If Connection response is not OK then show Logs
if (c.getResponseCode() != HttpURLConnection.HTTP_OK) {
Log.e(TAG, "Server returned HTTP " + c.getResponseCode()
+ " " + c.getResponseMessage());
}
//Get File if SD card is present
if (new CheckForSDCard().isSDCardPresent()) {
apkStorage = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString());
} else
Toast.makeText(context, "Oops!! There is no SD Card.", Toast.LENGTH_SHORT).show();
//If File is not present create directory
if (!apkStorage.exists()) {
apkStorage.mkdir();
Log.e(TAG, "Directory Created.");
}
outputFile = new File(apkStorage, downloadFileName);//Create Output file in Main File
//Create New File if not present
if (!outputFile.exists()) {
outputFile.createNewFile();
Log.e(TAG, "File Created");
}
FileOutputStream fos = new FileOutputStream(outputFile);//Get OutputStream for NewFile Location
InputStream is = c.getInputStream();//Get InputStream for connection
byte[] buffer = new byte[1024];//Set buffer type
int len1 = 0;//init length
while ((len1 = is.read(buffer)) != -1) {
fos.write(buffer, 0, len1);//Write new file
}
//Close all connection after doing task
fos.close();
is.close();
} catch (Exception e) {
//Read exception if something went wrong
e.printStackTrace();
outputFile = null;
Log.e(TAG, "Download Error Exception " + e.getMessage());
}
return null;
}
}
private class CheckForSDCard {
//Check If SD Card is present or not method
boolean isSDCardPresent() {
return Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED);
}
}
}
Ищете мой файл манифеста? Ну вот и все .. я добавил некоторую поддержку для загрузки файлов, загрузки файлов, inte rnet acces, ect
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-feature android:name="android.hardware.camera"/>
<uses-permission android:name="android.permission.CAMERA"/>
Я даже пытался добавить некоторые вещи провайдера в приложение
<provider
android:name=".DownloadTask"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
Но глубоко внутри я понятия не имел, что поместить в файл пути провайдера.
<paths><external-path name="external_files" path="."/></paths>
Извините, этот текст такой длинный. Я просто в отчаянии и почти закончил с моим проектом, но мне нужно искать помощь где-то, чтобы продолжать идти. Я надеюсь, что я достаточно объяснил мою проблему и предоставил достаточно кода, был бы рад узнать, какой код лучше / более возможен (создайте класс или запишите все прямо под слушателем).
--- EDIT ---
Итак ... я попытался запустить мою старую версию кода, которая является длинным блоком кода выше, файлом класса "DownloadTask. java", который вызывается конструктором, передавая utl, mimetype и context , Вы можете увидеть строку, прокомментированную в первом блоке кода в этом посте, 3-я строка. Вот что произошло: я зашел на страницу, нажал на ссылку, чтобы скачать pdf файл, но затем застрял в диалоговом окне с надписью «загрузка ...», в то время как logcat сказал следующее:
D/ViewRootImpl@19478bd[webview_base]: MSG_RESIZED: frame=Rect(18, 617 - 701, 821) ci=Rect(0, 0 - 0, 0) vi=Rect(0, 0 - 0, 0) or=1
D/ViewRootImpl@c47c382[webview_base]: Relayout returned: old=[0,0][720,1480] new=[0,0][720,1480] result=0x1 surface={valid=true 484837122048} changed=false
I/ample.viaapp_v: Compiler allocated 4MB to compile void android.widget.TextView.<init>(android.content.Context, android.util.AttributeSet, int, int)
I/System.out: (HTTPLog)-Static: isSBSettingEnabled false
I/System.out: (HTTPLog)-Static: isSBSettingEnabled false
W/System.err: java.io.FileNotFoundException: /storage/emulated/0/Download/Datu%20p%C4%81rraides%20t%C4%ABkli-III_LV.pdf (Is a directory)
at java.io.FileOutputStream.open0(Native Method)
at java.io.FileOutputStream.open(FileOutputStream.java:308)
at java.io.FileOutputStream.<init>(FileOutputStream.java:238)
at java.io.FileOutputStream.<init>(FileOutputStream.java:180)
at com.example.viaapp_v2.DownloadTask$DownloadingTask.doInBackground(DownloadTask.java:173)
at com.example.viaapp_v2.DownloadTask$DownloadingTask.doInBackground(DownloadTask.java:54)
W/System.err: at android.os.AsyncTask$2.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:764)
E/Download Task: Download Error Exception /storage/emulated/0/Download/Datu%20p%C4%81rraides%20t%C4%ABkli-III_LV.pdf (Is a directory)
E/Download Task: Download Failed
D/ViewRootImpl@19478bd[webview_base]: MSG_WINDOW_FOCUS_CHANGED 1 1
И да, Как вы можете видеть, имя файла содержит буквы с акцентом, поэтому его странно, 99% пользователей, которые хотят использовать приложение, будут работать с контентом, который использует буквы с акцентом, так что мне интересно, может, я должен заменить имена файлов без всех этих букв как "āēīģčķ" в "aeigck".