Does passing the Activity Context to an singleton affect the getContentResolver()? - android

**Problem:**I'm updating an app to Android 11 (API 30) and I cannot get the getContentResolver() to work in the following instance though it works elsewhere.
With help from this forum and reading the Scoped Storage docs, elsewhere in the app, the following code works within the Actiities they reside; which includes inputting and outputting from the shared download folder using the getContentResolver(). The file types are a variety.
What I'm trying to do: This app also allows a user (with approved permissions) to attach a variety of files (e.g., docs, audio, video, etc.) to notes in a database. In this case, a PDF. However, the file information gets passed to a Singleton class created to avoid redundancy in the same code within other activities, so I just pass the Context if needed.
As I mentioned, this code works fine when contained within the Activity and the only difference I see is the context is passed to a singleton class. I was hoping someone would see something I'm not.
I do realize the URI is not passed (see the resultLauncher code, but I don't believe that really matters, I can take the file path and change it to an URI, which I have successfully multiple times. Aside from passing to a singleton class, this is the only difference (I can see) compared to my other code.
I could be way off here, the first app and all. A user needs to have the option to attach multiple file types.
**INTENT**
btnAddFile.setOnClickListener(v -> {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
resultLauncher.launch(intent);
});
RESULT LAUNCHER
resultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null){
Uri uri = result.getData().getData();
String util = UriUtils.getPathFromUri(this, uri);
tableLayoutFiles.addView(BuildTableLayout.setupFilesTableRow(EditNote.this,tableLayoutFiles, util,false));
}
});
CODE WITHIN SINGLETON CLASS METHOD (a TableLayout of files and file paths TextViews are passed. So the following code resides in a loop iterating the TextView (i.e., tv) file paths.
File f = new File(tv.getText().toString());
String n = f.getName();
try {
InputStream fis = context.getContentResolver().openInputStream(Uri.fromFile(new File(f.getPath())));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
for (int read; (read = fis.read(buf)) != -1; ) {
bos.write(buf, 0, read);
}
fis.close();
noteFiles.add(new Files(0, n, bos.toByteArray()));
} catch (Exception e) {
Log.e("getContentResolver", e.toString());
e.printStackTrace();
}

Well I solved my own problem and as I suspected, I had to pass the Uri directly to the getContentResolver().openInputStream() and could not convert a path using Uri.fromFile(). Another learners experience.
Adding a new variable HashMap<String, Uri>() called fileURIs to my method, I passed the file Uri to my method. I used a file path as the key and the file's Uri as the key value. I updated the getContentResolver().openInputStream() within the for loop to retrieve the correct file Uri.
Update:
InputStream fis = context.getContentResolver().openInputStream(fileURIs.get(f.getPath()));
File f = new File(tv.getText().toString());
String n = f.getName();
try {
InputStream fis = context.getContentResolver().openInputStream(fileURIs.get(f.getPath()));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
for (int read; (read = fis.read(buf)) != -1; ) {
bos.write(buf, 0, read);
}
fis.close();
noteFiles.add(new Files(0, n, bos.toByteArray()));
} catch (Exception e) {
Log.e("getContentResolver", e.toString());
e.printStackTrace();
}
And I should clarify, the use of Uri.fromFile() does work on file/directories that are not "access denied", so passing a cache file path can be passed to the ContextResolver() without issue in this fashion. In my case, when files are imported from an xml, they get moved to the cache directory which eliminates the issue and why Uri.fromFile() works. That was part of my confusion and not being fully eductated on scoped storage.

Related

Android - Access External Storage Files Android 10/11

I really need your help because I'm stuck <.<
I have already tryed all solutions I've found here in "stackoverflow" for this problem, but any of them worked for me..
I have updated my application to follow the new Google Play Store policies, so actually my app is using only "Scoped Storage" without "Shared Storage" behaviour, so I've removed from the manifest the "requestLegacyExternalStorage".
In my app I need to send to the server some file selected by the user, these file can be stored in the "internal storage" or in the "external storage". They are always stored outside of the app-specific directory, so the directory in:
"/storage/emulated/0/Android/data/[APP_PACKAGE]/files"
My biggest problem is that I can't open any file stored outside of the app-specific directory!
I really need to open the files that the user has selected and convert its content to "base64" to send it to the server.
But when I use this method to get the file content encoded in base64:
public static String fileToBase64(#NonNull String filePath) {
String ret = null;
byte[] buffer = new byte[8192];
int bytesRead;
try(ByteArrayOutputStream bAOS = new ByteArrayOutputStream();
Base64OutputStream b64OS = new Base64OutputStream(bAOS, Base64.DEFAULT);
InputStream iS = new FileInputStream(filePath)){
while ((bytesRead = iS.read(buffer)) != -0x1) {
b64OS.write(buffer, 0x0, bytesRead);
}
b64OS.flush();
ret = bAOS.toString();
} catch (IOException ioE) {
Logger.onException(TAG, ioE);
}
return ret;
}
I always get an ACCESS PERMISSION DENIED.
Is there any way to solve this problem without using "requestLegacyStorage" in the manifest?
I know that Google will remove all applications that don't use only "SCOPED STORAGE" from the store starting from 5 of july, so I can't use the "reqestLegacyStorage" to solve this problem...
I'm already requesting and giving the "READ_EXTERNAL_STORAGE" and "WRITE_EXTERNAL_STORAGE" permissions, but I still can't open the file content if it is stored outside of the dedicated application directory...
I can only open and encode file content if it is in the app directory (so: "/storage/emulated/0/Android/data/[APP_PACKAGE]/files"), but I need to upload to my server files choosed by the user so these files were never stored inside the app directory, they are always stored inside the internal storage or the external storage in directories like the "Download" dir or the "Pictures" dir (I need to upload every type of files, so pictures, documents, pdfs ecc.. ecc.)
Is there any solution to this problem?
I have already tryed all the solutions I found online, but I always get an ACCESS EXCEPTION if I don't add "requestLegacyStorage" to the manifest (and I can't add it to notg go against Google's policies...)
Please I really need to solve this problem because this is one of the most important feature of my app..
Thank you so much!
I hope anybody can help me solve this problem T_T
Have a nice day and nice coding!
(Ask if you need more informations and I will add them!)
##########################################################################
If anyone needs it I found a "solution" but works only by using "ACTION_GET_CONTENT" (and probably by using "ACTION_OPEN_DOCUMENT", but I didn't try it yet).
When you select a file (stored outside the app-specific directory) using "ACTION_GET_CONTENT" this file is copied inside the app-specific directory ("/storage/emulated/0/Android/data/[APP_PACKAGE]/files") so you can open it because it agrees with the "SCOPED STORAGE" policy.
# "ACTION_GET_CONTENT" code:
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
String[] mimes = {
"application/*",
"audio/*",
"font/*",
"image/*",
"message/*",
"model/*",
"multipart/*",
"text/*",
"video/*"
};
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimes);
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
startActivityForResult(Intent.createChooser(intent, getString(R.string.msg_select_file_to_upload)), REQ_CODE_OPEN_DOCUMENT);
Theoretically it also works without passing the "mimes" array to the intent extra "EXTRA_MIME_TYPES".
To get the path inside the "onActivityResult":
String path = FilesUtils.onActivityResultOpenDocument(this, data);
public static String onActivityResultOpenDocument(Context context, Intent data){
String selectedPath, fileName;
Uri uri = data.getData();
String mimeType = context.getContentResolver().getType(uri);
if (mimeType == null) {
String path = getPathFromOpenDocumentUri(context, uri);
if (path == null) {
fileName = FilenameUtils.getName(uri.toString());
} else {
File file = new File(path);
fileName = file.getName();
}
} else {
Uri returnUri = data.getData();
Cursor returnCursor = context.getContentResolver().query(returnUri, null, null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
fileName = returnCursor.getString(nameIndex);
}
String sourcePath = context.getExternalFilesDir(null).toString();
selectedPath = formatFilepath(sourcePath, fileName);
File fileSave = new File(selectedPath);
try {
copyUriStreamToFile(context, uri, fileSave);
} catch (Exception e) {
Logger.onException(TAG, e);
Toast.makeText(context, R.string.error_impossibile_recuperare_file, Toast.LENGTH_SHORT).show();
selectedPath = null;
}
return selectedPath;
}
So summarizing by using "ACTION_GET_CONTENT" (and maybe "ACTION_OPEN_DOCUMENT" too, but I didn't tryed this) the selected file is copied inside the app-specific directory (so: "/storage/emulated/0/Android/data/[APP_PACKAGE]/files") in this way the file can be opened because it agrees with the "Scoped Storage" policy.
Thank you all for your answers and your time! (: <3
I still don't know how to read a file if it is stored outside the app-specific directory, so if anybody know it please share your solution (:

Get real path from Uri - DATA is deprecated in android Q

I'm successfully implementing a method for retrieving the real path of an image from gallery by the Uri returned from ACTION_PICK intent. Here's a sample:
// getRealPathFromURI(intent.getData());
private String getRealPathFromURI(Uri contentURI) {
String result;
Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
if (cursor == null) { // Source is Dropbox or other similar local file path
result = contentURI.getPath();
} else {
cursor.moveToFirst();
int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
result = cursor.getString(idx);
cursor.close();
}
return result;
}
Just like this answer.
Recently updated the compileSdkVersion to 29 and apparently the DATA attribute everyone's using is deprecated.
In the official docs, they recommend to use FileDescriptor instead, problem is i don't know exactly how.
Only thing i found is this question. Didn't find a proper answer there though.
Please help me overcome that deprecation issue with a solution using the suggested way or any other way.
Thank you.
Update:
Followed #CommonsWare's answer and copied the returned Uri (of an image the user picked) to a local directory, using context.getContentResolver.openInputStream(Uri).
Even tried retrieving a file from Google Drive - and it worked. Only problem was the long time it took (about 20 sec for 5MB file).
As a bonus, i was cleared to remove external storage permissions, which one doesn't need for using app's local directories.
No more externals paths for me!
This question came up for me too a week ago.
My solution was to create an InputStream from the URI and then, from this, create an OutputStream by copying the contents of the input stream.
Note: You could call this method using an asynchronous call because copying extremely large files could have some delays and you won't want to block your UI
#Nullable
public static String createCopyAndReturnRealPath(
#NonNull Context context, #NonNull Uri uri) {
final ContentResolver contentResolver = context.getContentResolver();
if (contentResolver == null)
return null;
// Create file path inside app's data dir
String filePath = context.getApplicationInfo().dataDir + File.separator
+ System.currentTimeMillis();
File file = new File(filePath);
try {
InputStream inputStream = contentResolver.openInputStream(uri);
if (inputStream == null)
return null;
OutputStream outputStream = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len;
while ((len = inputStream.read(buf)) > 0)
outputStream.write(buf, 0, len);
outputStream.close();
inputStream.close();
} catch (IOException ignore) {
return null;
}
return file.getAbsolutePath();
}
I'm successfully implementing a method for retrieving the real path of an image from gallery by the Uri returned from ACTION_PICK intent.
That code may not work for all images. There is no requirement for DATA to point to a filesystem path that you can access.
Just like this answer.
FWIW, this was my answer to that question.
Only thing i found is this question. Didn't find a proper answer there though.
That technique wasn't particularly good and will no longer work, as Android has locked down /proc.
In the official docs, they recommend to use FileDescriptor instead, problem is i don't know exactly how.
The more general concept is that you use ContentResolver to work with the Uri, whether you get an InputStream (openInputStream()), OutputStream (openOutputStream()), or FileDescriptor. Consume the content using those things. If you have some API that absolutely needs a file, copy the content (e.g., from the InputStream) to a file that you control (e.g., in getCacheDir()).
As a bonus, now your code is also in position to use the Storage Access Framework (e.g., ACTION_OPEN_DOCUMENT) and the Internet (e.g., OkHttp), if and when that would be useful.

How to get Path of image which is shared from whatsapp in android [duplicate]

I'm successfully implementing a method for retrieving the real path of an image from gallery by the Uri returned from ACTION_PICK intent. Here's a sample:
// getRealPathFromURI(intent.getData());
private String getRealPathFromURI(Uri contentURI) {
String result;
Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
if (cursor == null) { // Source is Dropbox or other similar local file path
result = contentURI.getPath();
} else {
cursor.moveToFirst();
int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
result = cursor.getString(idx);
cursor.close();
}
return result;
}
Just like this answer.
Recently updated the compileSdkVersion to 29 and apparently the DATA attribute everyone's using is deprecated.
In the official docs, they recommend to use FileDescriptor instead, problem is i don't know exactly how.
Only thing i found is this question. Didn't find a proper answer there though.
Please help me overcome that deprecation issue with a solution using the suggested way or any other way.
Thank you.
Update:
Followed #CommonsWare's answer and copied the returned Uri (of an image the user picked) to a local directory, using context.getContentResolver.openInputStream(Uri).
Even tried retrieving a file from Google Drive - and it worked. Only problem was the long time it took (about 20 sec for 5MB file).
As a bonus, i was cleared to remove external storage permissions, which one doesn't need for using app's local directories.
No more externals paths for me!
This question came up for me too a week ago.
My solution was to create an InputStream from the URI and then, from this, create an OutputStream by copying the contents of the input stream.
Note: You could call this method using an asynchronous call because copying extremely large files could have some delays and you won't want to block your UI
#Nullable
public static String createCopyAndReturnRealPath(
#NonNull Context context, #NonNull Uri uri) {
final ContentResolver contentResolver = context.getContentResolver();
if (contentResolver == null)
return null;
// Create file path inside app's data dir
String filePath = context.getApplicationInfo().dataDir + File.separator
+ System.currentTimeMillis();
File file = new File(filePath);
try {
InputStream inputStream = contentResolver.openInputStream(uri);
if (inputStream == null)
return null;
OutputStream outputStream = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len;
while ((len = inputStream.read(buf)) > 0)
outputStream.write(buf, 0, len);
outputStream.close();
inputStream.close();
} catch (IOException ignore) {
return null;
}
return file.getAbsolutePath();
}
I'm successfully implementing a method for retrieving the real path of an image from gallery by the Uri returned from ACTION_PICK intent.
That code may not work for all images. There is no requirement for DATA to point to a filesystem path that you can access.
Just like this answer.
FWIW, this was my answer to that question.
Only thing i found is this question. Didn't find a proper answer there though.
That technique wasn't particularly good and will no longer work, as Android has locked down /proc.
In the official docs, they recommend to use FileDescriptor instead, problem is i don't know exactly how.
The more general concept is that you use ContentResolver to work with the Uri, whether you get an InputStream (openInputStream()), OutputStream (openOutputStream()), or FileDescriptor. Consume the content using those things. If you have some API that absolutely needs a file, copy the content (e.g., from the InputStream) to a file that you control (e.g., in getCacheDir()).
As a bonus, now your code is also in position to use the Storage Access Framework (e.g., ACTION_OPEN_DOCUMENT) and the Internet (e.g., OkHttp), if and when that would be useful.

Trouble writing internal memory android

void launchImageCapture(Activity context) {
Uri imageFileUri = context.getContentResolver()
.insert(Media.INTERNAL_CONTENT_URI, new ContentValues());
m_queue.add(imageFileUri);
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageFileUri);
context.startActivityForResult(i, ImportActivity.CAMERA_REQUEST);
}
The above code, which has always worked, is now generating this exception for me at insert().
java.lang.UnsupportedOperationException: Writing to internal storage is not supported.
at com.android.providers.media.MediaProvider.generateFileName(MediaProvider.java:2336)
at com.android.providers.media.MediaProvider.ensureFile(MediaProvider.java:1851)
at com.android.providers.media.MediaProvider.insertInternal(MediaProvider.java:2006)
at com.android.providers.media.MediaProvider.insert(MediaProvider.java:1974)
at android.content.ContentProvider$Transport.insert(ContentProvider.java:150)
at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:140)
at android.os.Binder.execTransact(Binder.java:287)
at dalvik.system.NativeStart.run(Native Method)
It is not a space issue, and the only thing I changed was the package of an unrelated class all together. Also, I restarted my phone.
Facing same problem here, I was happy to find this thread. Even though two things were bugging me in this workaround, this post had me looking in the right direction. I'd like to share my own workaround/solution.
Let me begin by stating what I did not see myself living with.
First, I did not want to leave the application private file as MODE_WORLD_WRITEABLE. This looks like non-sense to me, although I cannot figure exactly how another application could access this file unless knowing where to look for it with complete name and path. I'm not saying it is necessarily bad for your scenario, but it is still bugging me somehow. I would prefer to cover all my bases by having picture files really private to my app. In my business case, pictures are of no use outside of the application and by no means should they be deleteable via, say, the Android Gallery. My app will trigger cleanup at an appropriate time so as to not vampirize Droid device storage space.
Second, openFileOutput() do not leave any option but to save the resulting file in the root of getFilesDir(). What if I need some directory structure to keep things in order? In addition, my application must handle more than one picture, so I would like to have the filename generated so I can refer to it later on.
See, it is easy to capture a photo with the camera and save it to public image area (via MediaStore) on the Droid device. It is also easy to manipulate (query, update, delete) media from MediaStore. Interestingly, inserting camera picture to MediaStore genreates a filename which appears to be unique. It is also easy to create private File for an application with a directory structure. The crux of the "Capturea camera picture and save it to internal memory" problem is that you can't do so directly because Android prevents ContentResolver to use Media.INTERNAL_CONTENT_URI, and because private app files are by definition not accessible via the (outside) Camera activity.
Finally I adopted the following strategy:
Start the Camera activity for result from my app with the Intent to capture image.
When returning to my app, insert capture to the MediaStore.
Query the MediaStore to obtain generated image file name.
Create a truly internal file onto whatever path relative to private application data folder using Context.getDir().
Use an OutputStream to write Bitmap data to this private file.
Delete capture from MediaStore.
(Optional) show an ImageView of the capture in my app.
Here is the code starting the cam:
public void onClick (View v)
{
ContentValues values = new ContentValues ();
values.put (Media.IS_PRIVATE, 1);
values.put (Media.TITLE, "Xenios Mobile Private Image");
values.put (Media.DESCRIPTION, "Classification Picture taken via Xenios Mobile.");
Uri picUri = getActivity ().getContentResolver ().insert (Media.EXTERNAL_CONTENT_URI, values);
//Keep a reference in app for now, we might need it later.
((XeniosMob) getActivity ().getApplication ()).setCamPicUri (picUri);
Intent takePicture = new Intent (MediaStore.ACTION_IMAGE_CAPTURE);
//May or may not be populated depending on devices.
takePicture.putExtra (MediaStore.EXTRA_OUTPUT, picUri);
getActivity ().startActivityForResult (takePicture, R.id.action_camera_start);
}
And here is my activity getting cam result:
#Override
protected void onActivityResult (int requestCode, int resultCode, Intent data)
{
super.onActivityResult (requestCode, resultCode, data);
if (requestCode == R.id.action_camera_start)
{
if (resultCode == RESULT_OK)
{
Bitmap pic = null;
Uri picUri = null;
//Some Droid devices (as mine: Acer 500 tablet) leave data Intent null.
if (data == null) {
picUri = ((XeniosMob) getApplication ()).getCamPicUri ();
} else
{
Bundle extras = data.getExtras ();
picUri = (Uri) extras.get (MediaStore.EXTRA_OUTPUT);
}
try
{
pic = Media.getBitmap (getContentResolver (), picUri);
} catch (FileNotFoundException ex)
{
Logger.getLogger (getClass ().getName ()).log (Level.SEVERE, null, ex);
} catch (IOException ex)
{
Logger.getLogger (getClass ().getName ()).log (Level.SEVERE, null, ex);
}
//Getting (creating it if necessary) a private directory named app_Pictures
//Using MODE_PRIVATE seems to prefix the directory name provided with "app_".
File dir = getDir (Environment.DIRECTORY_PICTURES, Context.MODE_PRIVATE);
//Query the MediaStore to retrieve generated filename for the capture.
Cursor query = getContentResolver ().query (
picUri,
new String [] {
Media.DISPLAY_NAME,
Media.TITLE
},
null, null, null
);
boolean gotOne = query.moveToFirst ();
File internalFile = null;
if (gotOne)
{
String dn = query.getString (query.getColumnIndexOrThrow (Media.DISPLAY_NAME));
String title = query.getString (query.getColumnIndexOrThrow (Media.TITLE));
query.close ();
//Generated name is a ".jpg" on my device (tablet Acer 500).
//I prefer to work with ".png".
internalFile = new File (dir, dn.subSequence (0, dn.lastIndexOf (".")).toString () + ".png");
internalFile.setReadable (true);
internalFile.setWritable (true);
internalFile.setExecutable (true);
try
{
internalFile.createNewFile ();
//Use an output stream to write picture data to internal file.
FileOutputStream fos = new FileOutputStream (internalFile);
BufferedOutputStream bos = new BufferedOutputStream (fos);
//Use lossless compression.
pic.compress (Bitmap.CompressFormat.PNG, 100, bos);
bos.flush ();
bos.close ();
} catch (FileNotFoundException ex)
{
Logger.getLogger (EvaluationActivity.class.getName()).log (Level.SEVERE, null, ex);
} catch (IOException ex)
{
Logger.getLogger (EvaluationActivity.class.getName()).log (Level.SEVERE, null, ex);
}
}
//Update picture Uri to that of internal file.
((XeniosMob) getApplication ()).setCamPicUri (Uri.fromFile (internalFile));
//Don't keep capture in public storage space (no Android Gallery use)
int delete = getContentResolver ().delete (picUri, null, null);
//rather just keep Uri references here
//visit.add (pic);
//Show the picture in app!
ViewGroup photoLayout = (ViewGroup) findViewById (R.id.layout_photo_area);
ImageView iv = new ImageView (photoLayout.getContext ());
iv.setImageBitmap (pic);
photoLayout.addView (iv, 120, 120);
}
else if (resultCode == RESULT_CANCELED)
{
Toast toast = Toast.makeText (this, "Picture capture has been cancelled.", Toast.LENGTH_LONG);
toast.show ();
}
}
}
Voila! Now we have a truly application private picture file, which name has been generated by the Droid device. And nothing is kept in the public storage area, thus preventing accidental picture manipulation.
here is my working code to save a captured image from the camera to app internal storage:
first, create the file with the desired filename. in this case it is "MyFile.jpg", then start the activity with the intent below. you're callback method(onActivityResult), will be called once complete. After onActivityResult has been called your image should be saved to internal storage. key note: the mode used in openFileOutput needs to be global.. Context.MODE_WORLD_WRITEABLE works fine, i have not tested other modes.
try {
FileOutputStream fos = openFileOutput("MyFile.jpg", Context.MODE_WORLD_WRITEABLE);
fos.close();
File f = new File(getFilesDir() + File.separator + "MyFile.jpg");
startActivityForResult(
new Intent(MediaStore.ACTION_IMAGE_CAPTURE)
.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(f))
, IMAGE_CAPTURE_REQUEST_CODE);
}
catch(IOException e) {
}
and in the activity result method:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == IMAGE_CAPTURE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
Log.i(TAG, "Image is saved.");
}
}
to retrieve your image:
try {
InputStream is = openFileInput("MyFile.jpg");
BitmapFactory.Options options = new BitmapFactory.Options();
//options.inSampleSize = 4;
Bitmap retrievedBitmap = BitmapFactory.decodeStream(is, null, options);
}
catch(IOException e) {
}
The camera apparently doesn't support writing to internal storage.
Unfortunately this is not mentioned in the documentation.
MediaProvider.java has the following code:
private String generateFileName(boolean internal,
String preferredExtension, String directoryName)
{
// create a random file
String name = String.valueOf(System.currentTimeMillis());
if (internal) {
throw new UnsupportedOperationException(
"Writing to internal storage is not supported.");
// return Environment.getDataDirectory()
// + "/" + directoryName + "/" + name + preferredExtension;
} else {
return Environment.getExternalStorageDirectory()
+ "/" + directoryName + "/" + name + preferredExtension;
}
}
So writing to internal storage has been intentionally disabled for the time being.
Edit - I think you can use binnyb's method as a work-around, but I wouldn't recommend it; I'm not sure if this will continue to work on future versions. I think the intention is to disallow writing to internal storage for media files.
I filed a bug in the Android issue tracker.
Edit - I now understand why binnyb's method works. The camera app is considered to be just another application. It can't write to internal storage if it doesn't have permissions. Setting your file to be world-writable gives other applications permission to write to that file.
I still don't think that this is a very good idea, however, for a few reasons:
You don't generally want other apps writing to your private storage.
Internal storage is quite limited on some phones, and raw camera images are quite large.
If you were planning on resizing the image anyway, then you can read it from external storage and write it yourself to your internal storage.

How should I decompress a large data file in AsyncTask.doInBackground?

I have a large data file, which is zipped, and approximately 20MB. When it's unzipped, it's up to about 50MB. The following source code works fine. I found the original on the web somewhere else and modified it a bit. And this method is called within the AsyncTask.doInBackground.
So, what I want to know is, how can I save the on going status(? sorry, I don't know the proper English word) and resume the procedure later? I mean, this method takes a bit long time (about a minute on an emulator), and I know there is no way since the data is kind of huge. So, if a main activity of this method gets killed, I want to save the current status of decompressing the file, and when the activity gets active, I want to resume decompressing from the last point. Hope my explanation clears my intent.
I was thinking using a service, but I also want to interact with UI, such as showing a progress or whatever. I can't find good information to do that in the service when I roughly scan the reference, but is there a way to do that in the service? And do you think I should use it?
Anyway, my main point is how to resume decompressing a file.
private final static int CHUNK_SIZE = 32 * 1024;
byte[] _fileIOBuffer = new byte[CHUNK_SIZE];
public void unzipFile(DBFileDownloader downloader, File zipFile, String directory)
throws IOException
{
ZipInputStream in = null;
FileOutputStream os = null;
try
{
in = new ZipInputStream (new FileInputStream(zipFile));
ZipEntry entry = null;
while ((entry = in.getNextEntry ())!= null)
{
String entryName = entry.getName();
if (entry.isDirectory ()) {
File file = new File (directory, entryName);
file.mkdirs();
}
else {
File file = new File(directory, entryName);
if (file.exists()){
file.delete(); // I don't know how to append, so delete it always
}
os = new FileOutputStream (file);
int bytesRead = 0;
while ((bytesRead = in.read (_fileIOBuffer))!= -1) {
os.write(_fileIOBuffer, 0, bytesRead);
// progress procedure
}
os.close();
}
}
}
catch (FileNotFoundException e) {
Log.v("unzip", e.getMessage());
}
catch (IOException e) {
Log.v("unzip", e.getMessage());
}
finally{
if (in != null ){
in.close();
}
if (os != null ){
os.close();
}
}
}
Thanks in advance,
yokyo
So, if a main activity of this method
gets killed, I want to save the
current status of decompressing the
file, and when the activity gets
active, I want to resume decompressing
from the last point.
That will be extremely difficult, if not impossible.
I was thinking using a service, but I
also want to interact with UI, such as
showing a progress or whatever.
This is a fine plan. Just have the activity register a listener with the service, and the service calls that listener for "a progress or whatever".

Categories

Resources