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.
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
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...
I'm using the following code to share an audio file with Android Studio 3.3.2 and Java. The file is extracted from the /raw/ folder.
public void funktionTeilen(String file) {
Uri uri = Uri.parse("android.resource://" + this.getPackageName() + "/raw/" + file);
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("audio/mpeg3");
share.putExtra(Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(share, "Audio teilen"));
}
In principle the sharing works fine, but the file is sent without a file extension, which of course makes it unreadable by most apps. If I add the file extension manually (for example after downloading it from the e-mail client), the MP3 works fine.
"file" is fed in by another function and corresponds to the file name from the raw folder. It is also found basically, otherwise the sharing process would not work at all.
So, how can I share a file with retention of the file extension? Thanks for your help!
Update #1
public void funktionTeilen(String Datei) {
try {
File tmpFile = new File(this.getCacheDir() + "/tmpfile.mp3");
InputStream in = getResources().openRawResource(R.raw.meise1);
FileOutputStream out = new FileOutputStream(tmpFile, true);
byte[] buff = new byte[1024];
int read = 0;
try {
while ((read = in.read(buff)) > 0) {
out.write(buff, 0, read);
}
} finally {
in.close();
out.close();
}
// Uri uri = FileProvider.getUriForFile(this, this.getPackageName(), tmpFile);
Uri uri = Uri.fromFile(tmpFile);
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("audio/*");
share.putExtra(Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(share, "Audio teilen"));
} catch (Exception e) {
Toast.makeText(MainActivity.this, e.toString(), Toast.LENGTH_LONG).show();
}
}
I'm using the following code to share an audio file with Android Studio 3.3.2 and Java
You are sharing a resource. That is a file on your development machine. It is not a file on the device.
In principle the sharing works fine, but the file is sent without a file extension, which of course makes it unreadable by most apps.
In Android, resources do not have file extensions when we work with them using the Android SDK.
So, how can I share a file with retention of the file extension?
If you share an actual file, using FileProvider, it will have a file extension. So, copy the bytes corresponding to your resource to a file (e.g., in getCacheDir()), then set up FileProvider and use FileProvider.getUriForFile() to get the Uri to use with EXTRA_STREAM.
I worked out the solution!
MainActivity.java
public void funktionTeilen(String Datei) {
try {
File tmpFile = new File(this.getCacheDir() + "/tmpfile.mp3");
InputStream in = getResources().openRawResource(R.raw.meise1);
FileOutputStream out = new FileOutputStream(tmpFile, false);
byte[] buff = new byte[1024];
int read = 0;
try {
while ((read = in.read(buff)) > 0) {
out.write(buff, 0, read);
}
} finally {
in.close();
out.close();
/* if (tmpFile.exists()) {
Toast.makeText(MainActivity.this, tmpFile.getAbsolutePath(), Toast.LENGTH_LONG).show();
} */
}
Uri uri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID, tmpFile.getAbsoluteFile());
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("audio/mpeg3");
share.putExtra(Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(share, "Audio teilen"));
} catch (Exception e) {
Toast.makeText(MainActivity.this, e.toString(), Toast.LENGTH_LONG).show();
}
}
AndroidManifest.xml
<application>
<provider
android:name="android.support.v4.content.FileProvider"
android:grantUriPermissions="true"
android:exported="false"
android:authorities="${applicationId}">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_provider_paths"/>
</provider>
</application>
file_provider_paths.xml
<paths>
<cache-path name="cache" path="/" />
<files-path name="files" path="/" />
</paths>
I am unable to attach my file in native android email or Gmail application.
Gmail gives me this error:
2019-01-17 16:33:17.884 15415-15415/? E/Gmail: Gmail:Error adding attachment
fjd: SecurityException when openAssetFileDescriptor.
That is the code where save the file on storage:
public Uri getUrlFromDrawable(String base64ImageData) {
FileOutputStream fos;
try {
final String pureBase64Encoded = base64ImageData.substring(base64ImageData.indexOf(",") + 1);
final byte[] decodedBytes = Base64.decode(pureBase64Encoded, Base64.DEFAULT);
String filename = "receipt2_" + System.currentTimeMillis() + ".jpg";
File file = new File(reactContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES), filename);
fos = new FileOutputStream(file);
fos.write(decodedBytes);
fos.flush();
fos.close();
Uri contentUri = FileProvider.getUriForFile(getReactApplicationContext(), "com.myapp.fileprovider", file);
return contentUri;
}
return null
}
And here is my function to send the mail:
public void mail(ReadableMap options, Callback callback) {
Intent i = new Intent(Intent.ACTION_SENDTO);
i.setData(Uri.parse("mailto:"));
if (options.hasKey("subject") && !options.isNull("subject")) {
i.putExtra(Intent.EXTRA_SUBJECT, options.getString("subject"));
}
if (options.hasKey("body") && !options.isNull("body")) {
String body = options.getString("body");
i.putExtra(Intent.EXTRA_TEXT, Html.fromHtml(new StringBuilder().append(body).toString())
);
}
if (options.hasKey("recipients") && !options.isNull("recipients")) {
ReadableArray recipients = options.getArray("recipients");
i.putExtra(Intent.EXTRA_EMAIL, readableArrayToStringArray(recipients));
}
if (options.hasKey("receipt") && !options.isNull("receipt")) {
String base64ImageData = options.getString("receipt");
try {
if (base64ImageData != null) {
Uri imageUri = getUrlFromDrawable(base64ImageData);
i.putExtra(Intent.EXTRA_STREAM, imageUri);
}
} catch (Exception e) {
Log.e("ERROR", "Cannot save receipt");
}
}
PackageManager manager = reactContext.getPackageManager();
List<ResolveInfo> list = manager.queryIntentActivities(i, 0);
if (list == null || list.size() == 0) {
callback.invoke("not_available");
return;
}
if (list.size() == 1) {
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
reactContext.startActivity(i);
} catch (Exception ex) {
callback.invoke("error");
}
} else {
Intent chooser = Intent.createChooser(i, "Send Mail");
chooser.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
reactContext.startActivity(chooser);
} catch (Exception ex) {
callback.invoke("error");
}
}
}
Here is the fileprovider.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path
name="Pictures"
path="/" />
</paths>
And here is the provider in `AndroidManifest.xml
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.myapp.fileprovider"
tools:replace="android:authorities"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
tools:replace="android:resource"
android:resource="#xml/fileprovider" />
</provider>
I managed to fix it by granting permission at runtime like this
List<ResolveInfo> list = manager.queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : list) {
String packageName = resolveInfo.activityInfo.packageName;
reactContext.grantUriPermission(packageName, imageUri , Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
If you also add it as ClipData, the permission grant will work successfully:
yourIntent.clipData = ClipData.newUri(context.contentResolver, fileName, contentUri)
The culprit is that Intent.FLAG_GRANT_READ_URI_PERMISSION will work only for Intent.data and Intent.clipData, and not for extras, unless the uri permission grant has been given explictly (inconvenient), or has been given by also adding a ClipData.
Note that your answer also worked, though I find it a little less convenient since for Android 11+, it requires you to declare queries in the AndroidManifest.xml, which might also be a privacy issue in some cases.
What I am trying to do is to get a file that is in the private area to be opened by another program. In this example, a image viewer app.
AndroidManifest.xml
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mydomain.myapp"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/path" />
</provider>
/res/xml/path.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="cache" path="cache/"/>
</paths>
Java code (file location)
File file = new File(context.getApplicationInfo().dataDir + File.separator + "cache" + File.separator + filename);
Java code (open file)
if(file != null && file.isFile() && file.canRead() && file.length() > 0){
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
List<ResolveInfo> resInfoList = this.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
Uri uri = Uri.parse(file.toURI().toString());
for (int i = 0; i < resInfoList.size(); i++) {
try{
ResolveInfo resolveInfo = resInfoList.get(i);
String packageName = resolveInfo.activityInfo.packageName;
grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}catch(Exception e){
}catch(Error e){
}
}
MimeTypeMap myMime = MimeTypeMap.getSingleton();
Intent newIntent = new Intent(Intent.ACTION_VIEW);
String mimeType = myMime.getMimeTypeFromExtension(".jpg");
newIntent.setDataAndType(Uri.fromFile(file), mimeType);
newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
context.startActivity(newIntent);
return true;
} catch (Exception e) {
}
}
This always results in a toast message "Unable to find item". What am I doing wrong and how to resolve this?
What am I doing wrong
File file = new File(context.getApplicationInfo().dataDir + File.separator + "cache" + File.separator + filename);
Use getCacheDir() to get the cache directory, as your approach is not going to be reliable for secondary accounts on Android 4.2+ tablets and Android 5.0+ phones. So, this should be:
File file = new File(context.getCacheDir(), filename);
Then, you need to replace Uri.fromFile(file) with a call to getUriForFile() on FileProvider, as is covered in the documentation. What you presently have is a Uri pointing to a file, not pointing to a ContentProvider, let alone your FileProvider implementation.
Finally, you will need to call addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) on the Intent before you use it with startActivity(), as otherwise the app that responds to the startActivity() request will not have access to the content.
This sample app demonstrates a working implementation of a FileProvider.