I'm making a profile photo change function using Retrofit in android pie.
So I succeeded in uploading the photos taken with the camera to the server.
But I don't know how to transfer the photos selected from the gallery to my server.
(I'm fine with any kind of code in Java Kotlin.)
I will upload the video later.
I searched a lot on Google, but it was hard to get the information I wanted.
Well done in the google doc but i don't know how to do it.
https://developer.android.com/guide/topics/providers/document-provider
The Google doc shows examples of using bitmap or inputstream or something.
Do I need a bitmap or inputstream to upload photos using Retrofit?
I actually need a valid uri.
public void performFileSearch() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, PICTURES_DIR_ACCESS_REQUEST_CODE);
}
#Override
public void onActivityResult(int requestCode, int resultCode,Intent resultData) {
if (requestCode == READ_REQUEST_CODE && resultCode ==Activity.RESULT_OK) {
Uri uri = null;
if (resultData != null) {
uri = resultData.getData();
Log.i(TAG, "Uri: " + uri.toString());
showImage(uri);
}
}
}
public void Edit_Profile (String Image_Uri) {
File file = new File(Image_Uri);
RequestBody requestBody = RequestBody.create(file, MediaType.parse("image/*"));
MultipartBody.Part body = MultipartBody.Part.createFormData("uploaded_file", Num+ID+".jpg", requestBody);
}
In fact, onActivityResult returns the following type of uri.
content://com.android.providers.media.documents/document/image%3A191474
So when I try to send it to my server using that uri I get a FileNotFoundException error.
This is a privacy restriction introduced in Android-Q. Direct access to shared/external storage devices is deprecated when an app targets API 29 and the path returned from getExternalStorageDirectory method is no longer directly accessible to apps. Use the app-specific directory to write & read files.
By default, apps targeting Android 10 and higher are given scoped access into external storage, or scoped storage. Such apps can see the following types of files within an external storage device without needing to request any storage-related user permissions:
Files in the app-specific directory, accessed using getExternalFilesDir().
Photos, videos, and audio clips that the app created from the media store.
Files in the app-specific directory, accessed using getExternalFilesDir().
Photos, videos, and audio clips that the app created from the media store.
Go through the documentation Open files using storage access framework
Coming to the context, One thing that you can do is that as
CommonsWare suggested use InputStreamRequestBody. Otherwise, copy
the selected file to your app sandbox folder IE, app-specific
directory, then access the file from there without any permission.
Just see the below implementation that works in Android-Q and later.
Perform file search
private void performFileSearch(String messageTitle) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
intent.setType("application/*");
String[] mimeTypes = new String[]{"application/x-binary,application/octet-stream"};
if (mimeTypes.length > 0) {
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
}
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(Intent.createChooser(intent, messageTitle), OPEN_DIRECTORY_REQUEST_CODE);
} else {
Log.d("Unable to resolve Intent.ACTION_OPEN_DOCUMENT {}");
}
}
onActivityResult returned
#Override
public void onActivityResult(int requestCode, int resultCode, final Intent resultData) {
// The ACTION_OPEN_DOCUMENT intent was sent with the request code OPEN_DIRECTORY_REQUEST_CODE.
// If the request code seen here doesn't match, it's the response to some other intent,
// and the below code shouldn't run at all.
if (requestCode == OPEN_DIRECTORY_REQUEST_CODE) {
if (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()"
if (resultData != null && resultData.getData() != null) {
new CopyFileToAppDirTask().execute(resultData.getData());
} else {
Log.d("File uri not found {}");
}
} else {
Log.d("User cancelled file browsing {}");
}
}
}
File writing to app specific path
public static final String FILE_BROWSER_CACHE_DIR = "CertCache";
#SuppressLint("StaticFieldLeak")
private class CopyFileToAppDirTask extends AsyncTask<Uri, Void, String> {
private ProgressDialog mProgressDialog;
private CopyFileToAppDirTask() {
mProgressDialog = new ProgressDialog(YourActivity.this);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
mProgressDialog.setMessage("Please Wait..");
mProgressDialog.show();
}
protected String doInBackground(Uri... uris) {
try {
return writeFileContent(uris[0]);
} catch (IOException e) {
Log.d("Failed to copy file {}" + e.getMessage());
return null;
}
}
protected void onPostExecute(String cachedFilePath) {
mProgressDialog.dismiss();
if (cachedFilePath != null) {
Log.d("Cached file path {}" + cachedFilePath);
} else {
Log.d("Writing failed {}");
}
}
}
private String writeFileContent(final Uri uri) throws IOException {
InputStream selectedFileInputStream =
getContentResolver().openInputStream(uri);
if (selectedFileInputStream != null) {
final File certCacheDir = new File(getExternalFilesDir(null), FILE_BROWSER_CACHE_DIR);
boolean isCertCacheDirExists = certCacheDir.exists();
if (!isCertCacheDirExists) {
isCertCacheDirExists = certCacheDir.mkdirs();
}
if (isCertCacheDirExists) {
String filePath = certCacheDir.getAbsolutePath() + "/" + getFileDisplayName(uri);
OutputStream selectedFileOutPutStream = new FileOutputStream(filePath);
byte[] buffer = new byte[1024];
int length;
while ((length = selectedFileInputStream.read(buffer)) > 0) {
selectedFileOutPutStream.write(buffer, 0, length);
}
selectedFileOutPutStream.flush();
selectedFileOutPutStream.close();
return filePath;
}
selectedFileInputStream.close();
}
return null;
}
// Returns file display name.
#Nullable
private String getFileDisplayName(final Uri uri) {
String displayName = null;
try (Cursor cursor = getContentResolver()
.query(uri, null, null, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
displayName = cursor.getString(
cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
Log.i("Display Name {}" + displayName);
}
}
return displayName;
}
Here's a perhaps more general solution that allows the AsyncTask class to be separate, rather than embedded in an activity. It also returns a response to your activity when the task is complete, and uses a ProgressBar rather than the deprecated ProgressDialog.
The async task:
public class FileLoader extends AsyncTask<Uri, Void, String>
{
private WeakReference<Context> contextRef;
public AsyncResponse delegate = null;
public interface AsyncResponse {
void fileLoadFinish(String result);
}
FileLoader(Context ctx , AsyncResponse delegate) {
contextRef = new WeakReference<>(ctx);
this.delegate = delegate;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
protected String doInBackground(Uri... uris) {
Context context = contextRef.get();
ContentResolver contentResolver = context.getContentResolver();
Uri uri = uris[0];
try {
String mimeType = contentResolver.getType(uri);
Cursor returnCursor =
contentResolver.query(uri, null, null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
returnCursor.moveToFirst();
String fileName = returnCursor.getString(nameIndex);
InputStream inputStream = contentResolver.openInputStream(uri);
File downloadDir =
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
File f = new File(downloadDir + "/" + fileName);
FileOutputStream out = new FileOutputStream(f);
IOUtils.copyStream(inputStream,out);
returnCursor.close();
return f.getPath();
}
catch (Exception e){
e.printStackTrace();
}
return null;
}
protected void onPostExecute(String result) {
delegate.fileLoadFinish(result);
super.onPostExecute(result);
}
}
and in your activity:
private static final int DIR_ACCESS_REQUEST_CODE = 13;
public void performFileSearch() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/*");
String[] mimeTypes = new String[]{"application/gpx+xml","application/vnd.google-earth.kmz"};
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(Intent.createChooser(intent, "Choose KMZ or GPX file"), DIR_ACCESS_REQUEST_CODE);
} else {
Log.d("****File","Unable to resolve Intent.ACTION_OPEN_DOCUMENT");
}
}
#Override
public void onActivityResult(int requestCode, int resultCode,Intent resultData)
{
super.onActivityResult(requestCode, resultCode, resultData);
if (requestCode == DIR_ACCESS_REQUEST_CODE && resultCode == Activity.RESULT_OK)
{
if (resultData != null)
{
Uri uri = resultData.getData();
mProgressBar.setVisibility(View.VISIBLE);
new FileLoader(this,
new FileLoader.AsyncResponse(){
#Override
public void fileLoadFinish(String result){
processFile(new File(result));
mProgressBar.setVisibility(View.GONE);
}
}).execute(uri);
}
}
}
My example attempts to find either .kmz files or .gpx files. The progress bar (if you need one, for long-running file operations) needs to be initialised (and hidden) in OnCreate():
mProgressBar = findViewById(R.id.progressbar);
mProgressBar.setVisibility(View.GONE);
My 'processFile()' method takes a while manipulating a map in the main activity so I have waited until it's done before hiding the ProgressBar.
I remain surprised that it takes such a lot of code to perform such a simple operation: to copy a file and make it available for use!
Related
This related with my previous question after I changed readFile and make it read from URI for devices running in android 11 and above I got ANR error while I tried to read file
gif showing the error
this my full code
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_CODE_DOC = 1;
private static final String TAG = "MainActivity";
private ActivityMainBinding activityMainBinding = null;
private File file;
private Uri selectedFileURI;
BufferedReader bufferedReader;
InputStream inputStream;
FileReader fileReader;
#Override
protected void onDestroy() {
super.onDestroy();
activityMainBinding = null;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityMainBinding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(activityMainBinding.getRoot());
}
#Override
protected void onStart() {
super.onStart();
activityMainBinding.textView.setMovementMethod(new ScrollingMovementMethod());
activityMainBinding.browseButton.setOnClickListener(view -> {
browseDocuments();
});
activityMainBinding.read.setOnClickListener(view -> {
if (TextUtils.isEmpty(activityMainBinding.editTextPath.getText())) {
activityMainBinding.editTextPath.setError("The file path cannot be empty");
} else {
readFile();
}
});
activityMainBinding.clear.setOnClickListener(view -> activityMainBinding.textView.setText(null));
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_DOC && resultCode == Activity.RESULT_OK) {
try {
if (data != null) {
selectedFileURI = data.getData();
file = new File(selectedFileURI.getPath());
activityMainBinding.editTextPath.setText(file.getAbsolutePath());
Log.d(TAG, "onActivityResult: " + file.getAbsolutePath());
} else {
Toast.makeText(this, "Allow permission for storage access!", Toast.LENGTH_SHORT).show();
}
String mimeType = getContentResolver().getType(selectedFileURI);
Log.i("Type of file", mimeType + "");
} catch (Exception exception) {
if (exception.getMessage() != null) {
Log.e("test Exception", exception.getMessage());
} else if (exception.getCause() != null) {
Log.e("test Exception", Objects.requireNonNull(exception.getCause()).toString());
}
}
}
}
public String getPath(Uri uri) {
String[] projection = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
if (cursor == null) return null;
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
String s = cursor.getString(column_index);
cursor.close();
return s;
}
private void readFile() {
try {
StringBuilder sb = new StringBuilder();
String line;
if (SDK_INT >= Build.VERSION_CODES.R) {
inputStream = getContentResolver().openInputStream(selectedFileURI);
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
} else {
fileReader = new FileReader(file);
bufferedReader = new BufferedReader(fileReader);
}
while ((line = bufferedReader.readLine()) != null) {
sb.append(line).append("\n");
}
activityMainBinding.textView.setText(sb.toString());
if(inputStream != null) {
inputStream.close();
}else if(bufferedReader != null) {
bufferedReader.close();
}else if(fileReader != null) {
fileReader.close();
}
} catch (IOException e) {
Log.e("IOException", e.getMessage());
Log.e("IOException2", e.getCause() + "");
Log.e("IOException3", "exception", e);
Toast.makeText(MainActivity.this, "Cannot read this file", Toast.LENGTH_LONG).show();
}
}
private boolean checkPermission() {
if (SDK_INT >= Build.VERSION_CODES.R) {
return Environment.isExternalStorageManager();
} else {
int result = ContextCompat.checkSelfPermission(this, READ_EXTERNAL_STORAGE);
int result1 = ContextCompat.checkSelfPermission(this, WRITE_EXTERNAL_STORAGE);
return result == PackageManager.PERMISSION_GRANTED && result1 == PackageManager.PERMISSION_GRANTED;
}
}
private void requestPermission() {
if (SDK_INT >= Build.VERSION_CODES.R) {
try {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
intent.addCategory("android.intent.category.DEFAULT");
intent.setData(Uri.parse(String.format("package:%s", getApplicationContext().getPackageName())));
startActivityForResult(intent, 1);
} catch (Exception e) {
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivityForResult(intent, 1);
}
} else {
ActivityCompat.requestPermissions(this, new String[]{READ_EXTERNAL_STORAGE,
WRITE_EXTERNAL_STORAGE}, 1);
}
}
#Override
public void onRequestPermissionsResult(int requestCode, #NonNull String[] permissions, #NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case REQUEST_CODE_DOC:
if (grantResults.length > 0) {
boolean READ_EXTERNAL_STORAGE = grantResults[0] == PackageManager.PERMISSION_GRANTED;
boolean WRITE_EXTERNAL_STORAGE = grantResults[1] == PackageManager.PERMISSION_GRANTED;
if (READ_EXTERNAL_STORAGE && WRITE_EXTERNAL_STORAGE) {
readFile();
} else {
Toast.makeText(this, "Allow permission for storage access!", Toast.LENGTH_SHORT).show();
}
}
break;
}
}
private void browseDocuments() {
if (!checkPermission()) {
requestPermission();
} else {
String[] mimeTypes =
{"text/plain", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"application/vnd.ms-powerpoint", "application/vnd.openxmlformats-officedocument.presentationml.presentation",
"application/vnd.ms-excel", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"textView/plain",
"application/pdf"};
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
setResult(Activity.RESULT_OK);
intent.setType("*/*");
intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
startActivityForResult(Intent.createChooser(intent, "ChooseFile"), REQUEST_CODE_DOC);
}
}
}
That's because you reading files on the Main UI thread which blocks it and causes ANR until is finished, you need to do this work on a background thread, I suggest to take look at the answers on this question even it's old "since 10 years approximately" but it's a good place to start trying, you will find some methods to do the process in background threads like AsyncTask, Callbacks and Executors all this ways can help you to do the fix the issue, but I'll focus in my answer on the newest and recommended way is using "RX Java" I suggest to take look at this RxJava Tutorial you will learn more about it.
let's start to fix this
Add Rx Java dependencies to your project
in build.gradle(app)
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
// Because RxAndroid releases are few and far between, it is recommended you also
// explicitly depend on RxJava's latest version for bug fixes and new features.
// (see https://github.com/ReactiveX/RxJava/releases for latest 3.x.x version)
implementation 'io.reactivex.rxjava3:rxjava:3.0.3'
in read button onClick create a new Observable and call method readFile on it,
the subscribeOn it's defines the thread that the observable will work on it I choosed Schedulers.computation() because you will not be able to determine the size of the doc/text file or How long this process will take, but you can choose other threads like Schedulers.io(), in observeOn you added the thread that observer will work on it, in your case, it's the main thread, and finally call subscribe to connect observable with the observer, I also suggest to add progressBar on your layout to show it while reading the file and hide it when finished the process
activityMainBinding.read.setOnClickListener(view -> {
if (TextUtils.isEmpty(activityMainBinding.editTextPath.getText())) {
activityMainBinding.editTextPath.setError("The file path cannot be empty");
} else {
Observable.fromCallable(this::readFile)
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Object>() {
#Override public void onSubscribe(Disposable d) {
activityMainBinding.progressBar.setVisibility(View.VISIBLE);
}
#Override public void onNext(Object o) {
if(o instanceof StringBuilder){
activityMainBinding.textView.setText((StringBuilder) o);
}
}
#Override public void onError(Throwable e) {
}
#Override public void onComplete() {
activityMainBinding.progressBar.setVisibility(View.INVISIBLE);
}
});
}
});
also, I would suggest you if the file is PDF use AndroidPdfViewer library it makes a lot easier for you and you will not need all these permissions to read PDF files, you can check these this article to learn more about it.
The major problem is caused by blocking the main thread.
You are reading from memory, which is usually taking some time. You are doing it on the main thread, which is used for all UI operations (layout, detecting inputs etc.). While you are occupying it, no rendering or input detection can happen. That is why you are seeing the ANR warning.
To solve your problem, you need to put your work to a background thread. There are several ways to do so.
This is a good starting point. Also that one, if you are running tasks frequently.
Now to give you a quick solution to your problem, you can create a class to do your work:
public class ReadFile extends Worker
{
private File file;
private BufferedReader bufferedReader;
private InputStream inputStream;
private FileReader fileReader;
public ReadFile(#NonNull Context context, #NonNull WorkerParameters workerParams)
{
super(context, workerParams);
}
#NonNull
#Override
public Result doWork()
{
try
{
StringBuilder sb = new StringBuilder();
String line;
if (SDK_INT >= Build.VERSION_CODES.R)
{
Uri uri = Uri.parse(getInputData().getString("URI"));
inputStream = getApplicationContext().getContentResolver().openInputStream(uri);
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
}
else
{
fileReader = new FileReader(file);
bufferedReader = new BufferedReader(fileReader);
}
while ((line = bufferedReader.readLine()) != null)
{
sb.append(line).append("\n");
}
if (inputStream != null)
{
inputStream.close();
}
else if (bufferedReader != null)
{
bufferedReader.close();
}
else if (fileReader != null)
{
fileReader.close();
}
Data myData = new Data.Builder()
.putString("text", sb.toString())
.build();
return Result.success(myData);
}
catch (IOException e)
{
Log.e("IOException", e.getMessage());
Log.e("IOException2", e.getCause() + "");
Log.e("IOException3", "exception", e);
return Result.failure();
}
}
}
If you are using an inner class of your activity, just make sure you are not using any fields in the UI thread, that you are using inside the worker as well. If you do so, you need to synchronize them. That's why I used input and output data in the example.
Then finally in your activity call:
Data myData = new Data.Builder()
.putString("URI", selectedFileURI.toString())
.build();
OneTimeWorkRequest readFile = new OneTimeWorkRequest.Builder(ReadFile.class).setInputData(myData).build();
WorkManager.getInstance(getContext()).enqueue(readFile);
WorkManager.getInstance(getContext()).getWorkInfoByIdLiveData(readFile.getId()).observe(this, info ->
{
String myResult = info.getOutputData().getString("text");
activityMainBinding.textView.setText(myResult);
});
this is because you are using the main thread to handle a lengthy operations
(ANR) error stands for Application Not Responding and this is happening because the main thread handle the application UI and other important staff , but you try to do a lengthy operation by get the image so this makes the main thread unable to manage the UI and the main app.
that's why this message appear to make you chose between waiting for the main thread to be ready for the UI or to close the app.
a good solution is to make this lengthy operation (get the image) handled by another thread , so you need to create a thread that will work with the operation and when the mission completed you need to switch back to the main thread so you can apply the changes on the UI.
Note that you can not touch or change the UI from any thread but the Main Thread because the main thread is the responsible for the application UI , that's why you need to switch back to the main thread after the mission complete.
When I am uploading a pdf file to windows server it's getting corupted when downloading from FileZilla. The File size is also increasing in bytes.
Some files gets corrupted or some files only have half content.
Any help or code would be appreciated.
Thanks!
On Click to choose file from phone's directory:
btnSelectFile.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("application/pdf");
startActivityForResult(Intent.createChooser(intent, "Select PDF File"), 21);
}
});
On Activity Result:
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 21:
if (resultCode == RESULT_OK) {
// Get the Uri of the selected file
uri = data.getData();
uriString = uri.toString();
myFile = new File(uriString);
path = myFile.getAbsolutePath();
displayName = null;
file1 = Uri.fromFile(new File(path));
if (uriString.startsWith("content://")) {
Cursor cursor = null;
try {
cursor = getActivity().getContentResolver().query(uri, null, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
displayName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
textFileSelectedOrNot.setText(displayName);
fileName = textFileSelectedOrNot.getText().toString();
}
} finally {
cursor.close();
}
} else if (uriString.startsWith("file://")) {
displayName = myFile.getName();
textFileSelectedOrNot.setText(displayName);
fileName = textFileSelectedOrNot.getText().toString();
}
}
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
Creating directory on server and uploading file:
class CreateDir extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... voids) {
String server = "XX.XXX.X.XX";
String username = "XXXXXXX";
String password = "XXXXX";
FTPClient ftpClient = new FTPClient();
try {
ftpClient.connect(server, 2102);
ftpClient.login(username, password);
ftpClient.setFileTransferMode(FTP.BINARY_FILE_TYPE);
ftpClient.enterLocalPassiveMode();
Log.d(TAG, "CONNECTED");
String dirPath = "/ws.gajeratrust.org/TestingApp/" + fileId + "/";
boolean created = ftpClient.makeDirectory(dirPath);
FTPUtils.mkdir(ftpClient, dirPath);
InputStream inputStream =getContentResolver().openInputStream(uri);
ftpClient.storeFile(dirPath+"/"+fileName,inputStream);
inputStream.close();
ftpClient.logout();
ftpClient.disconnect();
Log.d(TAG, "DISCONNECTED");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
Please check the code any alterations or something I have coded wrong, please comment it.
InputStream inputStream = new FileInputStream(file);
Replace that by
InputStream inputStream = getContentResolver().openInputStream(data.getData());
You can throw away all code messing around with File and Cursor instances.
Also i wonder what those four lines before the line i quoted should do.
The problem was that I was setting the transfer mode incorrect way.
Right way is :
ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
ftpClient.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);
.... but when do with the standard camera it works perfectly.
I´m struggling against a code that allows me to take a photo in a fragment and also allows me to check
it´s preview with de LatLng in a map (another fragment) with two labels showing both lat and long.
I got to the conclusion that the problem must be when taking the photo because I CAN see Lat and long when I retrieve the standard camera´s photo from the gallery.Files created with my app do not have geolocalization at all.
I tried different permissions.
I Check the type of file JPG
I Check android map Accuracy
I but there is a strange issue "if I reset my Emulator PHONE and start once it works fine for the first camera shot"
'''
public class HomeFragment extends Fragment {
private final String TAG = "HomeFragment";
//
private Uri uriSavedImage;
private ImageView imageView;
private Button btn_takePhoto;
private static final int CAPTURA_RQC = 1;
private static final int REQUEST_PERMISION_CODE = 2;
private View root;
public View onCreateView(#NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
root = inflater.inflate(R.layout.fragment_home, container, false);
imageView = root.findViewById(R.id.imageView2);
//
btn_takePhoto = root.findViewById(R.id.button);
btn_takePhoto.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//checkPermissions
if (checkPermissions()) {
takePhoto();
} else {
askForPermissions();
}
}
});
return root;
}
public void takePhoto() {
//takePhoto
//Allows access otherwise...
/// android.os.FileUriExposedException: file:///sdcard/DCIM/Camera/... .png exposed beyond app through ClipData.Item.getUri()
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
//Crear el intent
Intent hacerFotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//Create unique name.
long timeStamp = System.currentTimeMillis();
/**
* I think the problem is around here.
*/
//Different path taken form emulator
String pathname = "/sdcard/DCIM/Camera/image_" + timeStamp + ".JPG";
// String pathname = "/sdcard/file_example.png";
//
uriSavedImage = Uri.fromFile(new File(pathname));
//
hacerFotoIntent.putExtra(MediaStore.EXTRA_OUTPUT, uriSavedImage);
startActivityForResult(hacerFotoIntent, CAPTURA_RQC);
}
public void fetchImage( ) {
File imgFile = new File(uriSavedImage.getPath());
if (imgFile.exists()) {//
try {
Bitmap mBitmap = MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), uriSavedImage);
imageView.setImageBitmap(mBitmap);
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CAPTURA_RQC && resultCode == Activity.RESULT_OK) {
fetchImage( );
} else {
Toast toast = Toast.makeText(getActivity(), "Could not find the image ", Toast.LENGTH_SHORT);
toast.show();
}
}
/**
* #return
*/
public boolean checkPermissions() {
Log.i(TAG, "checkPermissions");
int permission_read = ActivityCompat.checkSelfPermission(getActivity().getApplicationContext(),
READ_EXTERNAL_STORAGE);
int permission_write_sd = ContextCompat.checkSelfPermission(getActivity().getApplicationContext(),
WRITE_EXTERNAL_STORAGE);
int permission_access_camera = ContextCompat.checkSelfPermission(getActivity().getApplicationContext(),
CAMERA);
int permission_FINE_LOCATION = ContextCompat.checkSelfPermission(getActivity().getApplicationContext(),
ACCESS_FINE_LOCATION);
int permission_COARSE_LOCATION = ContextCompat.checkSelfPermission(getActivity().getApplicationContext(),
ACCESS_COARSE_LOCATION);
int permission_INTERNT = ContextCompat.checkSelfPermission(getActivity().getApplicationContext(),
INTERNET);
return permission_write_sd == PackageManager.PERMISSION_GRANTED
&&
permission_read == PackageManager.PERMISSION_GRANTED
&&
permission_access_camera == PackageManager.PERMISSION_GRANTED
&&
permission_FINE_LOCATION == PackageManager.PERMISSION_GRANTED
&&
permission_COARSE_LOCATION == PackageManager.PERMISSION_GRANTED
&&
permission_INTERNT == PackageManager.PERMISSION_GRANTED
;
}
private void askForPermissions() {
ActivityCompat.requestPermissions(getActivity(),
new String[]{
CAMERA,
READ_EXTERNAL_STORAGE,
WRITE_EXTERNAL_STORAGE,
ACCESS_FINE_LOCATION,
ACCESS_COARSE_LOCATION,
ACCESS_FINE_LOCATION,
ACCESS_BACKGROUND_LOCATION
}
, REQUEST_PERMISION_CODE);
}
}
'''
After capturing the image from the camera and when you are saving the bitmap to a file location , what you can do is you can get the lat and long values from the LocationServices and write that to the file as meta-values like show below
String pathname = "/sdcard/DCIM/Camera/image_" + timeStamp + ".JPG";
ExifInterface exif = new ExifInterface(pathname);
// call this next setAttributes a few times to write all the GPS data to it.
exif.setAttribute(... );
// don't forget to save
exif.saveAttributes();
you can write whatever meta data you need to to that using ExifInterface that you need to see later when you check the details of photo
public class RemoteApkInstaller {
public void install(String url) {
listenForDownloadCompleteEvent();
downloadApk(url);
}
private void listenForDownloadCompleteEvent() {
IntentFilter downloadCompleteIntentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
downloadCompleteReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (isInitiatedApkDownload(intent)) {
if (isDownloadSuccessful()) {
handleDownloadComplete();
} else {
Toast.makeText(context, R.string.download_error, Toast.LENGTH_SHORT).show();
logFailure();
}
context.unregisterReceiver(downloadCompleteReceiver);
}
}
};
context.registerReceiver(downloadCompleteReceiver, downloadCompleteIntentFilter);
}
private void downloadApk(String url) {
Uri uri = Uri.parse(url);
apkFileName = uri.getLastPathSegment();
if (isFileExistsInDownloadDir()) {
handleDownloadComplete();
return;
}
DownloadManager.Request request = getDownloadRequest(uri);
downloadID = getDownloadManager().enqueue(request);
}
private boolean isFileExistsInDownloadDir() {
File directory = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
File file = new File(directory, apkFileName);
return file.exists();
}
private void handleDownloadComplete() {
doOperationsOnDownloadedPackage()
installApk(context);
}
private void doOperationsOnDownloadedPackage() {
PackageManager packageManager= getApplicationContext().getPackageManager();
String appName = (String) packageManager.getApplicationLabel(packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA));
...
}
private void installApk(Context context) {
File directory = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
File file = new File(directory, apkFileName);
String mimeType = downloadID == UNSET_DOWNLOAD_ID ? MIME_TYPE_FOR_APK_FILE : getDownloadManager().getMimeTypeForDownloadedFile(downloadID);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Intent installIntent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
Uri apkUri = FileProvider.getUriForFile(context, "my.package.fileProvider", file);
installIntent.setDataAndType(apkUri, mimeType);
installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(installIntent);
} else {
Uri apkUri = Uri.fromFile(file);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(apkUri, mimeType);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
}
Basically when I click a weblink, an instance of this class is created and the install() method invoked. The problem is when I click the weblink again while the previous download is still only partially complete. I'm trying to check the downloads folder to determine if file already exists and if so, proceed with next set of action.
Even if the previous download is partially complete, the flag is true so in this case I'm executing the doOperationsOnDownloadedPackage() method on an apk file which is not 100% downloaded and I see crashes obviously.
What I'm looking for is a way to avoid initiating a repeat download if the same file is currently downloading.
Any help appreciated.
Thanks
I download an image from the internet with a service. When the download is complete it changes the textview. I tried this on my device and it works.
Now i want the imageview in my layout to change to the downloaded image.
ServiceFile java
public class ServiceFile extends IntentService {
private int result = Activity.RESULT_CANCELED;
public static final String URL = "urlpath";
public static final String FILENAME = "filename";
public static final String FILEPATH = "filepath";
public static final String RESULT = "result";
public static final String NOTIFICATION = "be.ehb.arnojansens.fragmentexampleii";
public ServiceFile() {
super("ServiceFile");
}
// will be called asynchronously by Android
#Override
protected void onHandleIntent(Intent intent) {
String urlPath = intent.getStringExtra(URL);
String fileName = intent.getStringExtra(FILENAME);
File output = new File(Environment.getExternalStorageDirectory(),
fileName);
if (output.exists()) {
output.delete();
}
InputStream stream = null;
FileOutputStream fos = null;
try {
java.net.URL url = new URL(urlPath);
stream = url.openConnection().getInputStream();
InputStreamReader reader = new InputStreamReader(stream);
fos = new FileOutputStream(output.getPath());
int next = -1;
while ((next = reader.read()) != -1) {
fos.write(next);
}
// successfully finished
result = Activity.RESULT_OK;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
publishResults(output.getAbsolutePath(), result);
}
private void publishResults(String outputPath, int result) {
Intent intent = new Intent(NOTIFICATION);
intent.putExtra(FILEPATH, outputPath);
intent.putExtra(RESULT, result);
sendBroadcast(intent);
}
}
This is my main activity where i have my textview and the future imageview
private TextView textView;
private ImageView imageView;
private BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
String string = bundle.getString(ServiceFile.FILEPATH);
int resultCode = bundle.getInt(ServiceFile.RESULT);
if (resultCode == RESULT_OK) {
Toast.makeText(MainActivity.this,
"Download complete. Download URI: " + string,
Toast.LENGTH_LONG).show();
textView.setText("Download done");
// here i shoud load my image i downloaded with my service
} else {
Toast.makeText(MainActivity.this, "Download failed",
Toast.LENGTH_LONG).show();
textView.setText("Download failed");
}
}
}
};
public void service (View view) {
Intent intent = new Intent(this, ServiceFile.class);
// add infos for the service which file to download and where to store
intent.putExtra(ServiceFile.FILENAME, "index.html");
intent.putExtra(ServiceFile.URL,
"http://en.wikipedia.org/wiki/Star_Wars#mediaviewer/File:Star_Wars_Logo.svg");
startService(intent);
textView.setText("Service started");
}
Better to use Picasso or Universal Image Loader than to pass the information per Intent.
If not, your service should write the image into a readable folder and you can send by intent the Uri to that file to whom it concerns with EventBus for instance