Xamarin.Forms Xamarin Android - xamarin

SaveFileDialog in XamarinForms
My requirement is to save a file in Xamarin.Forms like below image and what ever I tried so far is:
public class CustomWebViewRenderer : WebViewRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged (e);
if (e.NewElement != null)
{
var customWebView = Element as CustomWebView;
Control.Settings.AllowUniversalAccessFromFileURLs = true;
string root = Android.OS.Environment.ExternalStorageDirectory.ToString();
// Java.IO.File myDir = new Java.IO.File(root + "/WingsPdfs");
// myDir.Mkdir();
string path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Java.IO.File file = new Java.IO.File(path, "WingsPdfGenerated.pdf");
FileOutputStream outs = new FileOutputStream(file);
outs.Write(customWebView.PdfStream.ToArray());
outs.Flush();
outs.Close();
try
{
Control.LoadUrl(string.Format("file:///android_asset/pdfjs/web/viewer.html?file={0}", file));
}
catch(Exception ex)
{
}
}
}
}
In the above code I hardcoded the location of the file but I want user to select particular location and save to the selected location.
Thank You
enter image description here

Related

Xamarin Camera2 does not save second picture

I use xamarin camera2 api.
my project save photo on same name ( override on one image ).
I set new filename for that but it override on first picture.
public override void OnActivityCreated(Bundle savedInstanceState)
{
base.OnActivityCreated(savedInstanceState);
mFile = getOutputMediaFile();
mCaptureCallback = new CameraCaptureListener(this);
mOnImageAvailableListener = new ImageAvailableListener(this, mFile);
}
public void OnClick(View v)
{
//button Take photo code :
if (v.Id == Resource.Id.picture)
{
TakePicture();
mFile = getOutputMediaFile();
}
}
private File getOutputMediaFile()
{
File mediaStorageDir = new File(Activity.GetExternalFilesDir(null), "Pictures");
if (!mediaStorageDir.Exists())
{
if (!mediaStorageDir.Mkdirs())
{
Log.Debug("Pictures", "failed to create Dir");
return null;
}
}
File mediaFile= new File(mediaStorageDir.Path, "pic.jpg");
int counter = 1;
while (mediaFile.Exists())
{
mediaFile = new File(mediaStorageDir.Path, "pic" + counter.ToString() + ".jpg");
counter++;
}
return mediaFile;
}
first time : I take a picture it's work and show message : saved image path/pic.jpg
second time : when I take picture it show message : saved image path/pic1.jpg , but pic1.jpg not created and image saved on pic.jpg
I use visual studio debugging in getOutputMediaFile() mFile = pic1.jpg but in ImageAvailableListener mFile = pic.jpg
public ImageAvailableListener(Camera2BasicFragment fragment, File file)
{
if (fragment == null)
throw new System.ArgumentNullException("fragment");
if (file == null)
throw new System.ArgumentNullException("file");
owner = fragment;
this.file = file;
}

Get and Display path of Chosen photo from gallery in Xamarin forms

I am using a Dependency service to pick a photo from the gallery. and I want to show the path when the user selects an image from their phone in a Label.
I have read too many logs but not getting the proper results.
I want it like this:
Now the selected image is displayed properly but what I don't get is how to display the path of the selected image.
Please suggest me how to do it for both android and ios.
Note: I'm using Dependency service for it so I don't want third-party plugins.
I hope I will get a better solution for this.
Thanks in advance.
Creating the interface in forms
namespace xxx
{
public interface IPhotoPickerService
{
Task<Dictionary<string,Stream>> GetImageStreamAsync();
}
}
in iOS
[assembly: Dependency (typeof (PhotoPickerService))]
namespace xxx.iOS
{
public class PhotoPickerService : IPhotoPickerService
{
TaskCompletionSource<Dictionary<string, Stream>> taskCompletionSource;
UIImagePickerController imagePicker;
Task<Dictionary<string, Stream>> IPhotoPickerService.GetImageStreamAsync()
{
// Create and define UIImagePickerController
imagePicker = new UIImagePickerController
{
SourceType = UIImagePickerControllerSourceType.PhotoLibrary,
MediaTypes = UIImagePickerController.AvailableMediaTypes(UIImagePickerControllerSourceType.PhotoLibrary)
};
// Set event handlers
imagePicker.FinishedPickingMedia += OnImagePickerFinishedPickingMedia;
imagePicker.Canceled += OnImagePickerCancelled;
// Present UIImagePickerController;
UIWindow window = UIApplication.SharedApplication.KeyWindow;
var viewController = window.RootViewController;
viewController.PresentModalViewController(imagePicker, true);
// Return Task object
taskCompletionSource = new TaskCompletionSource<Dictionary<string, Stream>>();
return taskCompletionSource.Task;
}
void OnImagePickerFinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs args)
{
UIImage image = args.EditedImage ?? args.OriginalImage;
if (image != null)
{
// Convert UIImage to .NET Stream object
NSData data;
if (args.ReferenceUrl.PathExtension.Equals("PNG") || args.ReferenceUrl.PathExtension.Equals("png"))
{
data = image.AsPNG();
}
else
{
data = image.AsJPEG(1);
}
Stream stream = data.AsStream();
UnregisterEventHandlers();
Dictionary<string, Stream> dic = new Dictionary<string, Stream>();
dic.Add(args.ImageUrl.ToString(), stream);
// Set the Stream as the completion of the Task
taskCompletionSource.SetResult(dic);
}
else
{
UnregisterEventHandlers();
taskCompletionSource.SetResult(null);
}
imagePicker.DismissModalViewController(true);
}
void OnImagePickerCancelled(object sender, EventArgs args)
{
UnregisterEventHandlers();
taskCompletionSource.SetResult(null);
imagePicker.DismissModalViewController(true);
}
void UnregisterEventHandlers()
{
imagePicker.FinishedPickingMedia -= OnImagePickerFinishedPickingMedia;
imagePicker.Canceled -= OnImagePickerCancelled;
}
}
}
in Android
in MainActivity
public class MainActivity : FormsAppCompatActivity
{
...
// Field, property, and method for Picture Picker
public static readonly int PickImageId = 1000;
public TaskCompletionSource<Dictionary<string,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);
Dictionary<string, Stream> dic = new Dictionary<string, Stream>();
dic.Add(uri.ToString(), stream);
// Set the Stream as the completion of the Task
PickImageTaskCompletionSource.SetResult(dic);
}
else
{
PickImageTaskCompletionSource.SetResult(null);
}
}
}
}
[assembly: Dependency(typeof(PhotoPickerService))]
namespace xxx.Droid
{
public class PhotoPickerService : IPhotoPickerService
{
public Task<Dictionary<string,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 Picture"),
MainActivity.PickImageId);
// Save the TaskCompletionSource object as a MainActivity property
MainActivity.Instance.PickImageTaskCompletionSource = new TaskCompletionSource<Dictionary<string,Stream>>();
// Return Task object
return MainActivity.Instance.PickImageTaskCompletionSource.Task;
}
}
}
invoke it
Dictionary<string, Stream> dic = await DependencyService.Get<IPhotoPickerService>().GetImageStreamAsync();
Stream stream;
string path;
foreach ( KeyValuePair<string, Stream> currentImage in dic )
{
stream = currentImage.Value;
path = currentImage.Key;
label.Text = path;
if (stream != null)
{
image.Source = ImageSource.FromStream(() => stream);
}
}
Update
If you want to get the path , you could invoke
Dictionary<string, Stream> dic = new Dictionary<string, Stream>();
dic.Add(uri.Path, stream);

Insert Image into PDF Document

Is it possible to add an image to the PDF document? My layout has one (Button) and one (ImageView), I would like that when I click (Button), it will open the Gallery to select the image, show it in (ImageView) and add it to the PDF document, as if it were a generator curriculum, thank you in advance.
*Using com.itextpdf:itextg:5.5.10
here is the complete code of what you wanted. Try this and let me know.
In the onCreate method:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_to_pdf);
imageView = findViewById(R.id.imageView);
galleryBtn = findViewById(R.id.gallery);
convertBtn = findViewById(R.id.convert);
galleryBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
String imageFileName = "PDF_" + timeStamp + "_";
File storageDir = getAlbumDir();
try {
pdfPath = File.createTempFile(
imageFileName, /* prefix */
".pdf", /* suffix */
storageDir /* directory */
);
} catch (IOException e) {
e.printStackTrace();
}
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(photoPickerIntent, GALLERY_INTENT);
}
});
convertBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (bitmap == null) {
Toast.makeText(ImageToPDF.this, "Please select the image from gallery", Toast.LENGTH_LONG).show();
} else {
convertToPDF(pdfPath);
}
}
});
}
Create a directory for PDF file:
private File getAlbumDir() {
File storageDir = null;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
storageDir = new File(Environment.getExternalStorageDirectory()
+ "/dcim/"
+ "Image to pdf");
if (!storageDir.mkdirs()) {
if (!storageDir.exists()) {
Log.d("CameraSample", "failed to create directory");
return null;
}
}
} else {
Log.v(getString(R.string.app_name), "External storage is not mounted READ/WRITE.");
}
return storageDir;
}
On camera activity intent result:
#Override
protected void onActivityResult(int requestCode, int resultCode, #Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == GALLERY_INTENT) {
if (resultCode == Activity.RESULT_OK && data != null) {
Uri selectedImage = data.getData();
String[] filePathColumn = {MediaStore.Images.Media.DATA};
if (selectedImage != null) {
Cursor cursor = getContentResolver().query(selectedImage,
filePathColumn, null, null, null);
if (cursor != null) {
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String imagePath = cursor.getString(columnIndex);
bitmap = BitmapFactory.decodeFile(imagePath);
imageView.setImageBitmap(bitmap);
cursor.close();
}
}
} else if (resultCode == Activity.RESULT_CANCELED) {
Log.e("Canceled", "Image not selected");
}
}
}
Now code to convert image to PDF and save to the directory:
private void convertToPDF(File pdfPath) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
PdfDocument pdfDocument = new PdfDocument();
PdfDocument.PageInfo pageInfo = new PdfDocument.PageInfo.Builder(width, height, 1).create();
PdfDocument.Page page = pdfDocument.startPage(pageInfo);
Canvas canvas = page.getCanvas();
Paint paint = new Paint();
paint.setColor(Color.parseColor("#ffffff"));
canvas.drawPaint(paint);
bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
paint.setColor(Color.BLUE);
canvas.drawBitmap(bitmap, 0, 0, null);
pdfDocument.finishPage(page);
try {
pdfDocument.writeTo(new FileOutputStream(pdfPath));
Toast.makeText(ImageToPDF.this, "Image is successfully converted to PDF", Toast.LENGTH_LONG).show();
} catch (IOException e) {
e.printStackTrace();
}
pdfDocument.close();
}

Xamarin video player unable to play video from simulator document folder

Hi i implemented the xamarin video player which is described the following link https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/video-player/
i am downloading videos through code to the documents folder of the application which has a path like this
[/Users/vaibhavjain/Library/Developer/CoreSimulator/Devices/{GUID}/data/Containers/Data/Application/{GUID}/Documents/MediaDocuments/Nature1.mp4]
now i checked that videos are downloaded correctly to this path but when i am passing this path as source to the video player above it is not able to play it
my guess is that the player is not able to reach the path or it is expecting a relative path but i am unable to find any example of the type of path i should provide.
here is the code for custom Renderer on ios
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, UIView>
{
AVPlayer player;
AVPlayerItem playerItem;
AVPlayerViewController _playerViewController; // solely for ViewController property
public override UIViewController ViewController => _playerViewController;
protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)
{
base.OnElementChanged(args);
if (args.NewElement != null)
{
if (Control == null)
{
// Create AVPlayerViewController
_playerViewController = new AVPlayerViewController();
// Set Player property to AVPlayer
player = new AVPlayer();
_playerViewController.Player = player;
var x = _playerViewController.View;
// Use the View from the controller as the native control
SetNativeControl(_playerViewController.View);
}
SetAreTransportControlsEnabled();
SetSource();
args.NewElement.UpdateStatus += OnUpdateStatus;
args.NewElement.PlayRequested += OnPlayRequested;
args.NewElement.PauseRequested += OnPauseRequested;
args.NewElement.StopRequested += OnStopRequested;
}
if (args.OldElement != null)
{
args.OldElement.UpdateStatus -= OnUpdateStatus;
args.OldElement.PlayRequested -= OnPlayRequested;
args.OldElement.PauseRequested -= OnPauseRequested;
args.OldElement.StopRequested -= OnStopRequested;
}
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (player != null)
{
player.ReplaceCurrentItemWithPlayerItem(null);
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs args)
{
base.OnElementPropertyChanged(sender, args);
if (args.PropertyName == VideoPlayer.AreTransportControlsEnabledProperty.PropertyName)
{
SetAreTransportControlsEnabled();
}
else if (args.PropertyName == VideoPlayer.SourceProperty.PropertyName)
{
SetSource();
}
else if (args.PropertyName == VideoPlayer.PositionProperty.PropertyName)
{
TimeSpan controlPosition = ConvertTime(player.CurrentTime);
if (Math.Abs((controlPosition - Element.Position).TotalSeconds) > 1)
{
player.Seek(CMTime.FromSeconds(Element.Position.TotalSeconds, 1));
}
}
}
void SetAreTransportControlsEnabled()
{
((AVPlayerViewController)ViewController).ShowsPlaybackControls = Element.AreTransportControlsEnabled;
}
void SetSource()
{
AVAsset asset = null;
if (Element.Source is UriVideoSource)
{
string uri = (Element.Source as UriVideoSource).Uri;
if (!String.IsNullOrWhiteSpace(uri))
{
asset = AVAsset.FromUrl(new NSUrl(uri));
}
}
else if (Element.Source is FileVideoSource)
{
string uri = (Element.Source as FileVideoSource).File;
if (!String.IsNullOrWhiteSpace(uri))
{
asset = AVAsset.FromUrl(NSUrl.CreateFileUrl(uri, null));
}
}
else if (Element.Source is ResourceVideoSource)
{
string path = (Element.Source as ResourceVideoSource).Path;
if (!String.IsNullOrWhiteSpace(path))
{
string directory = Path.GetDirectoryName(path);
string filename = Path.GetFileNameWithoutExtension(path);
string extension = Path.GetExtension(path).Substring(1);
NSUrl url = NSBundle.MainBundle.GetUrlForResource(filename, extension, directory);
asset = AVAsset.FromUrl(url);
}
}
if (asset != null)
{
playerItem = new AVPlayerItem(asset);
}
else
{
playerItem = null;
}
player.ReplaceCurrentItemWithPlayerItem(playerItem);
if (playerItem != null && Element.AutoPlay)
{
player.Play();
}
}
// Event handler to update status
void OnUpdateStatus(object sender, EventArgs args)
{
VideoStatus videoStatus = VideoStatus.NotReady;
switch (player.Status)
{
case AVPlayerStatus.ReadyToPlay:
switch (player.TimeControlStatus)
{
case AVPlayerTimeControlStatus.Playing:
videoStatus = VideoStatus.Playing;
break;
case AVPlayerTimeControlStatus.Paused:
videoStatus = VideoStatus.Paused;
break;
}
break;
}
((IVideoPlayerController)Element).Status = videoStatus;
if (playerItem != null)
{
((IVideoPlayerController)Element).Duration = ConvertTime(playerItem.Duration);
((IElementController)Element).SetValueFromRenderer(VideoPlayer.PositionProperty, ConvertTime(playerItem.CurrentTime));
}
}
TimeSpan ConvertTime(CMTime cmTime)
{
return TimeSpan.FromSeconds(Double.IsNaN(cmTime.Seconds) ? 0 : cmTime.Seconds);
}
// Event handlers to implement methods
void OnPlayRequested(object sender, EventArgs args)
{
player.Play();
}
void OnPauseRequested(object sender, EventArgs args)
{
player.Pause();
}
void OnStopRequested(object sender, EventArgs args)
{
player.Pause();
player.Seek(new CMTime(0, 1));
}
}
I don't see your code and can't know what is your problem. I would show you how to make it work about play a local video stored in the document folder:
In xamarin.forms:
public class MyVideoView : View
{
}
And in xaml, simply use this MyVideoView :
<StackLayout>
<!-- Place new controls here -->
<local:MyVideoView HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
</StackLayout>
In the iOS project, the custom renderer should be:
public class VideoPlayerRenderer : ViewRenderer
{
AVPlayer player;
AVPlayerItem playerItem;
AVPlayerViewController _playerViewController; // solely for ViewController property
public override UIViewController ViewController => _playerViewController;
protected override void OnElementChanged(ElementChangedEventArgs<View> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
if (Control == null)
{
// Create AVPlayerViewController
_playerViewController = new AVPlayerViewController();
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var path = Path.Combine(documents, "iOSApiVideo.mp4");
NSUrl url = NSUrl.CreateFileUrl(path, null);
//if you put your video in the project directly use below code
//NSUrl url = NSBundle.MainBundle.GetUrlForResource("iOSApiVideo", "mp4");
// Set Player property to AVPlayer
player = new AVPlayer(url);
_playerViewController.Player = player;
// Use the View from the controller as the native control
SetNativeControl(_playerViewController.View);
player.Play();
}
}
}
}
Replace the path with your own path there and it will work.
Note:
Doesn't work
NSUrl url = new NSUrl(path);
Work
NSUrl url = NSUrl.CreateFileUrl(path, null);
I am posting the final answer because i ended up using Jack's answer to modify my customRenderer.
so i changed this condition in the SetSource() function in custom Renderer
else if (Element.Source is FileVideoSource)
{
string uri = (Element.Source as FileVideoSource).File;
if (!String.IsNullOrWhiteSpace(uri))
{
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var path = Path.Combine(documents, uri);
NSUrl url = NSUrl.CreateFileUrl(path, null);
asset = AVAsset.FromUrl(url);
}
}
the only change is now i am getting the path inside customRenderer and only passing fileName as the parameter from ViewModel. earlier it was full path like [/Users/vaibhavjain/Library/Developer/CoreSimulator/Devices/{GUID}/data/Containers/Data/Application/{GUID}/Documents/MediaDocuments/Nature1.mp4]

Image doesn't appear in custom view renderer

I've this cusom view in shared project:
public class CustomAdView : View
{
}
then I add it in the main page:
var adView = new CustomAdView();
Grid.SetRowSpan(adView, 4);
_mainGrid.Children.Add(adView);
// here if I add an Image instead, it shows without problem.
Then in the android project, I've a custom renderer for that CustomAdView like this:
[assembly: ExportRenderer(typeof(CustomAdView), typeof(CustomAdViewRenderer))]
namespace MyApp.Droid.Renderers
{
public class CustomAdViewRenderer : ViewRenderer<CustomAdView, Android.Views.View>
{
public CustomAdViewRenderer(Context context)
: base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<CustomAdView> e)
{
base.OnElementChanged(e);
if (e.NewElement != null && Control == null)
SetNativeControl(CreateAdView());
}
// ...
private Android.Views.View CreateAdView()
{
try
{
var url = RequestImageUrlFromServer();
var imageView = new ImageView(Context);
imageView.Click += (o, e) =>
{
Device.OpenUri(new Uri("http://www.google.com"));
};
imageView.SetImageURI(Android.Net.Uri.Parse(url));
imageView.LayoutParameters = new LinearLayout.LayoutParams(LayoutParams.FillParent, LayoutParams.FillParent);
return imageView;
}
catch (Exception)
{
return null;
}
}
}
}
CreateAdView() returns imageView correctly. If I click on the screen, it responds to click event and opens the browser, but the problem is that it doesn't show the image!
I guess I set ImageView source incorrectly. Image url is something like this:
https://addeals.s3.amazonaws.com/campaigns/pub8/testing/768x1024_hangman_test.jpg
Any help is much appreciated.
You can refer the following code
var iamgeview = new ImageView(Context);
URL url = new URL("xxx");
Bitmap pngBP = BitmapFactory.DecodeStream(url.OpenStream());
iamgeview.SetImageBitmap(pngBP);

Resources