xamarin android image picker - unable to get path - android

when I open the gallery and go to dropbox or one drive I get a message unable to get path but when I go to google photos or local, I get the right image to load in a grid.
DependencyService.Get<IMediaService>().OpenGallery();
MessagingCenter.Unsubscribe<App, List<string>>((App)Xamarin.Forms.Application.Current, "ImagesSelectedAndroid");
MessagingCenter.Subscribe<App, List<string>>((App)Xamarin.Forms.Application.Current, "ImagesSelectedAndroid", (s, images) =>
{
if (images.Count > 0)
{
Console.WriteLine($"Processed {images.Count} images");
UploadToBlob(images);
GetGallery();
}
public void OpenGallery()
{
try
{
var imageIntent = new Intent(Intent.ActionPick);
imageIntent.SetType("image/*");
imageIntent.PutExtra(Intent.ExtraAllowMultiple, true);
imageIntent.SetAction(Intent.ActionGetContent);
((Activity)CrossCurrentActivity.Current.Activity).StartActivityForResult(Intent.CreateChooser(imageIntent, "Select photo"), Opengallerycode);
Toast.MakeText(CrossCurrentActivity.Current.Activity, "Tap and hold to select multiple photos.", ToastLength.Short)?.Show();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Toast.MakeText(CrossCurrentActivity.Current.Activity, "Error. Can not continue, try again.", ToastLength.Long)?.Show();
}
}

You could get the image into stream from dropbox or one drive and load in a grid.
Create the interface: IPhotoPickerService.cs
public interface IPhotoPickerService
{
Task<Stream> GetImageStreamAsync();
}
Android implementation:
MainActivity.cs
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
internal static MainActivity Instance { get; private set; }
... ...
// Field, property, and method for Picture Picker
public static readonly int PickImageId = 1000;
public TaskCompletionSource<Stream> PickImageTaskCompletionSource { set; get; }
protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
{
base.OnActivityResult(requestCode, resultCode, intent);
if (requestCode == PickImageId)
{
if ((resultCode == Result.Ok) && (intent != null))
{
Android.Net.Uri uri = intent.Data;
Stream stream = ContentResolver.OpenInputStream(uri);
// Set the Stream as the completion of the Task
PickImageTaskCompletionSource.SetResult(stream);
}
else
{
PickImageTaskCompletionSource.SetResult(null);
}
}
}
}
PhotoPickerService.cs
public class PhotoPickerService : IPhotoPickerService
{
public Task<Stream> GetImageStreamAsync()
{
// Define the Intent for getting images
Intent intent = new Intent();
intent.SetType("image/*");
intent.SetAction(Intent.ActionGetContent);
// Start the picture-picker activity (resumes in MainActivity.cs)
MainActivity.Instance.StartActivityForResult(
Intent.CreateChooser(intent, "Select Photo"),
MainActivity.PickImageId);
// Save the TaskCompletionSource object as a MainActivity property
MainActivity.Instance.PickImageTaskCompletionSource = new TaskCompletionSource<Stream>();
// Return Task object
return MainActivity.Instance.PickImageTaskCompletionSource.Task;
}
}
For more information about IOS, UWP implementation of photo picker, you could check the MS article. https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/dependency-service/photo-picker
And download the source file from link. https://learn.microsoft.com/zh-cn/samples/xamarin/xamarin-forms-samples/dependencyservice/

Related

Creating Multiple ParcelFileDescriptor for a file from default file picker URI throws File Not Found Exception

I have targeted Android API level 30 and tried to create multiple file descriptors for a single file from the URI from the default file picker which looks like this.
content://com.android.externalstorage.documents/document/E8A6-89C4%3AMovies%2FTestFile.mp4
content://com.android.externalstorage.documents/document/primary%3ATestFile.mp4
I am using the below code to create 10 threads to asynchronously access a file with ParcelFileDescriptors.
public class TestActivity extends AppCompatActivity {
public static final String TAG = "TEST_LOG";
private Button pickFileBtn;
final int VIDEO_REQUEST_CODE = 111;
#Override
public void onCreate(#Nullable Bundle savedInstanceState, #Nullable PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
setContentView(R.layout.test_activity);
pickFileBtn = findViewById(R.id.pick_file);
pickFileBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
pickFile();
}
});
}
private void pickFile() {
Intent intent = new Intent();
intent.setType("video/*");
intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(intent, VIDEO_REQUEST_CODE);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == VIDEO_REQUEST_CODE) {
if (resultCode == RESULT_OK) {
loadFrame(data.getData());
}
}
}
void loadFrame(Uri uri) {
for (int i = 0; i < 10; i++) {
new Thread(new CustomRunnable(uri)).start();
}
}
}
class CustomRunnable implements Runnable {
private Uri uri;
public CustomRunnable(Uri uri) {
this.uri = uri;
}
#Override
public void run() {
try {
ParcelFileDescriptor descriptor = null;
try {
descriptor = CustomApplication.appContext.getContentResolver().openFileDescriptor(uri, "r");
// Working with the descriptor here.
} catch (Exception e) {
Log.e(TestActivity.TAG, String.format("Failed to obtain parcelFileDescriptor for %s.%s", uri.toString(), e.getMessage()));
} finally {
if (descriptor != null)
descriptor.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Failed to obtain r parcelFileDescriptor for content://com.android.externalstorage.documents/document/primary%3ATestFile.mp4.Permission Denial: reading com.android.externalstorage.ExternalStorageProvider uri content://com.android.externalstorage.documents/document/primary%3ATestFile.mp4 from pid=6190, uid=12283 requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
This can create around 5 descriptors and throws File Not Found Exception for the rest. But, It can successfully create all the required descriptors when I use URI from the media store. And the media store URI looks like this.
content://media/external/video/media/268019
I have tried to reuse the 5 descriptors but when I try to read the files using them, I get an error saying EBML header parsing failed
I have also tried to obtain access to a specific folder using Intent.ACTION_OPEN_DOCUMENT_TREE and then picking a file, but the result is the same, that it only opens 5 descriptors.
Why do opening descriptors from the default file picker only create 5 descriptors and why I can not reuse them.

How to convert a pdf file to an image?

I'm trying to use the ML Kit Text Recognition API in my android app. However it doesn't work with a PDF, I need an image. So I'm looking for a way to convert PDF to image.
I have already try several solutions but most of them are not working anymore.
Note: The PDF file is picked on the device with a file picker
private void extractText2(Uri uri) {
try {
Log.e("URI", uri.toString());
FirebaseVisionImage image = FirebaseVisionImage.fromFilePath(getApplicationContext(), uri);
FirebaseVisionTextRecognizer detector = FirebaseVision.getInstance().getOnDeviceTextRecognizer();
Task<FirebaseVisionText> result =
detector.processImage(image)
.addOnSuccessListener(new OnSuccessListener<FirebaseVisionText>() {
#Override
public void onSuccess(FirebaseVisionText firebaseVisionText) {
displayText(firebaseVisionText);
}
})
.addOnFailureListener(
new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
// Task failed with an exception
// ...
}
});
} catch (Exception e) {
Log.e("Erreur", e.getMessage());
}
}
private void displayText(FirebaseVisionText result) {
String resultText = result.getText();
Log.e("Text trouvé:",resultText);
}
public void performFileSearch() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, READ_REQUEST_CODE);
}
#Override
public void onActivityResult(int requestCode, int resultCode,
Intent resultData) {
if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
Uri uri;
if (resultData != null) {
uri = resultData.getData();
Log.i("---", "Uri: " + uri.toString());
extractText2(uri);
}
}
}
The above code allows to pick an image, then the text recognition work well. If I want to pick a pdf, I have to replace intent.setType("image/*"); with intent.setType("application/pdf"); but the text recognition API won't work, that's why i need to convert the pdf file choosen to an image and then use the text recognition

How to read code 39 using zxing in android?

I am using zxing in my android application to read QR_CODE and Barcodes. My application is unable to read the CODE_39 using zxing. I am using the following code in CaptureActivity OnResume Method:
Intent intent = getIntent();
String action = intent == null ? null : intent.getAction();
String dataString = intent == null ? null : intent.getDataString();
if (intent != null && action != null) {
if (action.equals(Intents.Scan.ACTION)) {
//Scan the formats the intent requested, and return the
//result
//to the calling activity.
source = Source.NATIVE_APP_INTENT;
decodeFormats = DecodeFormatManager.parseDecodeFormats(intent);
} else if (dataString != null
&& dataString.contains(PRODUCT_SEARCH_URL_PREFIX)
&& dataString.contains(PRODUCT_SEARCH_URL_SUFFIX)) {
// Scan only products and send the result to mobile Product
// Search.
source = Source.PRODUCT_SEARCH_LINK;
sourceUrl = dataString;
decodeFormats = DecodeFormatManager.PRODUCT_FORMATS;
} else if (dataString != null
&& dataString.startsWith(ZXING_URL)) {
// Scan formats requested in query string (all formats if
// none
// specified).
// If a return URL is specified, send the results there.
// Otherwise, handle it ourselves.
source = Source.ZXING_LINK;
sourceUrl = dataString;
Uri inputUri = Uri.parse(sourceUrl);
returnUrlTemplate = inputUri
.getQueryParameter(RETURN_URL_PARAM);
decodeFormats = DecodeFormatManager
.parseDecodeFormats(inputUri);
} else {
// Scan all formats and handle the results ourselves
// (launched
// from Home).
source = Source.NONE;
decodeFormats = null;
}
characterSet = intent
.getStringExtra(Intents.Scan.CHARACTER_SET);
Please Help me to solve this issuse. Thanks in advance.
If you are using Android Studio then Add those dependancies-
compile 'me.dm7.barcodescanner:zxing:1.8.3'
compile 'com.journeyapps:zxing-android-embedded:3.0.2#aar'
compile 'com.google.zxing:core:3.2.0'
Zxing Automatically takes code type while scanning
integrator.setDesiredBarcodeFormats(IntentIntegrator.ALL_CODE_TYPES)
Here which is consider all types of codes by default
If you want specific QR then just
integrator.setDesiredBarcodeFormats(IntentIntegrator.QR_CODE_TYPES);
Use following code-
import me.dm7.barcodescanner.zxing.ZXingScannerView;
public class YourActivity extends Activity {
//Barcode Scanning
private ZXingScannerView mScannerView;
// This is your click listener
public void checkBarcode(View v) {
try {
IntentIntegrator integrator = new IntentIntegrator(GateEntryActivity.this);
integrator.setDesiredBarcodeFormats(IntentIntegrator.ALL_CODE_TYPES);
integrator.setPrompt("Scan a barcode");
integrator.setCameraId(0); // Use a specific camera of the device
integrator.setBeepEnabled(false);
integrator.initiateScan();
//start the scanning activity from the com.google.zxing.client.android.SCAN intent
// Programmatically initialize the scanner view
// setContentView(mScannerView);
} catch (ActivityNotFoundException anfe) {
//on catch, show the download dialog
showDialog(GateEntryActivity.this, "No Scanner Found", "Download a scanner code activity?", "Yes", "No").show();
}
}
//alert dialog for downloadDialog
private static AlertDialog showDialog(final Activity act, CharSequence title, CharSequence message, CharSequence buttonYes, CharSequence buttonNo) {
AlertDialog.Builder downloadDialog = new AlertDialog.Builder(act);
downloadDialog.setTitle(title);
downloadDialog.setMessage(message);
downloadDialog.setPositiveButton(buttonYes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogInterface, int i) {
Uri uri = Uri.parse("market://search?q=pname:" + "com.google.zxing.client.android");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
try {
act.startActivity(intent);
} catch (ActivityNotFoundException anfe) {
}
}
});
downloadDialog.setNegativeButton(buttonNo, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialogInterface, int i) {
}
});
return downloadDialog.show();
}
//on ActivityResult method
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if (result != null) {
if (result.getContents() == null) {
Log.d("MainActivity", "Cancelled scan");
Toast.makeText(this, "Cancelled", Toast.LENGTH_LONG).show();
} else {
Log.d("MainActivity", "Scanned");
Toast.makeText(this, "Scanned: " + result.getContents(), Toast.LENGTH_LONG).show();
}
} else {
Log.d("MainActivity", "Weird");
// This is important, otherwise the result will not be passed to the fragment
super.onActivityResult(requestCode, resultCode, data);
}
}
}

How to respond to 'attach' from Gmail? Responding to intent with attachment

I am writing an android application. This app is built on top of Gmail. I want to add the ability to attach files from other apps. The first app I am working on doing this with is a custom Box app (made with the box sdk). I can currently send an intent, open an activity in the Box app, pick an attachment, and return. However, in my Box-SDK app, once an item is selected, I have no idea how to turn it into data that I can properly send back to my Gmail app (or any original sender of the intent). I also do not know how to send that data back to the originator of the intent.
I know setResult() is involved, but I am not sure where to put it or how to properly use it to carry the data chosen in box into the email app.
What's currently happening is it just goes back into gmail without an attachment and says that the download has finished.
Here is the code I currently have:
private void onFileSelected(final int resultCode, final Intent data) {
if (Activity.RESULT_OK != resultCode) {
Toast.makeText(this, "fail", Toast.LENGTH_LONG).show();
}
else {
final BoxAndroidFile file = data.getParcelableExtra(FilePickerActivity.EXTRA_BOX_ANDROID_FILE);
AsyncTask<Null, Integer, Null> task = new AsyncTask<Null, Integer, Null>() {
#Override
protected void onPostExecute(Null result) {
Toast.makeText(MainActivity.this, "done downloading", Toast.LENGTH_LONG).show();
// Intent result2 = new Intent();
// result2.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + ));
// setResult(Activity.RESULT_OK, result2);
//// setResult(resultCode, data);
super.onPostExecute(result);
finish();
}
#Override
protected void onPreExecute() {
Toast.makeText(MainActivity.this, "start downloading", Toast.LENGTH_LONG).show();
super.onPreExecute();
}
#Override
protected Null doInBackground(Null... params) {
BoxAndroidClient client = ((HelloWorldApplication) getApplication()).getClient();
try {
File f = new File(Environment.getExternalStorageDirectory(), file.getName());
Intent result2 = new Intent();
result2.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + f.getAbsolutePath()));
setResult(Activity.RESULT_OK, data);
// setResult(resultCode, data);
System.out.println(f.getAbsolutePath());
client.getFilesManager().downloadFile(file.getId(), f, null, null);
}
catch (Exception e) {
}
return null;
}
};
task.execute();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == AUTH_REQUEST) {
onAuthenticated(resultCode, data);
}
else if (requestCode == UPLOAD_REQUEST) {
onFolderSelected(resultCode, data);
}
else if (requestCode == DOWNLOAD_REQUEST) {
onFileSelected(resultCode, data);
}
}
Try using a file provider:
https://developer.android.com/reference/android/support/v4/content/FileProvider.html#ServeUri
From the page:
"There are a variety of ways to serve the content URI for a file to a client app. One common way is for the client app to start your app by calling startActivityResult(), which sends an Intent to your app to start an Activity in your app. In response, your app can immediately return a content URI to the client app or present a user interface that allows the user to pick a file. In the latter case, once the user picks the file your app can return its content URI. In both cases, your app returns the content URI in an Intent sent via setResult()"
From another stackoverflow answer:
public void showCameraScreen(View view) {
// BUILT IN CAMERA
Intent camera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
camera.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(getTempFile(this)) );
this.startActivityForResult(camera, 1);
}
private File getTempFile(Context context) {
// it will return /sdcard/MyImage.tmp
final File path = new File(Environment.getExternalStorageDirectory(), context.getPackageName());
if (!path.exists()) {
path.mkdir();
}
return new File(path, "MyImage.tmp");
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1 && resultCode == RESULT_OK) {
final File file = getTempFile(this);
byte[] _data = new byte[(int) file.length()];
try {
InputStream in = new FileInputStream(file);
in.read(_data);
in.close();
in = null;
//DO WHAT YOU WANT WITH _data. The byte array of your image.
} catch (Exception E) {
}
}
}
We can modify this code, where it says //Do What you want with the _data, just call
public final void setResult (int resultCode, Intent data)

Android's camera doesn't go back to my app when photo has been taken

It even can't make a folder on the sdcard. When the camera takes the photo, it doesn't respond when I press the 'OK' Button. What's wrong with my code?
public static final String MACCHA_PATH = Environment.getExternalStorageDirectory().getPath() + "/Twigit";
public static final String PHOTO_PATH = MACCHA_PATH + "/camera.jpg";
public static boolean takePhoto(Activity activity) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File fileDir = new File(MACCHA_PATH);
boolean isSuccessful = true;
if (!fileDir.exists()) {
isSuccessful = fileDir.mkdir();
}
if(!isSuccessful) {
return false;
} else {
File file = new File(PHOTO_PATH);
Uri outputFileUri = Uri.fromFile(file);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
activity.startActivityForResult(intent, TAKEPHOTO);
return true;
}
}
do you have this? You need to override the onActivityResult. which will be called before onResume when you use startActivityForResult. The requestCode will be the code you used to start the photo taking activity. In your case it would be TAKEPHOTO..
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == TAKEPHOTO) {
if (resultCode == RESULT_OK) {
//Pic taken
} else {
//Pic not taken
}
}
}
EDIT:
take a look at this link
http://achorniy.wordpress.com/2010/04/26/howto-launch-android-camera-using-intents/

Categories

Resources