Uploading image from Android to my server (Permission Denied) - java

I am trying to upload an image selected from gallery to my Springboot server, but when my service try to post the image I get permission denied for the file path. I have added these permissions to my AndroidManifest:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- permission below just in case, should not be needed I believe -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
I then ask for permission in real time to select the image, and then I want to place it in an inflated view where the user can provide more details about the image, then add it to a report which I will later post.
Since I got this permission trouble I also asked for permission again when I try to submit this Report object containing the images (Uri).
But still I get this error:
Caused by: java.io.FileNotFoundException: /storage/emulated/0/DCIM/Camera/IMG_20200206_120434.jpg (Permission denied)
Every hit I find on this error on google will point to someone who don't ask for this real-time permission, but I even do it once to much I believe.
This is some related snippets of my code:
else if (view.getId() == R.id.stubNewBreedingReportSelectImageButt) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[] {Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
} else {
getPhotoFromPhone(); // this starts the intent to pick an image
}
}
}
else if (view.getId() == R.id.stubNewBreedingReportSubmitButt) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[] {Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 2);
} else {
submitNewBreedingReport();
}
}
}
This is from my onClick(View view) method. The first one works since I am allowed to pick an image from the gallery. The second one should probably not need to check the permissions based on every example I have found of projects uploading images from android.
In my onActivityResult(int requestCode, int resultCode, Intent data) method I inflate this "add image details view". I also store the selected Uri as a private Uri selectedImg in the activity for future use. This all seems to work pretty much fine.
Then when I submit the image (in the submitNewReport() method) I use an ExecutorService (java class) to start a new async thread for the upload. In this Callable<> I get an instance of Springs RestTemplate and try to post the image, but when my restTemplate try to call and fetch the file from my Uri I get the permission denied.
This is the upload method in my apps ImageService:
public Gallery uploadPictureWithInfo(Uri uri, Map<String,Object> imgParams, Context context) {
if (uri.getPath() != null) {
File resourceFile = new File(getPathFromUri(uri,context));
//if (resourceFile.exists()) {
Gallery saved = null;
Map<String,Object> params = new HashMap<>();
params.put(PARAM_FILE, new FileSystemResource(resourceFile));
if (imgParams.get(PARAM_GALLERY_ID) != null || (long) imgParams.get(PARAM_GALLERY_ID) > (long) 0) {
params.put(PARAM_GALLERY_ID, imgParams.get(PARAM_GALLERY_ID));
if (imgParams.get(PARAM_DESCRIPTION) != null) {
params.put(PARAM_DESCRIPTION, imgParams.get(PARAM_DESCRIPTION));
}
if (imgParams.get(PARAM_PHOTOGRAPH) != null) {
params.put(PARAM_PHOTOGRAPH, imgParams.get(PARAM_PHOTOGRAPH));
}
if (imgParams.get(PARAM_USER_ID) != null && (long) imgParams.get(PARAM_USER_ID) > 0) {
params.put(PARAM_USER_ID, imgParams.get(PARAM_USER_ID));
}
HttpEntity requestEntity = new HttpEntity<>(params, AquaDbConfig.getImageHeaders());
ResponseEntity<Gallery> responseEntity =
restTemplate.exchange(AquaDbConfig.getApiUrl() + "/images/uploadImgWithInfo", HttpMethod.POST, requestEntity, Gallery.class);
if (responseEntity.hasBody()) {
saved = responseEntity.getBody();
}
return saved;
}
//}
}
return null;
}
public static String getPathFromUri(Uri uri, Context context) {
String[] filePath = { MediaStore.Images.Media.DATA };
Cursor c = context.getContentResolver().query(uri,filePath, null, null, null);
c.moveToFirst();
int columnIndex = c.getColumnIndex(filePath[0]);
String picturePath = c.getString(columnIndex);
c.close();
return picturePath;
}
I commented out the check for the file.isExist() to get past that test since it wont generate a stack trace otherwise.
So my question is HOW do I get to read the image file when I POST it to the server? I read a little about FileProvider class, but it seems to me that it is used to send files through Intents to new Activites or other Apps. It don't seem to me like it is intended for this because I never leave my Activity exept for picking the image in the gallery. The diffrent steps of creating this ReportedBreeding object is handeled by inflated ViewStubs and not new activites. Also the Uri I use don't refer to any directories I created for my app but rather the users image gallery (external storage).
I also tried to declare my ImageService as a Service in the android manifest, even though I'm not sure we talk about the same kind of service. I then added it this permission but it made no diffrence:
<service
android:name=".service.MyImageFactory"
android:permission="android.permission.READ_EXTERNAL_STORAGE">
</service>
If you know how to get the permission all the way to this RestTemplate POST method (which noone else seems to need in my reviewed examples) or how I can get around this problem, please share! I'm starting to get a little frustrated and stuck. The problem to me is Why do android require yet another permission check and how do I provide it or work around it in my uploadPictureWithInfo(..) method?

Try asking the permission for WRITE_EXTERNAL_STORAGE before getPhotoFromPhone()

For Android 10 this may be the permission issue, there are two solutions for that to handle for now. First method is to permission to manifest Application tag: android:requestLegacyExternalStorage="true"
The other one is to use openFileDescriptor
val parcelFileDescriptor = context.contentResolver.openFileDescriptor(fileUri, "r", null)
val inputStream = FileInputStream(parcelFileDescriptor.fileDescriptor)
fun ContentResolver.getFileName(fileUri: Uri): String {
var name = ""
val returnCursor = this.query(fileUri, null, null, null, null)
if (returnCursor != null) {
val nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
returnCursor.moveToFirst()
name = returnCursor.getString(nameIndex)
returnCursor.close()
}
return name
}
val file = File(context.cacheDir, getFileName(context.contentResolver, fileUri))
val parcelFileDescriptor = context.contentResolver.openFileDescriptor(fileUri, "r", null)
parcelFileDescriptor?.let {
val inputStream = FileInputStream(parcelFileDescriptor.fileDescriptor)
val file = File(context.cacheDir, context.contentResolver.getFileName(fileUri))
val outputStream = FileOutputStream(file)
IOUtils.copy(inputStream, outputStream)
}

Related

imread not reading an existing image on Java Android from Linux

package com.example.cppinandroid;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import org.opencv.core.Mat;
import java.io.File;
import static org.opencv.imgcodecs.Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE;
import org.opencv.imgcodecs.Imgcodecs;
public class MainActivity extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById( R.id.sample_text );
Mat image;
image = Imgcodecs.imread( "/home/<myName>/a.png", CV_LOAD_IMAGE_GRAYSCALE);
if ( image.size().height > 0 )
{
tv.setText( "No datfa in image!");
}
else
{
tv.setText( "xxff " + image.size().height);
}
}
}
I am NOT using any drawable or external media. Image is present in home folder and can be opened by a normal opencv c++ program by giving the exact same path.
Someone here told me that native C++ NDK will not be able to read Linux paths. Alright. Here it is all Java.
When I execute this, it always goes in else statement and shows the height as 0.0.
I have removed the extra code.
What is the way to read a normal png from home folder in this program on Linux?
All Android devices or emulators don't have access to storages outside like your Linux storage partition, they have access to their internal storage or sdcard. In the case of the emulator, their internal storage is emulated using a file with a certain format that cannot be easily read. In an emulator or a device that has Developer options enabled, one could use the adb command found within the Android SDK platform-tools folder to transfer files into it as such:
adb push file.jpg /sdcard/file.jpg
After that, you'll need to change the path of the file your using in the code to match and also enable permissions to READ_EXTERNAL_STORAGE (here external means external to the application your running, but still internal to the device).
Someone here told me that native C++ NDK will not be able to read
Linux paths. Alright. Here it is all Java.
Looking at your question and the answer, for start this is the same problem, trying to access a file that is not part of the device/emulator internal storage. However the answer isn't entirely true, the C/C++ code can access files and directories of the internal storage as long as permission is granted to the application. I would suggest you first try fixing the problem using Java and then switch back to the code you had in your other question but with the corrected path. With Java, you'll be using the Java wrapper for the OpenCV APIs, hence you'll need to call OpenCVLoader.initDebug() to load the wrapper lib. When using pure NDK, you'll only need to load the compiled lib (System.loadLibrary(<libname>) you created with the native C/C++ code.
When you use Imgcodecs.imread(...), it will read the path on your machine, which running your application.
So, if you run the Java Application, it will run on your JVM within your computer, that mean it read the path like ~/home/... on your computer and that path exist, so it can get somethings.
But, Android App will run on DVM within Android Device, that mean when you read ~/home/.., it will take that path on Android Device, but it wasn't exist on Android Devices. So you can't get anything.
The best practice, you should use Imgcodecs.imread(...) with the External Storage Path like some guys suggest you above.
Sometime, you maybe can use Imgcodecs.imread(...) on /mtn/..,path of SD Card, but it isn't correct completely.
see this example code, maybe help you, i tested this code and work for me, and i can get width and height of any image.
1) first you need import OpenCVLibrary to your project: see this Link - Link
2) then you need set READ_EXTERNAL_STORAGE permision to your application:
plz add this command on your AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
3) you need file picker for get specific file i use ru.bartwell:exfilepicker: Link
implementation 'ru.bartwell:exfilepicker:2.1'
4) and at the end you just add this simple code to your MainActivity:
private static final int READ_STORAGE_PERMISSION_REQUEST_CODE = 1;
private static final int EX_FILE_PICKER_RESULT = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initLoadOpenCV();
if (!checkPermissionForReadExtertalStorage()) {
requestPermissionForReadExtertalStorage();
}
}
private void initLoadOpenCV() {
boolean isDebug = OpenCVLoader.initDebug();
if (isDebug) {
Log.i("init Opencv", "init openCV success!!");
} else {
Log.e("init Opencv", "init openCV failure!!");
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == EX_FILE_PICKER_RESULT) {
ExFilePickerResult result = ExFilePickerResult.getFromIntent(data);
if (result != null && result.getCount() > 0) {
// Here is object contains selected files names and path
Log.i("folderLocation", result.getPath() + result.getNames().get(0));
Mat srcMat1 = Imgcodecs.imread(result.getPath() + result.getNames().get(0));
if (srcMat1.empty()) {
return;
}
int width = srcMat1.width();
int height = srcMat1.height();
int type = srcMat1.type();
Log.i("width", srcMat1.width() + "");
Log.i("height", srcMat1.height() + "");
Log.i("type", srcMat1.type() + "");
}
}
}
public boolean checkPermissionForReadExtertalStorage() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
int result = getApplicationContext().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE);
return result == PackageManager.PERMISSION_GRANTED;
}
return false;
}
public void requestPermissionForReadExtertalStorage() {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
READ_STORAGE_PERMISSION_REQUEST_CODE);
}
#Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
switch (requestCode) {
case READ_STORAGE_PERMISSION_REQUEST_CODE:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.e("value", "Permission Granted, Now you can use local drive .");
ExFilePicker exFilePicker = new ExFilePicker();
exFilePicker.start(this, EX_FILE_PICKER_RESULT);
} else {
Log.e("value", "Permission Denied, You cannot use local drive .");
requestPermissionForReadExtertalStorage();
}
break;
}
}
The first problem mentioned in the comment below the question is that you must load the native library that implements the image loading. This can be done with the following code:
static {
// TODO: use OpenCVLoader.initAsync for a real application
if (!OpenCVLoader.initDebug()) {
Log.d(TAG, "Failed to initialize OpenCV.");
}
}
In a real application you would use the initAsync function such that the loading of the libraries does not block the main thread. In a simple example this does not matter.
Another problem is, that file IO on Android requires a permission if you want to access files in arbitrary directories. The permission must be declared in your manifest file. This can be done by adding the following two lines above the application tag in the manifest file.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
These permissions must be requested at runtime. To check if the permission was granted already the following code can be used:
if (ContextCompat.checkSelfPermission(this,
android.Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
// do something with the permission
}
If the permission is not available, it can be requested as follows:
ActivityCompat.requestPermissions(this, new String[]{
android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, RW_PERMISSION_REQUEST_CODE);
Note: we only request the write permission because they are grouped and if the user grants the write permission we automatically also obtain the read permission.
To handle the result the onRequestPermissionsResult callback in the activity class should be overwritten as seen in the full code example below. Because the permission system is quite complex take a look at the official documentation. For info on requesting permissions look here.
Finally to make the loading work the file path must be correct. The user-accessible memory locations depend on the phone manufacturer, therefore it is good to use the system methods provided by Android to find the correct path, for example getExternalStorageDirectory(). More information on the various storage locations can be found here.
Here the full code:
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
import static android.os.Environment.getExternalStorageDirectory;
public class MainActivity extends AppCompatActivity {
private static String TAG = "MainActivity";
private static final int RW_PERMISSION_REQUEST_CODE = 123;
static {
// TODO: use OpenCVLoader.initAsync for a real application
if (!OpenCVLoader.initDebug()) {
Log.d(TAG, "Failed to initialize OpenCV.");
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (ContextCompat.checkSelfPermission(
this, WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
permissionDenied();
} else {
permissionGranted();
}
}
private void permissionDenied() {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, WRITE_EXTERNAL_STORAGE)) {
new AlertDialog.Builder(this)
.setTitle("Read/Write permission required to read an image file.")
.setCancelable(false)
.setPositiveButton("Grant", (dialog, which) ->
ActivityCompat.requestPermissions(this, new String[]{
WRITE_EXTERNAL_STORAGE}, RW_PERMISSION_REQUEST_CODE))
.setNegativeButton("Deny", (dialog, which) -> {
Toast.makeText(this,
"App cannot work without permission.", Toast.LENGTH_LONG).show();
this.finish();
})
.create()
.show();
} else {
ActivityCompat.requestPermissions(
this, new String[]{WRITE_EXTERNAL_STORAGE}, RW_PERMISSION_REQUEST_CODE);
}
}
private void permissionGranted() {
String path = getExternalStorageDirectory().getAbsolutePath() + "/a.png";
Mat image = Imgcodecs.imread(path, Imgcodecs.IMREAD_GRAYSCALE);
if (image.empty()) {
Toast.makeText(this, "Failed image", Toast.LENGTH_LONG).show();
} else {
Size size = image.size();
Toast.makeText(this, "Loaded image " + size.height, Toast.LENGTH_LONG).show();
// the following code is only necessary to display the image in an ImageView
ImageView iv = findViewById(R.id.imageView);
Mat tmp = new Mat((int) size.height, (int) size.width, CvType.CV_8U, new Scalar(4));
try {
Imgproc.cvtColor(image, tmp, Imgproc.COLOR_GRAY2RGBA, 4);
Bitmap bmp = Bitmap.createBitmap(tmp.cols(), tmp.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(tmp, bmp);
iv.setImageBitmap(bmp);
} catch (CvException e) {
Log.d(TAG, e.getMessage());
Toast.makeText(this, "Couldn't convert image.", Toast.LENGTH_LONG).show();
}
}
}
#Override
public void onRequestPermissionsResult(
int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
if (requestCode == RW_PERMISSION_REQUEST_CODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
permissionGranted();
} else {
permissionDenied();
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}
To make this code work add an ImageView with id imageView to your activity_main.xml layout file.
Before you read on further I would like to clarify a few things
From question it is not clear where the code is running? It seems that the user is running this android app on a virtual machine or emulator.
1.1 - From the question it seems that the file she wants to open is on the home directory of Linux machine and not in the emulator's storage or on an Android device - in this case Please note that android apps running on emulator can not access files from your computer.
---- so if you were trying to access file on your Linux pc from within android emulator or vm, please note that it is not possible. Instead copy and put the file in the android emulator or device on which your app will be running.
Please clarify question and let us know whether you have file on the emulator storage (or android device) or it is on your pc and code running on emulator.
If you have file on emulator or android device, please make sure you have right permissions declared in manifest and you have also requested user for permission to read storage before trying to read the image.
Update
Thank you for the response in comments.
How to put file on emulator?
To add a file to the emulated device, drag the file onto the emulator screen. The file is placed in the /sdcard/Download/ directory. You can view the file from Android Studio using the Device File Explorer, or find it from the device using the Downloads or Files app, depending on the device version
source https://developer.android.com/studio/run/emulator
For permissions related stuff you can refer easy to follow documentation on official website - https://developer.android.com/training/permissions/requesting
or check out this question - Android marshmallow request permission?
You can also check -- https://stackoverflow.com/a/30434631/9640177
Depreciation note
Please check https://stackoverflow.com/a/59812593/9640177
To avoid dealing with permissions and external directories, you can also transfer your file to your app's internal storage using android studio -- explore device storage.
Update 2
Refer this answer please - https://stackoverflow.com/a/5990806/9640177
If you want to access them in runtime and not push the files there you have to have a public IP on the internet in which you can access and expose the files on HTTP with something like Apache Tomcat or FTP and then access that from the emulator.
You can also access it locally without exposing it to the whole internet, by accessing from the Android emulator to the "localhost" IP. You'll have to search which IP this is.
So your use case necessitates accessing files from location on your pc, you can use something like tomcat to create a local http server.

How can i disable any android app such that it cannot be opened until i change/press some button in app?

I am trying to build a parental control app. So now i want to disable or lock app (like Whatsapp, Facebook, etc). I have tried using PackageManager.setComponentEnabledSetting(). But it is throwing java.lang.SercurityException.
So how can I make a parental control app such that I can disable any app I want without root.
my code is
pm.setComponentEnabledSetting(new ComponentName(temp.activityInfo.packageName,
temp.activityInfo.name+".class"),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
my error was this
java.lang.SecurityException: Permission Denial: attempt to change component state from pid=11537, uid=10067, package uid=10029
You must add below permissions to manifest.
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
but , these permissions are for System apps and you can not use. :(
You can not write a app to lock or close another app.this is a policy in Google.
for lock a app you must check running apps repeatedly, if specific app is open,then show a activity over that.
while(!Thread.currentThread().isInterrupted())
{
String topActivity = getFrontApp();
if(topActivity.isEmpty())
{
threadSleep(500);
continue;
}
if(topActivity.equals("lockApp"))
{
showLockActivity();
}
threadSleep(500);
}
// for Api21+ need permission
public static String getFrontApp()
{
if (Build.VERSION.SDK_INT >= 21)
{
UsageStatsManager usageManager = SystemMaster.getUsageStatsManager();
long now = System.currentTimeMillis();
List<UsageStats> localList = usageManager.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, now - 900_000L, now);
String str = "";
if (localList != null)
{
SortedMap<Long,UsageStats> mySortedMap = new TreeMap<>();
for(UsageStats usageStats : localList)
mySortedMap.put(usageStats.getLastTimeUsed(), usageStats);
if(!mySortedMap.isEmpty())
str = mySortedMap.get(mySortedMap.lastKey()).getPackageName();
}
return str;
}
else
{
ActivityManager am = (ActivityManager) getApplication().getSystemService(Context.ACTIVITY_SERVICE);
return am.getRunningTasks(1).get(0).topActivity.getPackageName();
}
above code is very simple , for real app must write more.

How to get file path from the content uri for zip file?

I am triggering an intent for selecting a zip file and in onActivityResult, it is providing me a content URI. For example :: (content://com.android.providers.downloads.documents/document/197). The file which is selected is from the download folder. Can any one explain, How to query the content resolver in order to get the file path? Thanks.
Already tried code :-
Cursor cursor = getActivity().getContentResolver()
.query(contentUri, null, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
String path=cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
Log.i(TAG, "path Name: " + path);
}
// NOTE :: Here I am getting the file name, But i want the file path.
} finally {
cursor.close();
}
I tried so many methods to retrieve the file path from the selected file in download folder. Since from android Q on-wards google suggested SAF(Storage Access Framework) to get the data from the user. We cannot use Environment.getExternalStoragePublicDirectory() to get the file path from android Q and above.
For android Q and above you can follow this steps.
1) Start the intent from the activity or fragment you want to trigger.
For example :
public void performFileSearch() { //from developer docs
// ACTION_OPEN_DOCUMENT is the intent to choose a file via the system's file
// browser.
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
// Filter to only show results that can be "opened", such as a
// file (as opposed to a list of contacts or timezones)
intent.addCategory(Intent.CATEGORY_OPENABLE);
// Filter to show only images, using the image MIME data type.
// If one wanted to search for ogg vorbis files, the type would be "audio/ogg".
// To search for all documents available via installed storage providers,
// it would be "*/*".
intent.setType("application/pdf"); // MIME type for what file you required
startActivityForResult(intent, READ_REQUEST_CODE);
}
2) Then result will come in onActivityResult of your activity or fragment
#Override
public void onActivityResult(int requestCode, int resultCode,
Intent resultData) { // from developer docs
// The ACTION_OPEN_DOCUMENT intent was sent with the request code
// READ_REQUEST_CODE. If the request code seen here doesn't match, it's the
// response to some other intent, and the code below shouldn't run at all.
if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
// The document selected by the user won't be returned in the intent.
// Instead, a URI to that document will be contained in the return intent
// provided to this method as a parameter.
// Pull that URI using resultData.getData().
Uri uri = null;
if (resultData != null) {
uri = resultData.getData();
Log.i(TAG, "Uri: " + uri.toString());
processWithUriToGetPath(uri);
}
}
}
Extracting data from uri(Note: For image,video and audio you can refer developer document.Since didn't find any proper document for file, giving this example).
private void processWithUriToGetPath(Uri contentUri) {
//Use content Resolver to get the input stream that it holds the data and copy that in a temp file of your app file directory for your references
File selectedFile = new File(getActivity().getFilesDir(), "your file name"); //your app file dir or cache dir you can use
InputStream in = getActivity().getContentResolver().openInputStream(contentUri);
OutputStream out = new FileOutputStream(selectedFile);
try {
byte[] buf = new byte[1024];
int len;
if (in != null) {
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
out.close();
in.close();
} catch (IOException ie) {
ie.printStackTrace();
}
//after this you will get file from the selected file and you can do
whatever with your wish.
// Hope it helps...
}

can't delete file from external storage in android programmatically

I am trying to delete a file located at the path
/storage/714D-160A/Xender/image/Screenshot_commando.png
What I've done so far:
try{
String d_path = "/storage/714D-160A/Xender/image/Screenshot_commando.png";
File file = new File(d_path);
file.delete();
}catch(Exception e){
e.printStackTrace();
}
and the file is still at its place(Not deleted :( )
Also I've given permission in Manifest file.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.STORAGE" />
public static boolean delete(final Context context, final File file) {
final String where = MediaStore.MediaColumns.DATA + "=?";
final String[] selectionArgs = new String[] {
file.getAbsolutePath()
};
final ContentResolver contentResolver = context.getContentResolver();
final Uri filesUri = MediaStore.Files.getContentUri("external");
contentResolver.delete(filesUri, where, selectionArgs);
if (file.exists()) {
contentResolver.delete(filesUri, where, selectionArgs);
}
return !file.exists();
}
Using ContentResolver to delete media files is wrong and provides many problems for the user.
You can not delete a file on the sd-card simply by deleting its information from the ContentResolver on Android versions greater than Jelly Bean(4.3).
It works only on Android versions prior to KitKat(4.4).
That's why the Android team provided DocumentProvider.
Why contentResolver.delete(...) is wrong?
1. Fills up the sd-card
When you try to delete a media file on the sd-card by the ContentResolver on Android versions greater than 4.3, the actual media file will remain untouched because the contentResolver.delete(...) approach only removes the information (name, date, path ...) of the media and you will end up having unregistered media files on your sd-card which ContentResolver has no idea about their existence anymore and that's why you couldn't see them in your gallery and you think they've been deleted with this approach while they're still there and fill up the sd-card each time the user tries to delete a media file on the sd-card.
2. Media files (Images, videos, gifs ...) will come back to the gallery
There are many apps out there especially gallery and file manager ones that will find these unregistered media files and will add them to the ContentResolver again as of their normal behavior while the user assumes his/her unwanted media files are gone.
Sure no user wants his/her assuming deleted images or videos show up in the middle of a demonstration.
So, what's the correct approach to remove media files on the sd-card?
Well, this has already been answered here with the use of DocumentProvider.
From Android 4.4 onwards, you can't write to SD card files (except in the App directory) using the normal way. You'll have to use the Storage Access Framework using DocumentFile for that.
The following code works for me:
private void deletefile(Uri uri, String filename) {
DocumentFile pickedDir = DocumentFile.fromTreeUri(this, uri);
DocumentFile file = pickedDir.findFile(filename);
if(file.delete())
Log.d("Log ID", "Delete successful");
else
Log.d("Log ID", "Delete unsuccessful");
}
where filename is the name of the file to be deleted and uri is the URI returned by ACTION_OPEN_DOCUMENT_TREE:
private static final int LOCATION_REQUEST = 1;
private void choosePath() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivityForResult(intent, LOCATION_REQUEST);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if (requestCode == LOCATION_REQUEST && resultCode == Activity.RESULT_OK) {
if (resultData != null) {
Uri uri = resultData.getData();
if (uri != null) {
/* Got the path uri */
}
}
}
}
Use Environment.getExternalStorageDirectory().getAbsolutePath() instead of hard coding storage path
String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath();
File f = new File(baseDir + "/714D-160A/Xender/image/Screenshot_commando.png");
boolean d = f.delete();

Android, Glide Image Loading Library: NullPointerException, ProviderInfo

Just getting into the Glide image loading library for Android. Currently working with some code from here:
https://github.com/bumptech/glide/issues/459
My full project is here is you want to look at it:
https://github.com/mhurwicz/glide02
I'm getting the following exception when I run the app in the emulator in Android Studio:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference
This is the key statement in MainActivity:
new ShareTask(this).execute("http://thelink");
(thelink is actually goo.gl/gEgYUd -- couldn't leave that in above because stackoverflow doesn't allow URL shorteners. )
Here is my code for the ShareTask class
class ShareTask extends AsyncTask<String, Void, File> {
private final Context context;
public ShareTask(Context context) {
this.context = context;
}
#Override protected File doInBackground(String... params) {
String url = params[0]; // should be easy to extend to share multiple images at once
try {
return Glide
.with(context)
.load(url)
.downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.get() // needs to be called on background thread
;
} catch (Exception ex) {
Log.w("SHARE", "Sharing " + url + " failed", ex);
return null;
}
}
#Override protected void onPostExecute(File result) {
if (result == null) { return; }
Uri uri = FileProvider.getUriForFile(context, context.getPackageName(), result);
share(uri); // startActivity probably needs UI thread
}
private void share(Uri result) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/jpeg");
intent.putExtra(Intent.EXTRA_SUBJECT, "Shared image");
intent.putExtra(Intent.EXTRA_TEXT, "Look what I found!");
intent.putExtra(Intent.EXTRA_STREAM, result);
context.startActivity(Intent.createChooser(intent, "Share image"));
}
}
Using debug, it seems I may be running into trouble at the get() statement. For one thing, the width and the height are very large negative numbers. (See the code highlighted in green below.) Then the get() statement returns null. (See the code highlighted in red below.)
Thanks in advance for any help you can provide!
The NPE is coming from FileProvider.getUriForFile because you're passing in the wrong authority. You declared android:authorities="com.example.fileprovider" in the manifest, but you're using the package name at the call. This fails to resolve the info in FileProvider.parsePathStrategy. Match those two strings up and you'll be good to go.
The easiest fix is to use android:authorities="${applicationId}", this leads to 0 hardcoded strings, so you can keep using context.getPackageName().
Regarding your concerns during debug:
Target.SIZE_ORIGINAL is declared to be MIN_VALUE, hence the large number
it's not returning null, IDEA is just confused about where it is in the method, that return null; shouldn't be executed if it fails in the FileProvider code.
doGet(null): null is the timeout here, it's guarded properly in code
I've run the app and weirdly I got a log line saying
W/SHARE: Sharing http://... failed
but not a stack trace, which is weird, because ex cannot be null in a catch!

Categories