I am passing a local file through Javascript to set an img source and I cannot get it working.
The javascript to set the path is
<img src='" + the_payload.image + "' width='" + (cellWidth()-20) + "' />
The String that I am sending is the complete file path:
file:///mnt/sdcard/Android/data/com.newvisioninteractive.android.myapp/files/c87eba4a-5349-4a55-baec-cc573a5f7571-thumb.png
I saw a post about ContentProviders and ParcelFileDescriptors but could not get that to work either.
This is for a Calendar View that displays images on certain days. It works fine when I pass in a static image, just not a local one.
Also, I have set
mWebView.getSettings().setAllowFileAccess(true);
mWebView.getSettings().setJavaScriptEnabled(true);
Do I need to set any other Permissions?
The solution is in extending the ContentProvider class and Overriding the openFile(Uri, String) method
package com.packagename.provider;
public class MyProvider extends ContentProvider {
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode){
URI fileURI = URI.create( "file://" + uri.getPath() );
File file = new File( fileURI );
ParcelFileDescriptor parcel = null;
try {
parcel = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
} catch (FileNotFoundException e) {
Log.e( TAG, "Error finding: " + fileURI + "\n" + e.toString() );
}
return parcel;
}
}
Then in your AndroidManifest.xml file include
<provider
android:name=".provider.MyProvider"
android:authorities="com.packagename" />
You can then access your files which would normally be
file://sdcard/Android/data/com.packagename/image.jpg
by using
content://com.packagename/sdcard/Android/data/com.packagename/image.jpg
So essentially replace file:// with content://com.packagename
try using file:///android_assets/image.png and place images in the android assets
Related
Using ACTION_VIEW with a FileProvider uri for a html file lets browsers like Chrome and Edge display the html page.
Also HTML-viewer app displays. And editor apps can handle the file.
If using a ContentProvider extended uri the browsers only offer to download the file.
HTML-viewer shows an indefinite progressbar.
My apps and external editor apps will still read and edit the file. So providing works.
I see that Chrome, Edge and HTML-viewer only call openFile() and getType().
content://com.jmg.expas.fileprovider/external_files/Documents/index.html
content://com.jmg.expas.simpleprovider/storage/emulated/0/Documents/index.html
I have no idea why both content scheme uries are treated differently.
This is the simple content provider code:
public class SimpleContentProvider extends ContentProvider
{
static String TAG = "simplecontentprovider";
public static Uri getUriForFile ( Context context, File file)
{
return Uri.parse("content://" + context.getPackageName() + ".simpleprovider" + file.getAbsolutePath());
}
#Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException
{
Log.d(TAG, "open-File() mode: " + mode ); // "w"
Log.d(TAG, "uri.getEncodedPath(): " + uri.getEncodedPath() );
String path = uri.getEncodedPath();
File f = new File(path);
if ( ! f.exists() )
{
Log.d(TAG, "path does not exist" );
throw new FileNotFoundException(
"in SimpleContentProvider\n"
+
uri.getPath() );
}
if ( mode.equals("r") )
return (ParcelFileDescriptor.open(f,ParcelFileDescriptor.MODE_READ_ONLY));
return (ParcelFileDescriptor.open(f,ParcelFileDescriptor.MODE_READ_WRITE));
}
private String getExtension ( String fileName )
{
int index = fileName.lastIndexOf(".");
if ( index < 0 )
return "";
return fileName.substring(index+1);
}
#Override
public String getType(Uri uri)
{
Log.d(TAG, "get-Type() getPath(): " + uri.getPath() );
String path = uri.getPath();
String extension = getExtension(path);
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
String mimeType = mimeTypeMap.getMimeTypeFromExtension(extension);
Log.d(TAG, "mimetype: " + mimeType + " for extension " + extension );
// O Oh Oh ..
//return extension;
return mimeType; // and problem solved ;-)
}
// Other member functions not used. They will be added by Android Studio
}
In manifest:
<provider
android:name=".SimpleContentProvider"
android:authorities="${applicationId}.simpleprovider"
android:enabled="true"
android:exported="true" />
Use:
String mimetype = "text/html";
String filePath = .... any filepath to a html file the app can read itself...
Uri uri = SimpleContentProvider.getUriForFile(context, new File(filePath));
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, mimetype);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
);
context.startActivity(intent);
The question is: What do i have to do to let browsers display the source like when FileProvider used? Or what makes them to offer a download instead?
i want to write to a file in the sdcard of my phone.i used the below code to do this.
private CSVWriter _writer;
private File _directory;
public String _fileTestResult;
private String PATH_FILE_EXPORT = "/applications/foru/unittestframework/";
public ExportData(){
_writer=null;
_directory = new File(Environment.getExternalStorageDirectory () +PATH_FILE_EXPORT);
if(!_directory.exists())
_directory.mkdirs();
}
public void exportResult(String testcaseNum,String testcase,String status){
try {
if(_directory.exists()){
//do something
}
but mkdirs() is not working.so i could not excecute following code in the if condition.please help me.
note:i have given the permission in manifest file.
EDIT:
i am using this file write option for storing the result of automation testing using robotium.i have created a normal project and tried to create directory in sdcard.but the same code when i am using in this testproject it is not working.why like that?dont unit testing framework support this?
have you add the correct permission in your manifest ?
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
Edit : ok, i just read your note for permission.
If it's help you this is my sdcard cache code :
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)){
String evtDir = "";
if(evt > 0){
evtDir = File.separator + evt;
}
cacheDir = new File(
android.os.Environment.getExternalStorageDirectory()
+ File.separator
+ "Android"
+ File.separator
+ "data"
+ File.separator
+ Application.getApplicationPackageName()
+ File.separator + "cache"
+ evtDir);
}else
cacheDir=context.getCacheDir();
if(!cacheDir.exists())
cacheDir.mkdirs();
}
Try below code
try {
File root = Environment.getExternalStorageDirectory();
if (root.canWrite()) {
imagefolder = new File(root,
mycontext.getString(R.string.app_name));
imagefolder.mkdirs();
}
} catch (Exception e) {
Log.e("DEBUG", "Could not write file " + e.getMessage());
}
Try with:
if(!_directory.exists())
_directory.mkdir();
Also check this - Creating a directory in /sdcard fails
I have many .mp3 files stored in res/raw folder.
I am getting URI of .mp3 file using following code.
Uri.parse("android.resource:///com.my.android.sharesound/"+resId);
This returns : android.resource:///com.my.android.sharesound/2130968609
Now I am using this URI in creating Share Intent
shareIntent.putExtra(Intent.EXTRA_STREAM,Uri.parse("android.resource://com.my.android.sharesound/"+resId));
startActivity(Intent.createChooser(shareIntent,"Share Sound");
When i select any Mail application eg. Gmail or YahooMail from the Share Intent, the mp3 file attached successfully. But it shows that 2130968609(as an attachment)
i dont want resourceId(2130968609) to appear.
I want to display fileName.mp3
How can i do that? Am i missing something ?
OR
Is there any other way to attach .mp3 file stored in res/raw to mail via Share Intent.
Finally after searching a lot i found the solution.
If you want to get any resource URI then there are two ways :
Using Resource Name
Syntax : android.resource://[package]/[res type]/[res name]
Example : Uri.parse("android.resource://com.my.package/drawable/icon");
Using Resource Id
Syntax : android.resource://[package]/[resource_id]
Example : Uri.parse("android.resource://com.my.package/" + R.drawable.icon);
This were the examples to get the URI of any image file stored in drawable folder.
Similarly you can get URIs of res/raw folder.
In my case i used 1st way ie. Using Resource Name
This work like magic:
Assuming the mp3 is saved in the raw folder as
notification_sound.mp3
then simple doing this works:
Uri uri=Uri.parse("android.resource://"+getPackageName()+"/raw/notification_sound");
Don't construct string on your own, use Uri.Builder:
/**
* #param resourceId identifies an application resource
* #return the Uri by which the application resource is accessed
*/
internal fun Context.getResourceUri(#AnyRes resourceId: Int): Uri = Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(packageName)
.path(resourceId.toString())
.build()
From AOSP-DeskClock.
Here are some methods that might help someone:
public Uri getRawUri(String filename) {
return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + File.pathSeparator + File.separator + getPackageName() + "/raw/" + filename);
}
public Uri getDrawableUri(String filename) {
return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + File.pathSeparator + File.separator + getPackageName() + "/drawable/" + filename);
}
public Uri getMipmapUri(String filename) {
return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + File.pathSeparator + File.separator + getPackageName() + "/mipmap/" + filename);
}
Just call the method like this:
Uri rawUri = getRawUri("myFile.filetype");
One line Answer
RingtoneManager.getRingtone(getContext(),Uri.parse("android.resource://com.my.package/" + R.raw.fileName)).play();
Here is a clean way to get any resource,
Uri.parse(String.format("android.resource://%s/%s/%s",this.getPackageName(),"resource_folder_name","file_name"));
Try saving the file to your SDCard and maybe then send the new URI,
public boolean saveAs(int resSoundId){
byte[] buffer = null;
InputStream fIn = getBaseContext().getResources().openRawResource(resSoundId);
int size=0;
try {
size = fIn.available();
buffer = new byte[size];
fIn.read(buffer);
fIn.close();
} catch (IOException e) {
// TODO Auto-generated catch block
return false;
}
String path = "/sdcard/yourapp/temp/";
String filename = "filename"+".mp3";
boolean exists = (new File(path)).exists();
if (!exists){new File(path).mkdirs();}
FileOutputStream save;
try {
save = new FileOutputStream(path+filename);
save.write(buffer);
save.flush();
save.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
return false;
} catch (IOException e) {
// TODO Auto-generated catch block
return false;
}
Remember to delete the file after the email is sent successfully.
ANSWER:
Needed the call to getExternalFilesDir(p); like so:
String p = thepathblah;
File path=context.getExternalFilesDir(p);
EDIT EDIT:
While I knew the Environment.DIRECTORY_PICTURES was returning just Pictures/ I figured this worked because in android I assumed the file pointer was already pointing to your application space (sorta like in c#). So in this:
String p = Environment.DIRECTORY_PICTURES + "/" + s.getClient().getFirstName()+s.getClient().getLastName() +
"/" + s.getPackage().getName() +
(mSession.getSessionDate().getMonth()+1) +
mSession.getSessionDate().getDate() +
(mSession.getSessionDate().getYear()+1900);
I thought was getting the full path, in fact I was writing a file out to this with no issues. It turns out though to delete individual files (and load them) I needed a fuller path which ended up being:
String p = Environment.DIRECTORY_PICTURES + "/" + s.getClient().getFirstName()+s.getClient().getLastName() +
"/" + s.getPackage().getName() +
(mSession.getSessionDate().getMonth()+1) +
mSession.getSessionDate().getDate() +
(mSession.getSessionDate().getYear()+1900);
File dir = new File("/sdcard/Android/data/com.software.oursoftware/files/"+p);
Not sure if I can take it that the above link is valid for all Honeycomb devices or not, specifically the /sdcard/Android/data/packagespace/files/
Is this safe to use this or do I have to do something more dynamic for honeycomb devices???
EDIT: This is my little test function code to just write something to a folder...
String p = Environment.DIRECTORY_PICTURES + "/" + s.getClient().getFirstName()+s.getClient().getLastName() + "/" + s.getPackage().getName() + (mSession.getSessionDate().getMonth()+1) + mSession.getSessionDate().getDate() + (mSession.getSessionDate().getYear()+1900);
File path = mContext.getExternalFilesDir(p);
File file = new File(path, "DemoPicture.jpg");
try {
// Very simple code to copy a picture from the application's
// resource into the external file. Note that this code does
// no error checking, and assumes the picture is small (does not
// try to copy it in chunks). Note that if external storage is
// not currently mounted this will silently fail.
InputStream is = getResources().openRawResource(R.drawable.ic_contact_picture);
OutputStream os = new FileOutputStream(file);
byte[] data = new byte[is.available()];
is.read(data);
os.write(data);
is.close();
os.close();
// Tell the media scanner about the new file so that it is
// immediately available to the user.
MediaScannerConnection.scanFile(mContext,
new String[] { file.toString() }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String arg0, Uri arg1) {
Log.i("ExternalStorage", "Scanned " + arg0 + ":");
Log.i("ExternalStorage", "-> uri=" + arg1);
}
});
} catch (IOException e) {
// Unable to create file, likely because external storage is
// not currently mounted.
Log.w("ExternalStorage", "Error writing " + file, e);
}
Then the way I try to delete this folder:
String p = Environment.DIRECTORY_PICTURES + "/" + firstName+lastName +"/" + pName+pDate;
File dir=new File(p);
deleteRecursive(dir);
results in
Pictures/ShaneThomas/Portrait882011/
Which can write a file, tested that, but if I try to say:
void deleteRecursive(File dir)
{
Log.d("DeleteRecursive", "DELETEPREVIOUS TOP" + dir.getPath());
if (dir.isDirectory())
{
String[] children = dir.list();
for (int i = 0; i < children.length; i++)
{
File temp = new File(dir, children[i]);
if(temp.isDirectory())
{
Log.d("DeleteRecursive", "Recursive Call" + temp.getPath());
deleteRecursive(temp);
}
else
{
Log.d("DeleteRecursive", "Delete File" + temp.getPath());
boolean b = temp.delete();
if(b == false)
{
Log.d("DeleteRecursive", "DELETE FAIL");
}
}
}
dir.delete();
}
}
The dir.isDirectory is always false!? I got this delete file/directories code off stack overflow but am puzzled as to why its not working?
and I do have this set:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
There are several reasons for File.isDirectory() to return false:
The path points to file (obviously), and not to directory.
The path is invalid (i.e. there is no such file/directory exists).
There is not enough permissions granted to your application to determine whether path points to directory.
In general, if isDirectory() returns true, you've got path that points to directory. But if isDirectory() returns false, then it might be or might not be a directory.
In your particular case, the path most likely does not exist. You need to call dir.mkdirs() to create all directories in the path. But since you need that to only recursively delete them, then there is no point in calling dir.mkdirs() just to remove that directory after that.
I think you want to add
dir.mkdirs() right after File dir=new File(p). mkdirs() is the method responsible for actually creating a directory, not new File().
Ok it's answered, but sometimes the issue fires because of sample reasone:permission :
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
if you forgot this permission you will always get false result.
I have an activity which uses the Android search and the auto search-suggestions capability.
The thumbnails I am using in my activity are all stored on the SD card (there's a lot of them) and are all in MDPI resolution.
When using them in my activity this is fine as I can account for this and scale appropriately, however in the search suggestions view it seems the thumbnail is assumed to be at the current DPI, and thus the thumbnails look small on HDPI devices.
I am retrieving the thumbnails for the search suggestions view using a custom ContentProvider and overriding the openFile method - however I can't see how this gives me any control over setting the DPI as it just returns a generic ParcelFileDescriptor.
Is it possible to use the ParcelFileDescriptor to inform the search suggestions view of the DPI? Or is there another possible method?
I cannot control it directly myself as the Android framework handles the mapping of data to the auto-suggestions view.
My openFile method looks like:
public ParcelFileDescriptor openFile( Uri uri, String mode ) {
String deletePackage = "content://" + PACKAGE_NAME + "/";
String url = uri.toString();
String filePath = url.substring(deletePackage.length());
Log.d("AssetsContentProvider", "openFile: " + filePath);
File file = new File( filePath );
ParcelFileDescriptor parcel = null;
try {
parcel = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
} catch (FileNotFoundException e) {
Log.e( "AssetsContentProvider", "Error finding: " + file.getPath() + "\n" + e.toString() );
}
return parcel;
}
This seems to be the only place I have any control over the file between the auto-suggest manager requesting it and it being displayed.