OnActivityResult() not being called when using a Camera intent? - c#

I am trying to make a page (lets call it page #2) within my Xamarin.Android app that has a button, when you press on that button the camera app opens. Then you take a picture, accept the picture you took, and then you should be brought back to the page that originally had the camera button on it (page #2), and the image you took will be displayed there under the button.
The problem is that my overridden OnActivityResult() method never gets called after you accept the picture. You press the button, the camera app opens, you take a picture, accept the picture, the camera app closes and you are brought back to the page BEFORE the camera button page (so you're on page #1 now). I think the camera app itself is crashing? I am not sure how to find those logs. Sometimes a popup will show that says "Unfortunately, the camera has stopped." I am guessing that it is the way I am trying to save the photo taken to the phone itself? The only errors I have seen are within the device log, and occasionally it will throw a permission error saying I dont have access to write to the storage device on the phone, but I have the "write" permissions in the manifest file. I am targeting API 26.
Any ideas?
Here is the method for clicking the button:
private int PIC_REQUEST_CODE = 1;
private ImageView imageView;
private Java.IO.File image;
private void Button_Click(object sender, EventArgs e)
{
Intent takePictureIntent = new Intent(MediaStore.ActionImageCapture);
if (takePictureIntent.ResolveActivity(PackageManager) != null)
{
try
{
// Create an image file name
string timeStamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
string imageFileName = "JPEG_" + timeStamp + "_";
Java.IO.File storageDir = GetExternalFilesDir(global::Android.OS.Environment.DirectoryPictures);
Java.IO.File image = Java.IO.File.CreateTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
global::Android.Net.Uri photoURI = FileProvider.GetUriForFile(ApplicationContext, PackageName + ".fileprovider", image);
takePictureIntent.PutExtra(MediaStore.ExtraOutput, photoURI);
takePictureIntent.AddFlags(ActivityFlags.GrantWriteUriPermission);
StartActivityForResult(takePictureIntent, PIC_REQUEST_CODE);
}
catch (Exception ex)
{
//Not sure what to do, but here's a break point at least.
Toast.MakeText(this, "blah blah something went wrong", ToastLength.Short).Show();
}
}
}
And here is my simple OnActivityResult that gets skipped/ never called:
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
if (requestCode == PIC_REQUEST_CODE && resultCode == Result.Ok)
{
Bitmap bitmap = (Bitmap)data.Extras.Get("data");
imageView.SetImageBitmap(bitmap);
}
}

I figured out the current issue of the OnActivityResult() not being called. At the top above the class I was declaring
[Activity(Label = "#string/page_name", ScreenOrientation = ScreenOrientation.Portrait, NoHistory = true)]
The important note here is the "NoHistory=True" apparently that makes the page exit and return back to the previous page. Deleting the "NoHistory=true" worked and made it so the OnActivityResult() gets called. Some documentation about it says "A value of 'true' means that the activity will not leave a historical trace. It will not remain in the activity stack for the task, so the user will not be able to return to it" so I guess that makes sense.

Related

Video player in Xamarin Forms is gone when minimized and resumed

I have implemented a video player in xamarin forms to play as a background video for my login screen. The video successfully loads at start and plays without sound and loops(as configured). But the problem is when I minimize the app and resume again, then the video is gone and I can't even play it back like video.play if paused or stopped
The links I tried are as follows
http://makanda.io/how-to-create-a-background-video-in-xamarin-forms/ (I tried this link first)
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/custom-renderer/video-player/ (then completely replaced with this)
Both links work as desired except the issue which is the same in both links.
What I have tried to solve the issue?
I tried resuming the video as if(videoPlayer.Status == Renderers.VideoStatus.Paused) videoPlayer.Play() inside my OnAppearing() method. But it doesn't work
I am currently trying to dynamically load the video from code behind inside a stack layout. But I don't know how to set the video source from code behind. I am doing it as follows
string source = "";
switch (Device.RuntimePlatform)
{
case Device.iOS:
source = "Videos/walkthroughvideo9_16.mp4";
break;
case Device.Android:
source = "walkthroughvideo9_16.mp4";
break;
default:
source = "walkthroughvideo9_16.mp4";
break;
}
VideoPlayer video = new VideoPlayer()
{
Source = (UriVideoSource)source
};
An error says, cannot convert string to UriVideoSource
AN HELP LINK:
https://developer.android.com/reference/android/widget/VideoView
Seems like the video view does not maintain its state when the app goes into the background.
I am currently trying to dynamically load the video from code behind inside a stack layout. But I don't know how to set the video source from code behind. I am doing it as follows
I notice that your path is from local Resource, you can use the following code to load the video. If your path is an URL, you can use videoPlayer.Source = VideoSource.FromUri(source); to load it.
var videoPlayer=new VideoPlayer();
string source = "";
switch (Device.RuntimePlatform)
{
case Device.iOS:
source = "Videos/iOSApiVideo.mp4";
break;
case Device.Android:
source = "AndroidApiVideo.mp4";
break;
default:
source = "AndroidApiVideo.mp4";
break;
}
videoPlayer.Source = VideoSource.FromResource(source);
I tried resuming the video as if(videoPlayer.Status == Renderers.VideoStatus.Paused) videoPlayer.Play() inside my OnAppearing() method. But it doesn't work
Here is my running demo result, when I am back to my application, it works. I also achieve the seek to the previous time span function as well.
Here is my demo.
https://drive.google.com/file/d/1kpYbMV5mA3UdbGD7r6tLv1S2ix7AwM5B/view?usp=sharing
I used a timer as a workaround to resume the video which pauses when the app goes into sleep mode.
protected override void OnAppearing()
{
base.OnAppearing();
shouldTimerRun = true;
Device.StartTimer(new TimeSpan(0, 0, 1), () =>
{
Device.BeginInvokeOnMainThread(() =>
{
if (videoPlayer.Status == VideoStatus.Paused)
{
//videoPlayer.TimeToEnd = timeSpan;
videoPlayer.Position = timeSpan;
videoPlayer.Play();
}
});
return shouldTimerRun;
});
}
protected override void OnDisappearing()
{
base.OnDisappearing();
shouldTimerRun = false;
timeSpan = videoPlayer.Position;
}

How can I pop a navigation page when a value changes in a static class?

So I have a bit of a weird problem.
I've implemented a camera preview class (largely following this code here: https://learn.microsoft.com/en-us/samples/xamarin/xamarin-forms-samples/customrenderers-view/) but have added a button at the bottom to take a picture. This involves the use of both some xamarin forms code and some xamarin android code.
However, the CapturePage is only put on the stack when the user announces that they want to take a photo, and after the photo has been taken, I want to pop the Capture page to go back to the main screen. Currently, I have a static boolean value in the overall project that is changed from false to true when a capture has occurred. Is there some way to get my code in Main.xaml.cs to wait on this value changing, then pop whatever is on top of the navigation stack? Is this a use for a property changed? See code below:
The code in Project.Droid that handles the capturing and saving of the image:
void OnCapButtonClicked(object sender, EventArgs e)
{
// capButton.capture is an instance of Android.Hardware.Camera
capButton.capture.TakePicture(null, null, this);
// stop the preview when the capture happens
CameraInfoContainer.isPreviewing = false;
}
public void OnPictureTaken(byte[] data, Camera camera)
{
var filepath = string.Empty;
var clientInstanceId = Guid.NewGuid().ToString();
var saveLoc = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryPictures);
filepath = System.IO.Path.Combine(saveLoc.AbsolutePath, clientInstanceId + ".jpg");
try
{
System.IO.File.WriteAllBytes(filepath, data);
//mediascan adds the saved image into the gallery
var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
mediaScanIntent.SetData(Android.Net.Uri.FromFile(new File(filepath)));
Forms.Context.SendBroadcast(mediaScanIntent);
}
catch (Exception e)
{
System.Console.WriteLine(e.ToString());
}
// CameraInfoContainer is a static class in Project NOT in Project.Droid
CameraInfoContainer.savedCapture = filepath;
CameraInfoContainer.capType = CaptureType.Photo;
CameraInfoContainer.captureComplete = true; // here is where I set the value (in Project)
}
Now the code in Project that pushes the capture page on the stack and that I want to trigger when the capture has happened:
// this method puts the capture page on the stack and starts the whole process
private async Task ExecuteNewCapture()
{
var cp = new CapturePage();
var np = new NavigationPage(cp);
await Navigation.PushModalAsync(np, true);
}
// this is the method that I want to trigger when a photo is taken (in Project/Main.xaml.cs)
private async Task Complete(string fileLoc)
{
await Navigation.PopModalAsync();
}
Answer is in a comment from Junior Jiang. Ended up using Xamarin.Forms.MessagingCenter to get done what I needed.

App crashing if I hide a few controls when a page loads without using MessageDialog

I have an app in which I have a setting which allows the user to stop the app's access to their location. This is stored in Windows.Storage.ApplicationData.Current.RoamingSettings.Values["location"]. If the location service + this setting allows access then I load a page with the map open. If the setting allows access and the location services are Off a message is displayed and I hide a few controls when the page loads. If the setting is off then I just want to hide the controls without any message.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
.....
// MUST ENABLE THE LOCATION CAPABILITY!!!
var locator = new Geolocator();
locator.DesiredAccuracyInMeters = 50;
locator.ReportInterval = (uint)TimeSpan.FromSeconds(15).TotalMilliseconds;
setloc(locator);
this.navigationHelper.OnNavigatedTo(e);
}
public async void setloc(Geolocator locator)
{
if (locator.LocationStatus != PositionStatus.Disabled && (bool)Windows.Storage.ApplicationData.Current.RoamingSettings.Values["location"]==true)
{
var position = await locator.GetGeopositionAsync();
await MyMap.TrySetViewAsync(position.Coordinate.Point, 16D);
....
return;
}
else if (locator.LocationStatus == PositionStatus.Disabled && (bool)Windows.Storage.ApplicationData.Current.RoamingSettings.Values["location"] == true)
{
MessageDialog msgbox = new MessageDialog("Location Services are turned off. Please turn them on to save Location while saving a Tip", "Location Unavailable");
await msgbox.ShowAsync();
savebutton.Visibility = Visibility.Collapsed;
myMapBlock.Visibility = Visibility.Collapsed;
return;
}
***// MessageDialog msgbox1 = new MessageDialog("Location Services are turned off. Please turn them on to save Location while saving a Tip", "Location Unavailable");
// await msgbox1.ShowAsync();***
savebutton.Visibility = Visibility.Collapsed;
myMapBlock.Visibility = Visibility.Collapsed;
}
Everything is fine when that setting is on(true) but when it's off(false), something strange happens.
The code above doesn't work. It causes the app to crash but when I uncomment the part which is within *** in the code, the message is displayed and the page is loaded properly. If I just try to hide the myMapBlock and the savebutton without using the MessageDialog, it crashes.
I want to hide the controls without using the MessageDialog. How can I do that?
Can you change following line:
setloc(locator);
to:
await Windows.UI.Core.CoreWindow.GetForCurrentThread().Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => { await setloc(locator); });
(change signature of void setloc method to Task)
In my opinion it looks like page is not loaded yet, MessageDialog can not be displayed. Dispatcher.RunAsync should enqueue this action and it should be proccessed after correct page initialization.
Also the base .OnNavigatedTo(..) call should be made before your location-messagedialog code.
That's my guess - can you provide a crash stacktrace?

Capture Image From Camera set to ImageView

How to upload or add an Image to UIImageView directly from iPhone/Ipad Captured camera Image.
I have uploaded an image to UIImageView from photo library.
Now, I want upload an image directly after taken an image through camera to ImageView.
Please suggest me how to implement this.
using IOS 8.0
This can be accomplished very easily with the Xamarin.Mobile component, which is free and works with all platforms.
http://components.xamarin.com/view/xamarin.mobile
From the example they give:
using Xamarin.Media;
// ...
var picker = new MediaPicker ();
if (!picker.IsCameraAvailable)
Console.WriteLine ("No camera!");
else {
try {
MediaFile file = await picker.TakePhotoAsync (new StoreCameraMediaOptions {
Name = "test.jpg",
Directory = "MediaPickerSample"
});
Console.WriteLine (file.Path);
} catch (OperationCanceledException) {
Console.WriteLine ("Canceled");
}
}
After you take the picture, it is saved to the directory you specified, with the name you specified. To easily retrieve this picture and display it with your ImageView using the above example you can do the following:
//file is declared above as type MediaFile
UIImage image = new UIImage(file.Path);
//Fill in with whatever your ImageView is
yourImageView.Image = image;
Edit:
Just a note that the above needs to be asynchronous. So if you want to launch the camera from a button call, for instance, you just slightly modify the .TouchUpInside event:
exampleButton.TouchUpInside += async (object sender, EventArgs e) => {
//Code from above goes in here, make sure you have async after the +=
};
Otherwise you could wrap the code from above in a function and add async to that:
public async void CaptureImage()
{
//Code from above goes here
}
You will need to use AVFoundation to do this. Check out the AVCam sample project in Xcode:
https://developer.apple.com/library/ios/samplecode/AVCam/Introduction/Intro.html

Prepare data before go to the main view

When the user touch the app icon,
I want do these steps before user go to the main view
Fetch json string from URI
Use JArray.Parse to get the value
After all finish, go to the main view.
The problem is how can I prevent user to go to the main view
and put all the code
I tried to put it in Application_Launching method in the App.xaml.cs file
// Code to execute when the application is launching (eg, from Start)
// This code will not execute when the application is reactivated
private void Application_Launching(object sender, LaunchingEventArgs e)
{
// code here
}
But it doesn't prevent the program to go to the main view before the fetching finished.
And I found that actually in the MainPage.xaml, if I put this code like this
protected override void OnNavigatedTo(NavigationEventArgs e)
{
while(true) {}
// it will prevent the program to go to the main view,
// and will stick with the loading screen until this function reach its end
}
So I think, I can put the all the code here, when I finish the fetch, I will just break the while and it will go to the main view automatically.
And I try, this is the code
protected override void OnNavigatedTo(NavigationEventArgs e)
{
bool isFetchFinished = false;
ObservableCollection<PromoViewModel> Promos = new ObservableCollection<PromoViewModel>();
WebClient client = new WebClient();
client.DownloadStringCompleted += (s, evt) =>
{
if (evt.Error == null)
{
// Retrieve the JSON
string jsonString = evt.Result;
JArray promos = JArray.Parse(jsonString);
foreach (JObject promo in promos)
{
string name = promo["name"].Value<string>();
string description = promo["description"].Value<string>();
string img = promo["image"].Value<string>();
Promos.Add(new PromoViewModel() { Name = name, Description = description, Img = img });
}
isFetchFinished = true;
System.Diagnostics.Debug.WriteLine("finish fetch");
}
};
// run
client.DownloadStringAsync(new Uri("the json url"));
while(true) {
if(isFetchFinished) {
App.ViewModel.LoadData(Promos); // pass value to main view model
break; // after complete, break
}
}
}
I thought it would work, but it was not.
This is what I found,
The WebClient DownloadStringAsync won't run until the OnNavigatedTo function finished.
Because it's still waiting for the while loop to break and reach the end function.
And this
isFetchFinished = true; // will never executed
Resulting infinite loop.
I think I put the fetch code in the wrong method. Where is the right place to put all of this?
Ouch, you are doing it all wrong. First of all, you have to specify the starting page. If you want to download some data before navigating to it, you can create a special "download" page that is actually the first page navigated to when starting the application. And then, once the download is completed, you navigate to your main page. This is actually a replacement for the extended splash screen.
Also, never put while (true) in any UI code, that will simply freeze the application. Besides, if the application is frozen, you never get the chance to "unfreeze" it.

Categories