When I take a photo, I save the image in the DCIM / Camera folder in the external folder with this method:
Method 1:
File fImage = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/Camera/image.jpg");
ContentResolver resolver = activity.getContentResolver();
Uri imageUri = FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + ".provider", fImage);
if(imageUri != null) {
OutputStream fos = resolver.openOutputStream(imageUri);
if(fos != null) {
fos.write(bytes, 0, bytes.length);
fos.flush();
fos.close();
new SingleMediaScanner(activity, fImage);
}
}
Method 2:
File fImage = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/Camera/image.jpg");
OutputStream outputStream=null;
try {
outputStream=new FileOutputStream(fImage);
outputStream.write(bytes);
}finally {
if (outputStream != null)
outputStream.close();
new SingleMediaScanner(activity, fImage);
}
My file is present on the tablet in the desired folder.
My provider contains this:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
In my manifest there is this:
<uses-sdk
android:minSdkVersion="22"
android:targetSdkVersion="30" />
...
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
<application
...
android:requestLegacyExternalStorage="true"
android:preserveLegacyExternalStorage="true">
...
</application>
My class allowing the file scan:
public static class SingleMediaScanner implements MediaScannerConnection.MediaScannerConnectionClient {
private MediaScannerConnection mMs;
private File mFile;
public SingleMediaScanner(Context context, File f) {
mFile = f;
mMs = new MediaScannerConnection(context, this);
mMs.connect();
}
#Override
public void onMediaScannerConnected() {
String extension = FilenameUtils.getExtension(mFile.getAbsolutePath()).toLowerCase();
MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
String mimeType = mimeTypeMap.getMimeTypeFromExtension(extension);
mMs.scanFile(mFile.getAbsolutePath(), mimeType);
}
#Override
public void onScanCompleted(String path, Uri uri) {
mMs.disconnect();
}
}
After this scan, my created file is deleted. And the value of uri is null in onScanCompleted.
Do you know why ?
Edited:
I tested on a Samsung Tab A and there is no problem. But on an Archos T101X4G it doesn't work.
In the end I think that the problem comes only from the tablet Archos: I just tested with WhatsApp and an image downloaded in WhatsApp also disappears...
Related
I spend +50h in this and I cant find a solution, need some help.
I'm trying to take a photo and save it on NAS folder.
I actually can connect to NAS from Android and create a folder but cant get the photo from camera to copy it on NAS.
Method to open the camera.
private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there's a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getApplication().getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
if (photoFile != null) {
photoURI = FileProvider.getUriForFile(getApplicationContext(),
"p.cristian.sealed.provider",
photoFile);
deleteFile(photoFile.getName());
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
// takePictureIntent.putExtra("picname", photoURI);
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
photoFile.deleteOnExit();
}
} catch (Exception ex) {
Log.e("ERROR", ex.getMessage());
}
}
}
private File createImageFile() throws IOException {
String imageFileName = "IMG";
File storageDir =
getApplication().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = null;
image = File.createTempFile(
imageFileName,
".jpg",
storageDir
);
currentPhotoPath = image.getAbsolutePath();
return image;
}
private void algo(String nombreCarpetaNueva, String rutaImagenACopiar){
SmbFileOutputStream out = null;
try {
NtlmPasswordAuthenticator auth = new NtlmPasswordAuthenticator("MyDomain",
"MyUser", "MyPass");
SmbFile smbFile = new SmbFile("smb://" + auth.getUserDomain() + ";" +
auth.getUsername() + ":" + auth.getPassword() + "#" + "192.168.1.50/NAS/Software/" +
nombreCarpetaNueva);
if (!smbFile.exists()) {
smbFile.mkdirs();
}
smbFile.connect();
SmbFile nuevoArchivo = new SmbFile(smbFile + "/" + rutaImagenACopiar);
int cursor;
jcifs.smb1.smb1.SmbFileInputStream in = new
jcifs.smb1.smb1.SmbFileInputStream(nuevoArchivo);
out = new SmbFileOutputStream(nuevoArchivo);
while ((cursor = in.read()) != -1) {
out.write(cursor);
}
out.flush();
out.close();
}
catch (Exception e) {
String msg = "ERROR: " + e.getLocalizedMessage();
Log.i("asdf:",msg);
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Bundle extras = data.getExtras(); // ALWAYS NULL
String fileName = extras.getString("picname"); //NOT WORKING IS NULL
algo("R_folder", fileName); //this just create the folder but never copy the image.
//algo("R_folder", fileName+".jpg");
}
}
MANIFEST
<uses-permission
android:name="android.permission.AUTHENTICATE_ACCOUNTS"
android:maxSdkVersion="22" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-feature android:name="android.hardware.camera.autofocus" />
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="p.cristian.sealed.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
FILE_PATHS FILE
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="my_images" path="/" />
</paths>
I tested a lot of stackoverflow answers but never got the correct one in my case :(
Some site answers tested from:
Android Camera Intent: how to get full sized photo?
Get file path of image on Android
How to get path of picture in onActivityresult (Intent data is null)
Android doc, and much more sites..
EDIT - GOT THE SOLUTION
private void algo(String rutaImagenACopiar){
SmbFileOutputStream out = null;
try {
NtlmPasswordAuthenticator auth = new NtlmPasswordAuthenticator("MyDomain", "myUser", "MyPass");
File file = new File(rutaImagenACopiar);
String fileName = file.getName();
SmbFile smbFile = new SmbFile("smb://" + auth.getUserDomain() + ";" + auth.getUsername() + ":" + auth.getPassword() + "#" + "192.168.1.50/NAS/Software/" + fileName);
if (!smbFile.exists()) {
smbFile.createNewFile();
//smbFile.mkdirs();
}
smbFile.connect();
InputStream inNew = new FileInputStream(file);
SmbFileOutputStream outNew = new SmbFileOutputStream(smbFile);
byte[] buf = new byte[8192];
int len;
while ((len = inNew.read(buf)) > 0) {
outNew.write(buf, 0, len);
}
inNew.close();
outNew.close();
}
Now I just need to get the correct buffer size
Recently I've updated an app to target SDK 30 and adapted the way to create and store a PDF with Mediastore. For create and store I use this piece of code:
#RequiresApi(api = Build.VERSION_CODES.Q)
#NonNull
private Uri savePDFFile(#NonNull final Context context, #NonNull byte[] in,
#NonNull final String mimeType,
#NonNull final String displayName, #Nullable final String subFolder) throws IOException {
String relativeLocation = Environment.DIRECTORY_DOCUMENTS;
if (!TextUtils.isEmpty(subFolder)) {
relativeLocation += File.separator + subFolder;
}
final ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, displayName);
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
final ContentResolver resolver = context.getContentResolver();
OutputStream stream = null;
Uri uri = null;
try {
final Uri contentUri = MediaStore.Files.getContentUri("external");
uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues);
ParcelFileDescriptor pfd;
try {
assert uri != null;
pfd = context.getContentResolver().openFileDescriptor(uri, "w");
assert pfd != null;
FileOutputStream out = new FileOutputStream(pfd.getFileDescriptor());
out.write(in);
out.close();
pfd.close();
} catch (Exception e) {
e.printStackTrace();
}
contentValues.clear();
contentValues.put(MediaStore.Video.Media.IS_PENDING, 0);
context.getContentResolver().update(uri, contentValues, null, null);
stream = resolver.openOutputStream(uri);
if (stream == null) {
throw new IOException("Failed to get output stream.");
}
return uri;
} catch (IOException e) {
// Don't leave an orphan entry in the MediaStore
resolver.delete(uri, null, null);
throw e;
} finally {
if (stream != null) {
stream.close();
renderPDF(this.fileName, this.fileType);
}
}
}
The PDf is stored in /storage/emulated/0/Download and once is saved I want to render it by intent
File path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS);
File file = new File(path.toString() + "/" + fileName + "." + filetype);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.setDataAndType(FileProvider.getUriForFile(this, getPackageName() + ".provider",file), "application/pdf");
My provider_paths.xml is setted like this:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external"
path="Download/" />
<external-files-path
name="external_files"
path="Download/" />
<files-path
name="files"
path="Download/" />
</paths>
At this point when you download a pdf shows the pdf reader you want to select and when you choose one shows a message error saying the path is too long?!?(discarded) or if is the Google Drive Pdf Reader shows for a millisecond a blank screen and back again to the app.
How is the proper way to retrieve the pdf file and launch the intent with FileProvider?
I think the problem is in the FileProvider at this point but I don't know how...
intent.setDataAndType(FileProvider.getUriForFile(this, getPackageName() + ".provider",file), "application/pdf");
I wonder if you can help, I'm trying to download an apk file as an update after version check.
it's downloading the apk but it will not open the apk file its printing an error when trying to open the apk
error :
E/UpdateAPP: Update error! file:///storage/emulated/0/download/update.apk exposed beyond app through Intent.getData()
code :
public class UpdateApp extends AsyncTask<String,Void,Void> {
private Context context;
public void setContext(Context contextf){
context = contextf;
}
#Override
protected Void doInBackground(String... arg0) {
try {
URL url = new URL(arg0[0]);
HttpURLConnection c = (HttpURLConnection) url.openConnection();
c.setRequestMethod("GET");
c.setDoOutput(true);
c.connect();
String PATH = Environment.getExternalStorageDirectory() + "/download/";
File file = new File(PATH);
file.mkdirs();
File outputFile = new File(file, "update.apk");
if(outputFile.exists()){
outputFile.delete();
}
FileOutputStream fos = new FileOutputStream(outputFile);
InputStream is = c.getInputStream();
byte[] buffer = new byte[1024];
int len1 = 0;
while ((len1 = is.read(buffer)) != -1) {
fos.write(buffer, 0, len1);
}
fos.close();
is.close();
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(Environment.getExternalStorageDirectory() + "/download/" + "/update.apk")), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // without this flag android returned a intent error!
context.startActivity(intent);
} catch (Exception e) {
Log.e("UpdateAPP", "Update error! " + e.getMessage());
}
return null;
}}
my targetSdkVersion :
targetSdkVersion 27
thanks for the help
Since API 26, 'REQUEST_INSTALL_PACKAGES' permission is necessary permission to achieve install apk file.
Make 'file_paths.xml' in res/xml folder to use FileProvider api
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="" path="/" />
</paths>
Declare FileProvider and permission in AndroidManifest.xml
Note. I used Androidx version of FileProvider, if you don't use androidx, make sure android.support.v4.content.FileProvider
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="{PACKAGE_NAME}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
Get 'Uri' by FileProvider api and request install permission
private fun openFile(file: File) {
val uri = if (Build.VERSION.SDK_INT >= 24) {
val authority = packageName + ".fileprovider"
FileProvider.getUriForFile(this, authority, file)
} else {
Uri.fromFile(file)
}
val myMime = MimeTypeMap.getSingleton()
val mimeType = myMime.getMimeTypeFromExtension(file.extension)
val intent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(uri, mimeType)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
if (Build.VERSION.SDK_INT >= 26 && !packageManager.canRequestPackageInstalls()) {
startActivity(
Intent(
Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
Uri.parse("package:$packageName")
)
)
} else {
intent.action = Intent.ACTION_VIEW
startActivity(intent)
}
}
Edit -> remove specific methods using in my project.
I'm trying to save an image on JPG format on a specific folder from my gallery. But my code is not creating a directory, whenever i create a Toast it return for me /storage/emulated/0/DCIM/MyFodler,but when will i open the gallery, this foder not exist. I'm building the application direct of my devide with Android Marshmallow 6.0.
Code to create Bitmap:
private Bitmap getToBitmap(ImageView view, int Width, int Heigth){
Bitmap bitmap = Bitmap.createBitmap(Width,Heigth, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}
Code to try save the image on gallery:
private void TrySaveMediaStore(){
String path = Environment.getExternalStorageDirectory().toString();
OutputStream FileOut = null;
File file = new File(path,"DCIM/MyFolder");
file.mkdirs();
Toast.makeText(getApplicationContext(),file.getAbsolutePath(),Toast.LENGTH_SHORT).show();
try{
FileOut = new FileOutputStream(file);
FileOut.flush();
FileOut.close();
Bitmap bitmap = getToBitmap(img,img.getMaxWidth(),img.getMaxHeight());
bitmap.compress(Bitmap.CompressFormat.JPEG,100,FileOut);
MediaStore.Images.Media.insertImage(getContentResolver(), file.getAbsolutePath(), file.getName(), file.getName());
Toast.makeText(this,file.getAbsolutePath(),Toast.LENGTH_SHORT).show();
}catch (FileNotFoundException e){
return;
}catch (IOException e){
e.printStackTrace();
}
}
Androidmanifest permissions:
<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" />
DCIM/MyFolder is a directory. You create this as a directory using mkdirs().
You cannot then try using DCIM/MyFolder as a filename for saving a JPEG. You need to create a file inside the directory.
So, instead of:
FileOut = new FileOutputStream(file);
use something like:
File theActualImageFile=new File(file, "something.jpeg");
FileOut = new FileOutputStream(theActualImageFile);
Also:
You need to deal with runtime permissions, if your targetSdkVersion is 23 or higher
A gallery app will see neither the directory nor the file, until you tell the MediaStore to index the newly-created JPEG
i think a had the same problem, actually the image do insert just fine in the memory, but when i tried to watch it didn't show as i expected, i solved it refreshing the gallery with the scanner class, used this code:
MediaScannerConnection.scanFile(this,
new String[] { file.toString() }, null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
Log.i("ExternalStorage", "Scanned " + path + ":");
Log.i("ExternalStorage", "-> uri=" + uri);
}
});
See this link for more info: How can I refresh the Gallery after I inserted an Image in android?
You may use the below code for asking runtime storage permission:
final int MyVersion = Build.VERSION.SDK_INT;
if (MyVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
if (!checkIfAlreadyhavePermission()) {
ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
} else {
TrySaveMediaStore() ;
}
checkIfAlreadyhavePermission() method:
private boolean checkIfAlreadyhavePermission() {
int result = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
return result == PackageManager.PERMISSION_GRANTED;
}
Add onRequestPermission():
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case 1: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
TrySaveMediaStore();
} else {
Toast.makeText(this, "Please give your permission.", Toast.LENGTH_LONG).show();
}
break;
}
}
}
After creating the file scan MediaStore:
public void scanFile(Context c, File file, String mimeType) {
MediaScannerConnection
.scanFile(c, new String[] {file.getAbsolutePath()},
new String[] {mimeType}, null);
}
Yes, the problem is the media scanner. Yo can simply check the file using a terminal (download the app if you don't have it) and go manually to the directory. I had the same problem, but at least I know the file is there.
How do you write files locally and save them to the Downloads App in Android?
Android version: Nougut
The file is not showing in Downloads though. Here's my code:
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_DOWNLOADS), "foo.txt");
try {
String exampleString = "bar\nfoo";
InputStream is = new ByteArrayInputStream(exampleString.getBytes(Charset.forName("UTF-8")));
OutputStream os = new FileOutputStream(file);
byte[] data = new byte[is.available()];
is.read(data);
os.write(data);
is.close();
os.close();
} catch (IOException e) {
throw new AssertionError(e.toString());
}
MediaScannerConnection.scanFile(
getContext(),
new String[]{file.getAbsolutePath()},
null,
new MediaScannerConnection.OnScanCompletedListener() {
#Override
public void onScanCompleted(String s, Uri uri) {
Log.d(TAG, "String: "+ s);
Log.d(TAG, "Uri: "+ uri );
}
});
It is logging this in onScanCompleted so it seems like the file should show in Downloads but it doesn't.
D/SignupFragment: String: /storage/emulated/0/Download/foo.txt
D/SignupFragment: Uri: content://media/external/file/84691
I have read the Android docs on saving files
AndroidManifest.xml
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
The standard AOSP Downloads app only shows what DownloadManager downloaded. It does not show files placed in the Downloads/ directory by other means.