I'm working on the creation of an Android App to track financial expenses and I would like to allow the user to batch import financial transactions into my app. The transactions would be come as .csv/.txt files.
However, I'm getting a cryptic exception:
java.lang.AbstractMethodError: abstract method "android.content.IContentProvider android.content.ContentResolver.acquireUnstableProvider(android.content.Context, java.lang.String)"
at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:1780)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1394)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1247)
at android.content.ContentResolver.openInputStream(ContentResolver.java:967)
....
Workflow is as follow: User selects text file to import, App imports content.
Start the file selector:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.setType("*/*");
//intent.addCategory("CATEGORY_OPENABLE");
startActivityForResult(intent, REEQUEST_CODE_IMPORT);
Catch the result:
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Check which request we're responding to
if (requestCode == REEQUEST_CODE_IMPORT) {
// Make sure the request was successful
Uri path = data.getData();
if (resultCode == RESULT_OK && path != null) {
InputStream inputStream = null;
try {
ContentResolver contentResolver = new ContentResolver(getContext()) {};
// Error happens in the next line
inputStream = contentResolver.openInputStream(path);
// Get the object of DataInputStream
DataInputStream in = new DataInputStream(inputStream);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String line = "";
while((line = br.readLine()) != null) {
// Do something meaningful...
}
} catch () {
// Catch the exceptions ( I have removed some boiler plate code here...)
} finally {
// Close the path ( I have removed some boiler plate code here...)
inputStream.close();
}
}
}
}
Android documentation helped me solve the issue: Access documents and other files from shared storage - Input Stream
Following code adaptation is working:
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// Check which request we're responding to
if (requestCode == REEQUEST_CODE_IMPORT) {
// Make sure the request was successful
Uri path = data.getData();
if (resultCode == RESULT_OK && path != null) {
StringBuilder stringBuilder = new StringBuilder();
try (InputStream inputStream = getActivity().getContentResolver().openInputStream(path);
BufferedReader reader = new BufferedReader(
new InputStreamReader(Objects.requireNonNull(inputStream)))) {
String line;
while ((line = reader.readLine()) != null) {
Log.d(TAG, line);
stringBuilder.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
String content = stringBuilder.toString();
// Do something with it
}
}
}
Related
Since Android 10 (API 29) I need to use the Storage Access Framework's File Picker to select GPX (GPS) files to copy from the Downloads folder to my local app folder. I have implemented the file picker and am able to select the GPX file, however the result data URI appears different to the filename (but unique) and I cannot seem to use it to copy the files. The rest of the code is the same "copy" code I used in previous versions of Android. What am I doing wrong and how should I best use the SAF File Picker to copy files? I haven't been able to find a recent (API 29) "file copy" example on the net...
private static final int READ_REQUEST_CODE = 42;
...
public void performFileSearch() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
// intent.setType("application/gpx"); // Filters GPX file but wont let me select them.
intent.setType("*/*");
startActivityForResult(intent, READ_REQUEST_CODE);
}
...
if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
Uri uri = null;
if (data != null) {
uri = data.getData();
handleDownloadedGPXFiles2(uri);
}
}
...
private void handleDownloadedGPXFiles2(Uri selectedFileUri) {
File sourceFile = new File(selectedFileUri.getPath()); // Returns a unique number or string but NOT filename string???
File destDirectory = new File(this.getExternalFilesDir(null), "Imported");
File destFile = new File(destDirectory, "test.gpx"); // Needs to be same name as original filename.
try {
if (!destFile.exists()) {
destFile.createNewFile();
}
FileInputStream inStream = new FileInputStream(sourceFile);
FileOutputStream outStream = new FileOutputStream(destFile);
FileChannel inChannel = inStream.getChannel();
FileChannel outChannel = outStream.getChannel();
inChannel.transferTo(0, inChannel.size(), outChannel);
inStream.close();
outStream.close();
} catch (IOException e) {
e.printStackTrace();
}
Toast.makeText(getApplicationContext(), "File Import Complete", Toast.LENGTH_LONG).show();
}
File sourceFile = new File(selectedFileUri.getPath());
Remove above line.
FileInputStream inStream = new FileInputStream(sourceFile);
Replace that line by:
InputStream inStream = getContentResolver().openInputStream(selectedFileUri);
Further you can remove
if (!destFile.exists()) {
destFile.createNewFile();
}
as the file will be created by the new FileOutputStream();
Finally: Your last Toast() is on the wrong place. It should be in the try block.
Place a different Toast() in the catch block to inform yourself or the user.
Thanks blackapps. Final code works well...
private void handleDownloadedGPXFiles2(Uri selectedFileUri) {
String displayName = "imported.gpx";
String fileExtension;
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(selectedFileUri, null, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
if (displayName != null && displayName.length() >=4) {
fileExtension = displayName.substring(displayName.length() - 4);
if (!fileExtension.equals(".gpx")){
myCustomToast("Must be a .GPX file!");
return;
}
} else {
myCustomToast("Must be a .GPX file!");
return;
}
}
File destDirectory = new File(this.getExternalFilesDir(null), "Imported");
File destFile = new File(destDirectory, displayName);
FileOutputStream outStream = new FileOutputStream(destFile);
InputStream in = getContentResolver().openInputStream(selectedFileUri);
OutputStream out = outStream;
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
in.close();
out.flush();
out.close();
Toast.makeText(getApplicationContext(), "File Import Complete", Toast.LENGTH_LONG).show();
} catch (IOException e) {
Toast.makeText(getApplicationContext(), "File Import FAILED", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
finally
{
if (cursor != null)
cursor.close();
}
}
i'm wiritng an app with Android Studio, I've setted a file chooser that give me the path of a file (the file could be on external or internal storage). I need read the chosen file, modify it and write in the same position with another name. I've implemented read and write function but they don't work with separator because they use FileInputStream/FileOutputStream, i've tried already a function with FileReader but return null. Someone how resolve my problem?
Thanks in Advance!
My read/write function:
public String Read_file(String fn, Context context) {
int ch;
String d;
StringBuffer fileContent = new StringBuffer("");
FileInputStream fis;
try {
fis = context.openFileInput(fn);
try {
while( (ch = fis.read()) != -1)
fileContent.append((char)ch);
} catch (IOException e) {
e.printStackTrace();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("Error");
}
return d = new String(fileContent);
}
public void Write_file(String fn, String data, Context context){
FileOutputStream outS;
//Scrive sul file
try {
outS = context.openFileOutput(fn, Context.MODE_PRIVATE);
outS.write(data.getBytes());
outS.close();
} catch (Exception e) {
e.printStackTrace();
}
}
I've already tried this but return null:
public String t(String fname){
BufferedReader br = null;
String response = null;
try {
StringBuffer output = new StringBuffer();
//String fpath = "/sdcard/"+fname+".txt";
br = new BufferedReader(new FileReader(fname));
String line = "";
while ((line = br.readLine()) != null) {
output.append(line +"n");
}
response = output.toString();
} catch (IOException e) {
e.printStackTrace();
return null;
}
return response;
}
My choose file:
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
if(requestCode == 10){
if(resultCode == RESULT_OK){
Uri uri = data.getData();
String src = uri.getPath();
String file_name = uri.getLastPathSegment();
String fn = src.substring(src.lastIndexOf('/') + 1);
System.out.println(fn);
//////READ THE FILE AT THE URI
}
}
}
Had a similar issue tring to open the file using fileName.getPath()
Kept getting file not found and all the content:path information was lost.
looking into content providers highlighted FileDescriptors.
Opening a fileDescriptor resulted in a ParcelFileDescriptor in which you can get the fileDescriptor and then onto opening a FileReader or an FileInputStream depending on your use case.
fun readFile(fileName: Uri): String? {
val fileDescriptor = requireContext().contentResolver.openFileDescriptor(fileName, "r") ?: return null
val fReader = FileReader(fileDescriptor.fileDescriptor)
//val file = File(fileName.path)
var bufferedReader: BufferedReader? = null
try {
bufferedReader = BufferedReader(fReader)
val stringBuilder = StringBuilder()
var line: String?
while (bufferedReader.readLine().also { line = it } != null) {
stringBuilder.append(line)
stringBuilder.append(System.lineSeparator())
}
return stringBuilder.toString()
} catch (e: FileNotFoundException) {
System.err.println("File : Not found")
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
} finally {
try {
bufferedReader?.close()
fReader.close()
fileDescriptor.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
return null
}
I am trying to browse only two file-types: images or pdf.
Here is the source:
String[] permissions = new String[]{Manifest.permission.READ_EXTERNAL_STORAGE};
myPermissions =new MyPermissions(TestDialog.this, 0, permissions);
MyPermissions.EventHandler permHandler = new MyPermissions.EventHandler() {
#Override
public void handle() {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("application/pdf");
intent.setType("image/jpeg");
startActivityForResult(intent, 0);
}
};
myPermissions.doIfHasPermissions(permHandler);
Here is a my onActivityResult source:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
String url = data.getData().getPath();
File myFile = new File(url);
Log.e("base64 ", getStringFile(myFile));
}
super.onActivityResult(requestCode, resultCode, data);
}
public String getStringFile(File f) {
InputStream inputStream = null;
String encodedFile = "", lastVal;
try {
inputStream = new FileInputStream(f.getAbsolutePath());
byte[] buffer = new byte[10240];//specify the size to allow
int bytesRead;
ByteArrayOutputStream output = new ByteArrayOutputStream();
Base64OutputStream output64 = new Base64OutputStream(output, Base64.DEFAULT);
while ((bytesRead = inputStream.read(buffer)) != -1) {
output64.write(buffer, 0, bytesRead);
}
output64.close();
encodedFile = output.toString();
} catch (FileNotFoundException e1) {
e1.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
lastVal = encodedFile;
return lastVal;
}
I would like to convert the selected file to Base64, but I get a FileNotFoundException. Does anyone have any idea what am I doing wrong?
Have a look at
Uri uri = data.getData();
Then try to log the value of uri.toString().
You will see that it starts with "content//....".
Do not try to find a file.
Instead of a FileInputStream use an InputStream.
InputStream inputStream = getContentResolver().openInputStream(uri);
I try to browse only two type files,images or pdf
Your code has nothing much to do with files. It uses ACTION_GET_CONTENT, which allows the user to choose a piece of content.
String url = data.getData().getPath();
This line is useless, unless the Uri has a scheme of file. Most likely, it has a scheme of content.
Stop using File and FileInputStream. Instead, get an InputStream from a ContentResolver (from getContentResolver()) and its openInputStream() method. You can pass in the Uri, and you will get an InputStream regardless of whether the Uri scheme is file or content.
Also note that your app is likely to crash with an OutOfMemoryError, except for fairly small files, as you will not have enough heap space to perform this conversion.
I'm trying to read the contents of a flat file stored in the phone's memory. But when I get the file address through the attempt, and I create the File object this is not readable.
Try to create it by manually passing the file's address and it works.
The problem is that I can not get that address from the uri that gives me the Intent
Here I open the android file browser
btnAdjuntar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("text/plain");
Intent chooser = Intent.createChooser(intent, "Elige App");
if (chooser.resolveActivity(getPackageManager()) != null) {
startActivityForResult(chooser, COD_ABRIR_FICHERO);
}
}
});
Here I receive the Intent
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == COD_ABRIR_FICHERO) {
if (resultCode == RESULT_OK) {
String ruta = data.getData().getPath();
String lectura;
try {
lectura = leerArchivo(ruta);
} catch (Exception e) {
lectura = e.getMessage();
}
Toast.makeText(getApplicationContext(), lectura, Toast.LENGTH_LONG).show();
}
}
}
The exception is thrown and you have this message:
/document/primary:Documents/PruebaArchivo.txt: open failed: ENOENT (No such file or directory)
Method leerArchivo
public String leerArchivo(String ruta) throws FileNotFoundException, IOException {
String cadena;
String lectura = "";
File file = new File(ruta);
FileReader f = new FileReader(file);
BufferedReader b = new BufferedReader(f);
while ((cadena = b.readLine()) != null) {
lectura += cadena;
}
b.close();
return lectura;
}
Already the permissions to read the external memory are accepted
You can open an InputStream from a Uri:
InputStream is = getContentResolver().openInputStream(uri);
Then wrap the input stream to create a BufferedReader:
BufferedReader b = new BufferedReader(new InputStreamReader(is));
while ((cadena = b.readLine()) != null) {
lectura += cadena;
}
b.close();
etc.
This way you never need to get the file's address. You can specify the encoding (e.g. "UTF-8") as a second parameter to the InputStreamReader constructor.
But when I get the file address through the attempt, and I create the File object this is not readable.
You are not getting a file. You are getting content. You are being given a Uri. A Uri is not a file.
String ruta = data.getData().getPath();
That line is only useful if the scheme of the Uri is file. In your case, the scheme is probably content.
Use a ContentResolver and openInputStream() to get an InputStream on the content. This works whether the Uri has a file scheme or a content scheme.
am writing a downloadManager and i need a hint. there i could create an event an fire it when ever i wanted. but i cant seem to find how to do that in java. what i want to do is i want to create an event for my class and then fire it inside one of the classes' member methods. now when ever this Class is called and sees that the download is finish(i.e. some variable has reached 100 for example) it fires an event indicating the situation. how can i create that in java?
public class DownloadManager
{
static Queue<AvailableGame> downloadQueue;
static Integer currenProgress;
static String currentDownload;
static Boolean isRunning;
/**
* Start download
*/
public void startDownloading()
{
AsyncTask task = new AsyncTask()
{
#Override
protected Object doInBackground(Object[] objects)
{
downloadNextFile();
return null;
private static String downloadFile(String downloadUrl)
{
String toDownload = downloadUrl;
String fileName = getFileNameFromUrl(toDownload);
// take CPU lock to prevent CPU from going off if the user
// presses the power button during download
PowerManager pm = (PowerManager)
MainActivity.getContext().getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"DownloadManager");
wl.acquire();
try
{
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try
{
URL url = new URL(toDownload);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK)
{
return "Server returned HTTP " + connection.getResponseCode() + " "
+ connection.getResponseMessage();
}
int fileLength = connection.getContentLength();
// download the file
input = connection.getInputStream();
output = new FileOutputStream("/sdcard/" + fileName);
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1)
{
total += count;
// publishing the progress....
if (fileLength > 0)
{
currenProgress = ((int) (total * 100 / fileLength));
currentDownload = "Downloading " + fileName;
}
output.write(data, 0, count);
}
}
catch (Exception e)
{
return e.toString();
}
finally
{
try
{
if (output != null)
{
output.close();
}
if (input != null)
{
input.close();
}
}
catch (IOException ignored)
{
}
if (connection != null)
{
connection.disconnect();
}
}
}
finally
{
wl.release();
}
return null;
}
}
How can i save information when my download is finish,the second probleem is how to a
First I think you should be using Android's DownloadManager for this. If you are, you can register a BroadcastReceiver that detects when a download is finished.
There is a full example you can download and run on your phone with full source code for you to see how this can be done: https://github.com/commonsguy/cw-omnibus/tree/master/EmPubLite/T16-Update
Check specifically the files DownloadCheckService.java and DownloadCompleteReceiver.java.
Hope it helps.