Currently I am creating apps that allowed user to take photo and show it to an ImageView. It works like charm on Android 5.1.1 Sony M2 Dual.
But on Kitkat 4.4.2 Samsung Galaxy Tab 3 and KitKat 4.4.4 Xiaomi Redmi 2, the camera force close after capturing image.
I don't know if this will be useful or not, but I realized that the camera probably force close because after capturing, on those two KitKat device, user will be prompted to accept the captured picture or not, then if accepted, it will be back to my current Activity.
Beacuse on my Sony with 5.1.1, user will not be prompted for the captured picture, it will be straight back to my current Activity.
Here I included the corresponding code.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_job_order_line_form);
bAddPhoto = (TextView) findViewById(R.id.bAddPhoto);
bSaveJOLine = (TextView) findViewById(R.id.bSaveJOLine);
editDescription = (EditText) findViewById(R.id.editDescription);
editQty = (EditText) findViewById(R.id.editQty);
editPrice = (EditText) findViewById(R.id.editPrice);
ivImage = (ImageView) findViewById(R.id.imageTaken);
Intent intent = getIntent();
jobId = intent.getIntExtra("jobId", 0);
docNo = intent.getStringExtra("docNo");
bAddPhoto.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intentCamera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (intentCamera.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
// Error occurred while creating the File
Toast.makeText(OJobOrderLineFormActivity.this, "Create file failed!",
Toast.LENGTH_SHORT).show();
}
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(OJobOrderLineFormActivity.this,
"com.opentoko.opentokolaundry.fileprovider",
photoFile);
System.out.println(photoURI);
intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(intentCamera, 360);
}
}
}
});
bSaveJOLine.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Snackbar.make(v, "Item has been saved!", Snackbar.LENGTH_LONG)
.setAction("OK", null).show();
}
});
}
Create temporary file function :
private File createImageFile() throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmm").format(new Date());
String imageFileName = docNo + "_" + timeStamp + "_";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
System.out.println(storageDir);
System.out.println(image);
// Save a file: path for use with ACTION_VIEW intents
currentPhotoPath = image.getAbsolutePath();
System.out.println(currentPhotoPath);
return image;
}
onActivityResult :
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case 360:
if(resultCode == RESULT_OK) {
int targetW = ivImage.getWidth();
int targetH = ivImage.getHeight();
// Get the dimensions of the bitmap
BitmapFactory.Options bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
BitmapFactory.decodeFile(currentPhotoPath, bmOptions);
int photoW = bmOptions.outWidth;
int photoH = bmOptions.outHeight;
// Determine how much to scale down the image
int scaleFactor = Math.min(photoW/targetW, photoH/targetH);
// Decode the image file into a Bitmap sized to fill the View
bmOptions.inJustDecodeBounds = false;
bmOptions.inSampleSize = scaleFactor;
bmOptions.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeFile(currentPhotoPath, bmOptions);
ivImage.setImageBitmap(bitmap);
}
}
}
I can't really figured out what causing this because in logcat there is seems no error at all, my build still running like usual.
Here is my logcat after entering current activity for take the picture:
D/TextLayoutCache: Enable myanmar Zawgyi converter
D/TextLayoutCache: Enable myanmar Zawgyi converter
D/TextLayoutCache: Enable myanmar Zawgyi converter
D/TextLayoutCache: Enable myanmar Zawgyi converter
I/System.out: /storage/emulated/0/Android/data/com.opentoko.opentokolaundry/files/Pictures
I/System.out: /storage/emulated/0/Android/data/com.opentoko.opentokolaundry/files/Pictures/A-00003_20170127_1758_-1345208956.jpg
I/System.out: /storage/emulated/0/Android/data/com.opentoko.opentokolaundry/files/Pictures/A-00003_20170127_1758_-1345208956.jpg
I/System.out: content://com.opentoko.opentokolaundry.fileprovider/my_images/A-00003_20170127_1758_-1345208956.jpg
W/IInputConnectionWrapper: showStatusIcon on inactive InputConnection
This logcat already in this exact point when it start begin open the camera. After capturing, nothing added to the logcat.
This is part of my Android-Manifest.xml :
<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.opentoko.opentokolaundry.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths">
</meta-data>
</provider>
Does anyone here had or has same problem with me? Any solution? Any help will be appreciated.
I've found solution for intent camera force close on KitKat by trial and error.
Seems I don't need FileProvider at all.
I change storageDir in my createImageFile() to this :
File storageDir = getExternalFilesDir("Pictures");
And photoURI to this :
Uri photoURI = Uri.fromFile(photoFile);
Now I have the right fullscreen image to display on KitKat too.
And I remove provider from my manifest. Now I have my captured image file at Android/data/my.package.name/files/Pictures/ folder.
Related
I'm currently writing a Xamarin Forms app which requires use of the camera, in the code below I am requesting the permission using the Xamarin Essentials Permissions which comes back as "Granted"; immediately following that I am requesting use of the camera to take a photo, which throws the following error.
ex = {Plugin.Media.Abstractions.MediaPermissionException: Camera permission(s) are required.
The permission code
public static async Task<bool> GetPermission<TPermission>() where TPermission : BasePermission, new()
{
var hasPermission = await Permissions.CheckStatusAsync<TPermission>();
if (hasPermission == PermissionStatus.Granted)
return true;
else if (hasPermission == PermissionStatus.Disabled)
return false;
var result = await Permissions.RequestAsync<TPermission>();
if (result != PermissionStatus.Granted)
return false;
return true;
}
The photo manager code
if(!await PermissionHelpers.GetPermission<Permissions.Camera>())
{
await new ErrorAlert().Show("App can't take a picture without permission to use the camera");
return string.Empty;
}
var photo = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions
{
PhotoSize = PhotoSize.Small,
SaveToAlbum = false
});
As previously said, the GetPermission method returns true, but still the error is thrown.
I'm currently running this on Android.
My AndroidManifest.xml has these permission in it.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
I have now made a sample project to showcase my issue
GitHub Repo for the issue
don't forget
Android
protected override void OnCreate(Bundle savedInstanceState) {
//...
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState); // add this line to your code, it may also be called: bundle
//...
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
Documentation
First of all, I notice you use Xam.Plugin.Media, this plugin need WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE and android.permission.CAMERA in Android, You should request these permission at runtime.
You can use following code in the MainActivity
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.Camera) != (int)Permission.Granted)
{
RequestPermissions(new string[] { Manifest.Permission.Camera, Manifest.Permission.WriteExternalStorage, Manifest.Permission.ReadExternalStorage }, 0);
}
}
Here is running gif.
Update
If you use this CrossMedia, you need grant Storage and Camera permission.Please open your PhotoManager.cs Add the request storage code like following code.
public class PhotoManager
{
public async Task<string> TakeNewPhoto()
{
try
{
if (!CrossMedia.IsSupported || !CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
return string.Empty;
}
if (!await PermissionHelpers.GetPermission<Permissions.Camera>())
{
return string.Empty;
}
//=====================================add above line==================================================
if (!await PermissionHelpers.GetPermission<Permissions.StorageWrite>())
{
return string.Empty;
}
var photo = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions
{
PhotoSize = PhotoSize.Small,
SaveToAlbum = false
});
if (photo != null)
{
return "photo taken successfully";
}
return string.Empty;
}
catch (Exception ex)
{
return ex.Message;
}
}
}
Here is your issueProjects' running GIF.
thank you for all of your time gone into helping me to resolve this issue.
It turned out that if you are using Xamarin essentials version 1.5.0 you need to install the CurrentActivity NuGet plugin to your android project.
Or, a better solution is update to 1.5.1 which resolves the issue entirely.
You must add permissions for camera to your Android Manifest file!
In Visual Studio right click your android project.
Go to Options -> Build -> Android Application and tick the box in required permissions that says camera.
NB: If you are going to be recording you may also want to enable microphone and audio permissions.
You must also add:
<uses-feature android:name="android.hardware.camera" />
To your android manifest.
I make an attempt to detect devices using Bluetooth LE, did as written in this library for Xamarin, but the devices found 0
Necessary manifestos in android included. Increased the time for scanning to 30 seconds, but nothing. Bluetooth enabled, version 4.0
Asus_Z00ED device (Android 6.0 - API 23). The project was created with a target on Android 8.1. Tried to switch target versions of Android (8.0,9.0) in the manifest, but there is also no detection
What else could be the reason for the lack of detection?
public class Page6 : ContentPage
{
IBluetoothLE ble;
IAdapter adapter;
ObservableCollection<IDevice> deviceList;
Label label;
public Page6()
{
ble = CrossBluetoothLE.Current;
adapter = CrossBluetoothLE.Current.Adapter;
deviceList = new ObservableCollection<IDevice>();
label = new Label { Text = "Welcome to Xamarin.Forms!" };
var buttonList = new Button() { Text = "Devices List" };
buttonList.Clicked += ButtonList_Clicked;
Content = new StackLayout
{
Children = {
label,
buttonList,
}
};
}
private async void ButtonList_Clicked(object sender, EventArgs e)
{
deviceList.Clear();
adapter.ScanTimeout = 30000;
adapter.DeviceDiscovered += (s, a) =>
{
deviceList.Add(a.Device);
};
await adapter.StartScanningForDevicesAsync(); // сканирование
label.Text = deviceList.Count.ToString();
}
}
Remove the android:targetSdkVersion from the uses-sdk tag in your AndroidManifest.xml and everything works. Full tag from our project:
<uses-sdk android:minSdkVersion="15" />
For my studying project, I need to realize an application that has a CameraView or a CameraPage, with a special design. However, I’m not able to figure out how to realize it.
I found a lot of information, to be honest, but they are either obsolete or incomplete, so, I would like to make a point about it, through this thread!
How to implement a Camera?
Well, two solutions can be considered based on what I read.
Camera Page
Let’s say that it’s the first “official” solution. It’s proposed by Xamarin itself, with the Customizing a ContentPage tutorial/documentation. It explains you, through a web page how to implement the camera service with a cross-platform solution.
I then tried the UWP solution:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CameraPreviewProject.Sources.Pages.CameraPage">
<ContentPage.Content>
<AbsoluteLayout>
<Button Text="Click me !" AbsoluteLayout.LayoutBounds="0.5, 0.5, 0.1, 0.1" AbsoluteLayout.LayoutFlags="All" />
</AbsoluteLayout>
</ContentPage.Content>
</ContentPage>
Finally, the C# side gives us this:
public partial class CameraPage : ContentPage
{
public CameraPage()
{
InitializeComponent();
}
}
Then, we create a renderer in the UWP side :
using CameraPreviewProject.Sources.Pages;
using CameraPreviewProject.UWP.Sources.PageRenderers;
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.Devices.Enumeration;
using Windows.Devices.Sensors;
using Windows.Foundation;
using Windows.Graphics.Display;
using Windows.Graphics.Imaging;
using Windows.Media;
using Windows.Media.Capture;
using Windows.Media.MediaProperties;
using Windows.Storage;
using Windows.Storage.FileProperties;
using Windows.Storage.Streams;
using Windows.System.Display;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Xamarin.Forms.Platform.UWP;
[assembly: ExportRenderer(typeof(CameraPage), typeof(CameraPageRenderer))]
namespace CameraPreviewProject.UWP.Sources.PageRenderers
{
public class CameraPageRenderer : PageRenderer
{
private readonly DisplayInformation displayInformation = DisplayInformation.GetForCurrentView();
private readonly SimpleOrientationSensor orientationSensor = SimpleOrientationSensor.GetDefault();
private readonly DisplayRequest displayRequest = new DisplayRequest();
private SimpleOrientation deviceOrientation = SimpleOrientation.NotRotated;
private DisplayOrientations displayOrientation = DisplayOrientations.Portrait;
// Rotation metadata to apply to preview stream (https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh868174.aspx)
private static readonly Guid RotationKey = new Guid("C380465D-2271-428C-9B83-ECEA3B4A85C1"); // (MF_MT_VIDEO_ROTATION)
private StorageFolder captureFolder = null;
private readonly SystemMediaTransportControls systemMediaControls = SystemMediaTransportControls.GetForCurrentView();
private MediaCapture mediaCapture;
private CaptureElement captureElement;
private bool isInitialized;
private bool isPreviewing;
private bool externalCamera;
private bool mirroringPreview;
private Page page;
private AppBarButton takePhotoButton;
private Application app;
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Page> e)
{
base.OnElementChanged(e);
if (e.OldElement != null || Element == null)
{
return;
}
try
{
app = Application.Current;
app.Suspending += OnAppSuspending;
app.Resuming += OnAppResuming;
SetupUserInterface();
SetupCamera();
this.Children.Add(page);
}
catch (Exception ex)
{
Debug.WriteLine(#" ERROR: ", ex.Message);
}
}
protected override Size ArrangeOverride(Size finalSize)
{
page.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
return finalSize;
}
private void SetupUserInterface()
{
takePhotoButton = new AppBarButton
{
VerticalAlignment = VerticalAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
Icon = new SymbolIcon(Symbol.Camera)
};
var commandBar = new CommandBar();
commandBar.PrimaryCommands.Add(takePhotoButton);
captureElement = new CaptureElement();
captureElement.Stretch = Stretch.UniformToFill;
var stackPanel = new StackPanel();
stackPanel.Children.Add(captureElement);
page = new Page();
page.BottomAppBar = commandBar;
page.Content = stackPanel;
page.Unloaded += OnPageUnloaded;
}
private async void SetupCamera()
{
await SetupUIAsync();
await InitializeCameraAsync();
}
#region Event Handlers
private async void OnSystemMediaControlsPropertyChanged(SystemMediaTransportControls sender, SystemMediaTransportControlsPropertyChangedEventArgs args)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
// Only handle event if the page is being displayed
if (args.Property == SystemMediaTransportControlsProperty.SoundLevel && page.Frame.CurrentSourcePageType == typeof(MainPage))
{
// Check if the app is being muted. If so, it's being minimized
// Otherwise if it is not initialized, it's being brought into focus
if (sender.SoundLevel == SoundLevel.Muted)
{
await CleanupCameraAsync();
}
else if (!isInitialized)
{
await InitializeCameraAsync();
}
}
});
}
private void OnOrientationSensorOrientationChanged(SimpleOrientationSensor sender, SimpleOrientationSensorOrientationChangedEventArgs args)
{
// Only update orientatino if the device is not parallel to the ground
if (args.Orientation != SimpleOrientation.Faceup && args.Orientation != SimpleOrientation.Facedown)
{
deviceOrientation = args.Orientation;
}
}
private async void OnDisplayInformationOrientationChanged(DisplayInformation sender, object args)
{
displayOrientation = sender.CurrentOrientation;
if (isPreviewing)
{
await SetPreviewRotationAsync();
}
}
private async void OnTakePhotoButtonClicked(object sender, RoutedEventArgs e)
{
await TakePhotoAsync();
}
/*async void OnHardwareCameraButtonPressed(object sender, CameraEventArgs e)
{
await TakePhotoAsync();
}*/
#endregion Event Handlers
#region Media Capture
private async Task InitializeCameraAsync()
{
if (mediaCapture == null)
{
var devices = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
var cameraDevice = devices.FirstOrDefault(c => c.EnclosureLocation != null && c.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Back);
// Get any camera if there isn't one on the back panel
cameraDevice = cameraDevice ?? devices.FirstOrDefault();
if (cameraDevice == null)
{
Debug.WriteLine("No camera found");
return;
}
mediaCapture = new MediaCapture();
try
{
await mediaCapture.InitializeAsync(new MediaCaptureInitializationSettings
{
VideoDeviceId = cameraDevice.Id,
AudioDeviceId = string.Empty,
StreamingCaptureMode = StreamingCaptureMode.Video,
PhotoCaptureSource = PhotoCaptureSource.Photo
});
isInitialized = true;
}
catch (UnauthorizedAccessException)
{
Debug.WriteLine("Camera access denied");
}
catch (Exception ex)
{
Debug.WriteLine("Exception initializing MediaCapture - {0}: {1}", cameraDevice.Id, ex.ToString());
}
if (isInitialized)
{
if (cameraDevice.EnclosureLocation == null || cameraDevice.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Unknown)
{
externalCamera = true;
}
else
{
// Camera is on device
externalCamera = false;
// Mirror preview if camera is on front panel
mirroringPreview = (cameraDevice.EnclosureLocation.Panel == Windows.Devices.Enumeration.Panel.Front);
}
await StartPreviewAsync();
}
}
}
private async Task StartPreviewAsync()
{
// Prevent the device from sleeping while the preview is running
displayRequest.RequestActive();
// Setup preview source in UI and mirror if required
captureElement.Source = mediaCapture;
captureElement.FlowDirection = mirroringPreview ? FlowDirection.RightToLeft : FlowDirection.LeftToRight;
// Start preview
await mediaCapture.StartPreviewAsync();
isPreviewing = true;
if (isPreviewing)
{
await SetPreviewRotationAsync();
}
}
private async Task StopPreviewAsync()
{
isPreviewing = false;
await mediaCapture.StopPreviewAsync();
// Use dispatcher because sometimes this method is called from non-UI threads
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
// UI cleanup
captureElement.Source = null;
// Allow device screen to sleep now preview is stopped
displayRequest.RequestRelease();
});
}
private async Task SetPreviewRotationAsync()
{
// Only update the orientation if the camera is mounted on the device
if (externalCamera)
{
return;
}
// Derive the preview rotation
int rotation = ConvertDisplayOrientationToDegrees(displayOrientation);
// Invert if mirroring
if (mirroringPreview)
{
rotation = (360 - rotation) % 360;
}
// Add rotation metadata to preview stream
var props = mediaCapture.VideoDeviceController.GetMediaStreamProperties(MediaStreamType.VideoPreview);
props.Properties.Add(RotationKey, rotation);
await mediaCapture.SetEncodingPropertiesAsync(MediaStreamType.VideoPreview, props, null);
}
private async Task TakePhotoAsync()
{
var stream = new InMemoryRandomAccessStream();
await mediaCapture.CapturePhotoToStreamAsync(ImageEncodingProperties.CreateJpeg(), stream);
try
{
var file = await captureFolder.CreateFileAsync("photo.jpg", CreationCollisionOption.GenerateUniqueName);
var orientation = ConvertOrientationToPhotoOrientation(GetCameraOrientation());
await ReencodeAndSavePhotoAsync(stream, file, orientation);
}
catch (Exception ex)
{
Debug.WriteLine("Exception when taking photo: " + ex.ToString());
}
}
private async Task CleanupCameraAsync()
{
if (isInitialized)
{
if (isPreviewing)
{
await StopPreviewAsync();
}
isInitialized = false;
}
if (mediaCapture != null)
{
mediaCapture.Dispose();
mediaCapture = null;
}
}
#endregion Media Capture
#region Helpers
private async Task SetupUIAsync()
{
// Lock page to landscape to prevent the capture element from rotating
DisplayInformation.AutoRotationPreferences = DisplayOrientations.Landscape;
/*// Hide status bar
if (ApiInformation.IsTypePresent("Windows.UI.ViewManagement.StatusBar"))
{
await Windows.UI.ViewManagement.StatusBar.GetForCurrentView().HideAsync();
}*/
displayOrientation = displayInformation.CurrentOrientation;
if (orientationSensor != null)
{
deviceOrientation = orientationSensor.GetCurrentOrientation();
}
RegisterEventHandlers();
var picturesLibrary = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures);
// Fallback to local app storage if no pictures library
captureFolder = picturesLibrary.SaveFolder ?? ApplicationData.Current.LocalFolder;
}
private async Task CleanupUIAsync()
{
UnregisterEventHandlers();
/*if (ApiInformation.IsTypePresent("Windows.UI.ViewManagement.StatusBar"))
{
await Windows.UI.ViewManagement.StatusBar.GetForCurrentView().ShowAsync();
}*/
// Revert orientation preferences
DisplayInformation.AutoRotationPreferences = DisplayOrientations.None;
}
private void RegisterEventHandlers()
{
/*if (ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))
{
HardwareButtons.CameraPressed += OnHardwareCameraButtonPressed;
}*/
if (orientationSensor != null)
{
orientationSensor.OrientationChanged += OnOrientationSensorOrientationChanged;
}
displayInformation.OrientationChanged += OnDisplayInformationOrientationChanged;
systemMediaControls.PropertyChanged += OnSystemMediaControlsPropertyChanged;
takePhotoButton.Click += OnTakePhotoButtonClicked;
}
private void UnregisterEventHandlers()
{
/*if (ApiInformation.IsTypePresent("Windows.Phone.UI.Input.HardwareButtons"))
{
HardwareButtons.CameraPressed -= OnHardwareCameraButtonPressed;
}*/
if (orientationSensor != null)
{
orientationSensor.OrientationChanged -= OnOrientationSensorOrientationChanged;
}
displayInformation.OrientationChanged -= OnDisplayInformationOrientationChanged;
systemMediaControls.PropertyChanged -= OnSystemMediaControlsPropertyChanged;
takePhotoButton.Click -= OnTakePhotoButtonClicked;
}
private static async Task ReencodeAndSavePhotoAsync(IRandomAccessStream stream, StorageFile file, PhotoOrientation orientation)
{
using (var inputStream = stream)
{
var decoder = await BitmapDecoder.CreateAsync(inputStream);
using (var outputStream = await file.OpenAsync(FileAccessMode.ReadWrite))
{
var encoder = await BitmapEncoder.CreateForTranscodingAsync(outputStream, decoder);
var properties = new BitmapPropertySet
{
{
"System.Photo.Orientation", new BitmapTypedValue(orientation, Windows.Foundation.PropertyType.UInt16)
}
};
await encoder.BitmapProperties.SetPropertiesAsync(properties);
await encoder.FlushAsync();
}
}
}
#endregion Helpers
#region Rotation
private SimpleOrientation GetCameraOrientation()
{
if (externalCamera)
{
// Cameras that aren't attached to the device do not rotate along with it
return SimpleOrientation.NotRotated;
}
var result = deviceOrientation;
// On portrait-first devices, the camera sensor is mounted at a 90 degree offset to the native orientation
if (displayInformation.NativeOrientation == DisplayOrientations.Portrait)
{
switch (result)
{
case SimpleOrientation.Rotated90DegreesCounterclockwise:
result = SimpleOrientation.NotRotated;
break;
case SimpleOrientation.Rotated180DegreesCounterclockwise:
result = SimpleOrientation.Rotated90DegreesCounterclockwise;
break;
case SimpleOrientation.Rotated270DegreesCounterclockwise:
result = SimpleOrientation.Rotated180DegreesCounterclockwise;
break;
case SimpleOrientation.NotRotated:
result = SimpleOrientation.Rotated270DegreesCounterclockwise;
break;
}
}
// If the preview is mirrored for a front-facing camera, invert the rotation
if (mirroringPreview)
{
// Rotating 0 and 180 ddegrees is the same clockwise and anti-clockwise
switch (result)
{
case SimpleOrientation.Rotated90DegreesCounterclockwise:
return SimpleOrientation.Rotated270DegreesCounterclockwise;
case SimpleOrientation.Rotated270DegreesCounterclockwise:
return SimpleOrientation.Rotated90DegreesCounterclockwise;
}
}
return result;
}
private static int ConvertDeviceOrientationToDegrees(SimpleOrientation orientation)
{
switch (orientation)
{
case SimpleOrientation.Rotated90DegreesCounterclockwise:
return 90;
case SimpleOrientation.Rotated180DegreesCounterclockwise:
return 180;
case SimpleOrientation.Rotated270DegreesCounterclockwise:
return 270;
case SimpleOrientation.NotRotated:
default:
return 0;
}
}
private static int ConvertDisplayOrientationToDegrees(DisplayOrientations orientation)
{
switch (orientation)
{
case DisplayOrientations.Portrait:
return 90;
case DisplayOrientations.LandscapeFlipped:
return 180;
case DisplayOrientations.PortraitFlipped:
return 270;
case DisplayOrientations.Landscape:
default:
return 0;
}
}
private static PhotoOrientation ConvertOrientationToPhotoOrientation(SimpleOrientation orientation)
{
switch (orientation)
{
case SimpleOrientation.Rotated90DegreesCounterclockwise:
return PhotoOrientation.Rotate90;
case SimpleOrientation.Rotated180DegreesCounterclockwise:
return PhotoOrientation.Rotate180;
case SimpleOrientation.Rotated270DegreesCounterclockwise:
return PhotoOrientation.Rotate270;
case SimpleOrientation.NotRotated:
default:
return PhotoOrientation.Normal;
}
}
#endregion Rotation
#region Lifecycle
private async void OnAppSuspending(object sender, SuspendingEventArgs e)
{
var deferral = e.SuspendingOperation.GetDeferral();
await CleanupCameraAsync();
await CleanupUIAsync();
deferral.Complete();
}
private async void OnAppResuming(object sender, object o)
{
await SetupUIAsync();
await InitializeCameraAsync();
}
private async void OnPageUnloaded(object sender, RoutedEventArgs e)
{
await CleanupCameraAsync();
await CleanupUIAsync();
}
#endregion Lifecycle
}
}
This idea is pretty logic, you have a basic page, but which have renderer that preview the camera in the background, I mean, this is the idea I understood, however, it only gives you a white screen that throws an exception… (x86)
Exception initializing MediaCapture - \\?\USB#VID_045E&PID_0779&MI_00#6&2E9BBB25&0&0000#{e5323777-f976-4f5b-9b55-b94699c46e44}\GLOBAL: System.Runtime.InteropServices.COMException (0xC00DABE6): The current capture source does not have an independent photo stream.
The current capture source does not have an independent photo stream.
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at CameraPreviewProject.UWP.Sources.PageRenderers.CameraPageRenderer.<InitializeCameraAsync>d__25.MoveNext()
Then I click the button of the downside woft menu and get:
Exception thrown: 'System.Runtime.InteropServices.COMException' in System.Private.CoreLib.ni.dll
WinRT information: This object needs to be initialized before the requested operation can be carried out.
I’m a Xamarin Fan, but on that part, I’m not. This link about MediaCapture can be interesting though!
CameraView
To be honest, it’s so way easier to have a control as a button!
<Camera/>
Well, let’s have a look at it! I found a couple of solutions:
Moment MVVM logic - It seems to work only with Android & iOS
Xlabs Camera Unable to try since I can’t start VS2017 from the .sln. Also, I couldn't test the UWP side because it's an MVVM logic..
Xam.Plugin.Media This solution works on UWP !! But run a new activity/instance/page with a native design, so this isn't the solution searched
So, my question is “Does someone could create an element public class Camera() that can be used and declared as a simple xamarin forms button?”
Because, I saw as well 2 others projects about it, one I can’t remember, but the second one is Barcode Scanning but I’m not able to understand or take back the code to implement it as I would like…
It seems so easy and it’s so hard to get, why? Because finally, we’re talking about a view/image that displays a stream from a camera? A camera is just a service where you have methods such as TakePictureAsync() or anything like that? Rotate(), Switch(ViewSide vs), etc etc?
So, I searched about getting a frame view or display the stream of the camera into an image or a view.. I began from those links:
UWP get live webcam video stream by David Pine
UWP stream Webcam over socket to mediaElement I just made some changes
because the subject is a bit different, but.. I couldn't make it work
To be honest, I don’t know what to try now… I’m lost because, at the same time, I tried some Xamarin Forms solution, but also some proper UWP solutions and … nothing…. Maybe my point of view is not good, maybe my idea and just on the side, maybe I should try another approach, I don’t know at all..
I was also thinking about creating a class with some interface that I redefine in each platform renderer, but, still nothing…
Do you have please, any idea or any approach?
Note I have cross-posed this to the Xamarin forums.
I try to pick an image from sdcard and then crop it.
ACTION_PICK is OK, but when i call ACTION_CROP, my system gallery app (I call it as A) can't done the action, but another app (B) can.
I tried the following cases:
1/ Pick by A and then crop by A => pick OK, crop fail
2/ Pick by B and then crop by A => the same as first case.
3/ Pick by A and then crop by B => every things OK.
4/ Pick by B and then crop by B => every things OK.
So my temporary conclusion is: my system app can't do the crop action with my code (may be i forgot something). Here is my code:
ACTION_PICK:
public Intent galleryIntent() {
Intent galleryIntent = new Intent(Intent.ACTION_PICK);
galleryIntent.setType("image/*");
galleryIntent.putExtra("return-data", true);
return galleryIntent;
}
ACTION_CROP:
public Intent cropIntent(Uri inUri, int outputX, int outputY,
boolean isScale) {
Intent cropIntent = new Intent("com.android.camera.action.CROP");
cropIntent.setDataAndType(inUri, "image/*");
cropIntent.putExtra("crop", "true");
cropIntent.putExtra("aspectX", outputX);
cropIntent.putExtra("aspectY", outputY);
cropIntent.putExtra("outputX", outputX);
cropIntent.putExtra("outputY", outputY);
cropIntent.putExtra("scale", isScale);
cropIntent.putExtra("return-data", true);
return cropIntent;
}
My onActivityResult method
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case REQUEST_CODE_GALLERY:
imageUri = data.getData();
startActivityForResult(cropIntent(imageUri,
500, 500, true), REQUEST_CODE_CROP);
break;
case REQUEST_CODE_CROP:
Bundle extras = data.getExtras();
Bitmap tempBitmap = extras.getParcelable("data");
imgvMain.setImageBitmap(null);
imgvMain.setImageBitmap(tempBitmap);
break;
}
} else {
imageUri = null;
}
}
Am i missing somethings?
Thank for your attention!
I use this code successfully for Android 2.2 and up:
It opens a selection of apps that can get image files e.g. the Gallery app. If the selected app can crop, it will also do so.
The cropped image will be saved to the supplied temp file.
(note the small difference for KITKAT).
Intent intent = new Intent();
intent.setType("image/*");
intent.putExtra("crop", "true");
intent.putExtra("outputX", Constants.IMAGE_WIDTH);
intent.putExtra("outputY", Constants.IMAGE_HEIGHT);
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("scale", true);
intent.putExtra("scaleUpIfNeeded", true);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(<a temp file created somewhere>));
intent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString());
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
{
intent.setAction(Intent.ACTION_GET_CONTENT);
}
else
{
intent.setAction(Intent.ACTION_PICK);
intent.setData(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
startActivityForResult(intent, RESULT_CROP);
EDIT:
I ended up using custom cropping using: https://github.com/biokys/cropimage. It was very easy and I had no more troubles with cropping :-)
I checked the logs by filtering with gallery3d and found out that the stock app is not getting permission to access the uri. Hence it's behaving unexpectedly. That behaviour is different for different platforms.
Solution:
get the uri of the selected image in onActivityResult() for intent ACTION_PICK.
save the image temporarily.
create new URI from the saved image.
pass the new URI to com.android.camera.action.CROP.
Sample code: (Dont copy paste. for simplicity ,I have removed the error checks and async tasks.)
public void pickCroppedPhoto(){
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/*");
photoPickerIntent.putExtra("crop", "true");
populateCropExtras(activity, photoPickerIntent);
startActivityForResult(photoPickerIntent , REQUEST_CODE_PICK);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, final Intent data) {
if (requestCode == REQUEST_CODE_PICK ) {
File mainImage = saveUriInFile( this,
data.getData(),
getTempFileForMainImage());
if (null == mainImage) {
handleImageSelectionFailure();
}else {
try {
Uri mainFileUri = Uri.fromFile(mainImage);
performCrop(this,mainFileUri);
}catch(Exception e){
handleImageSelectionFailure();
}
}
}else if ( requestCode == PIC_CROP ){
postImageSelection(data.getData());
}
}
Here is my performCrop code which is similar to that of the question.
public static boolean performCrop(Activity activity, Uri picUri) {
try {
Intent cropIntent = new Intent("com.android.camera.action.CROP");
// indicate image type and Uri
cropIntent.setDataAndType(picUri, "image/*");
if (populateCropExtras(activity, cropIntent)) return false;
activity.startActivityForResult(cropIntent, PIC_CROP);
return true;
}
// respond to users whose devices do not support the crop action
catch (ActivityNotFoundException anfe) {
String errorMessage = "Device doesn't support crop!";
Log.w(PhotoPickerUtil.class.getCanonicalName(), errorMessage);
return false;
}catch (IOException ioe){
String errorMessage = "Error while getting temporary file from external storage";
Log.w(PhotoPickerUtil.class.getCanonicalName(), errorMessage);
return false;
}
}
private static void populateCropExtras(Activity activity, Intent cropIntent) throws IOException { {
// set crop properties
cropIntent.putExtra("crop", "true");
// indicate output X and Y
cropIntent.putExtra("outputX", 300);
cropIntent.putExtra("outputY", 300);
// indicate aspect of desired crop
cropIntent.putExtra("aspectX", 1);
cropIntent.putExtra("aspectY", 1);
cropIntent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString());
Uri tempUri = Uri.fromFile(getTempFile(activity));
cropIntent.putExtra("output", tempUri);
}
My app has a camera button. Upon click this button launches the native camera app and the user can take a picture. If the user accepts the picture (taps check mark) I want to start a new activity which will display that image on a new screen. I am trying to save the image like so: File file = new File(this.getFilesDir(), filename); and then get the file path and pass the path on as a string to my new activity. In my new activity I am attempting to retrieve the filepath from the previous intent, and use that to load the image into an ImageView. Before I write the bit to load the image into the ImageView I wanted to check that the filepath string is being passed by printing it in a text area on the new screen. Unfortunately, it does not appear to be passing properly. Any help would be greatly appreciated. Below is a snippet of my code. Many thanks in advance for the assistance!
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_use_camera);
// create Intent to take a picture and return control to the calling application
Intent cameraintent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//start the image capture Intent
startActivityForResult(cameraintent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
String filename = "myfile";
//make sure the user clicked to accept the picture in the native camera app
if (resultCode == RESULT_OK) {
if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE)
{
//save image internally
File file = new File(this.getFilesDir(), filename);
//get the filepath
String filepath = file.getAbsolutePath();
//create the swatchintent
Intent Swatchintent = new Intent(this, DisplaySwatchActivity.class);
//pass filepath to the intent
Swatchintent.putExtra(EXTRA_MESSAGE, filepath);
//start the intent
startActivity(Swatchintent);
}
}
}
then, my next activity:
public class DisplaySwatchActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display_swatch);
String filepath = this.getIntent().getStringExtra(UseCameraActivity.EXTRA_MESSAGE);
TextView textView = new TextView(this);
textView.setTextSize(20);
textView.setText(filepath);
}