From 614b77c57f7d05ab11332f3943310719fdd0acd0 Mon Sep 17 00:00:00 2001 From: David Britch Date: Fri, 30 Oct 2020 15:04:04 +0000 Subject: [PATCH] Use Camera2 on Android. --- .../View/CustomRenderer/CameraPreview.cs | 25 +- .../View/CustomRenderer/CustomRenderer.csproj | 2 +- .../View/CustomRenderer/MainPage.xaml | 13 +- .../View/Droid/AutoFitTextureView.cs | 84 +++ .../View/Droid/CameraCaptureStateListener.cs | 14 + CustomRenderers/View/Droid/CameraFragment.cs | 552 ++++++++++++++++++ CustomRenderers/View/Droid/CameraPreview.cs | 143 ----- .../View/Droid/CameraPreviewRenderer.cs | 198 ++++++- .../View/Droid/CameraStateListener.cs | 18 + .../View/Droid/CustomRenderer.Droid.csproj | 13 +- CustomRenderers/View/Droid/MainActivity.cs | 12 +- .../View/Droid/Properties/AndroidManifest.xml | 5 +- .../View/Droid/Resources/Resource.designer.cs | 380 ++++++------ .../Droid/Resources/layout/CameraFragment.xml | 10 + .../View/Droid/Resources/layout/Tabbar.axml | 12 - .../View/Droid/Resources/layout/Toolbar.axml | 9 - .../View/UWP/CustomRenderer.UWP.csproj | 2 +- .../View/iOS/CustomRenderer.iOS.csproj | 4 +- 18 files changed, 1075 insertions(+), 421 deletions(-) mode change 100755 => 100644 CustomRenderers/View/CustomRenderer/CameraPreview.cs mode change 100755 => 100644 CustomRenderers/View/CustomRenderer/MainPage.xaml create mode 100644 CustomRenderers/View/Droid/AutoFitTextureView.cs create mode 100644 CustomRenderers/View/Droid/CameraCaptureStateListener.cs create mode 100644 CustomRenderers/View/Droid/CameraFragment.cs delete mode 100755 CustomRenderers/View/Droid/CameraPreview.cs create mode 100644 CustomRenderers/View/Droid/CameraStateListener.cs mode change 100755 => 100644 CustomRenderers/View/Droid/MainActivity.cs mode change 100755 => 100644 CustomRenderers/View/Droid/Properties/AndroidManifest.xml create mode 100644 CustomRenderers/View/Droid/Resources/layout/CameraFragment.xml delete mode 100644 CustomRenderers/View/Droid/Resources/layout/Tabbar.axml delete mode 100644 CustomRenderers/View/Droid/Resources/layout/Toolbar.axml diff --git a/CustomRenderers/View/CustomRenderer/CameraPreview.cs b/CustomRenderers/View/CustomRenderer/CameraPreview.cs old mode 100755 new mode 100644 index 467fb9c009..fd00144de4 --- a/CustomRenderers/View/CustomRenderer/CameraPreview.cs +++ b/CustomRenderers/View/CustomRenderer/CameraPreview.cs @@ -2,17 +2,18 @@ namespace CustomRenderer { - public class CameraPreview : View - { - public static readonly BindableProperty CameraProperty = BindableProperty.Create ( - propertyName: "Camera", - returnType: typeof(CameraOptions), - declaringType: typeof(CameraPreview), - defaultValue: CameraOptions.Rear); + public class CameraPreview : View + { + public static readonly BindableProperty CameraProperty = BindableProperty.Create( + propertyName: "Camera", + returnType: typeof(CameraOptions), + declaringType: typeof(CameraPreview), + defaultValue: CameraOptions.Rear); - public CameraOptions Camera { - get { return (CameraOptions)GetValue (CameraProperty); } - set { SetValue (CameraProperty, value); } - } - } + public CameraOptions Camera + { + get { return (CameraOptions)GetValue(CameraProperty); } + set { SetValue(CameraProperty, value); } + } + } } diff --git a/CustomRenderers/View/CustomRenderer/CustomRenderer.csproj b/CustomRenderers/View/CustomRenderer/CustomRenderer.csproj index 38f0f50c80..1d9b11c45f 100644 --- a/CustomRenderers/View/CustomRenderer/CustomRenderer.csproj +++ b/CustomRenderers/View/CustomRenderer/CustomRenderer.csproj @@ -18,4 +18,4 @@ - \ No newline at end of file + diff --git a/CustomRenderers/View/CustomRenderer/MainPage.xaml b/CustomRenderers/View/CustomRenderer/MainPage.xaml old mode 100755 new mode 100644 index 63f5865c80..db83ac73ee --- a/CustomRenderers/View/CustomRenderer/MainPage.xaml +++ b/CustomRenderers/View/CustomRenderer/MainPage.xaml @@ -3,12 +3,11 @@ xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer" x:Class="CustomRenderer.MainPage" - Padding="0,20,0,0" Title="Main Page"> - - - - + + diff --git a/CustomRenderers/View/Droid/AutoFitTextureView.cs b/CustomRenderers/View/Droid/AutoFitTextureView.cs new file mode 100644 index 0000000000..e41ae3f0c0 --- /dev/null +++ b/CustomRenderers/View/Droid/AutoFitTextureView.cs @@ -0,0 +1,84 @@ +using System; +using Android.Content; +using Android.Runtime; +using Android.Util; +using Android.Views; + +namespace CustomRenderer.Droid +{ + public class AutoFitTextureView : TextureView + { + int mRatioWidth = 0; + int mRatioHeight = 0; + readonly object locker = new object(); + + public AutoFitTextureView(Context context) : this(context, null) + { + } + + public AutoFitTextureView(Context context, IAttributeSet attrs) : this(context, attrs, 0) + { + } + + public AutoFitTextureView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle) + { + } + + protected AutoFitTextureView(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer) + { + } + + public void SetAspectRatio(int width, int height) + { + if (width == 0 || height == 0) + { + throw new ArgumentException("Size can't be negative."); + } + + mRatioWidth = width; + mRatioHeight = height; + RequestLayout(); + } + + protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec) + { + base.OnMeasure(widthMeasureSpec, heightMeasureSpec); + + int width = MeasureSpec.GetSize(widthMeasureSpec); + int height = MeasureSpec.GetSize(heightMeasureSpec); + + if (mRatioWidth == 0 || mRatioHeight == 0) + { + SetMeasuredDimension(width, height); + } + else + { + if (width < (float)height * mRatioWidth / mRatioHeight) + { + SetMeasuredDimension(width, width * mRatioHeight / mRatioWidth); + } + else + { + SetMeasuredDimension(height * mRatioWidth / mRatioHeight, height); + } + } + } + + public void ClearCanvas(Android.Graphics.Color color) + { + using var canvas = LockCanvas(null); + lock (locker) + { + try + { + canvas.DrawColor(color); + } + finally + { + UnlockCanvasAndPost(canvas); + } + Invalidate(); + } + } + } +} diff --git a/CustomRenderers/View/Droid/CameraCaptureStateListener.cs b/CustomRenderers/View/Droid/CameraCaptureStateListener.cs new file mode 100644 index 0000000000..5cfab93105 --- /dev/null +++ b/CustomRenderers/View/Droid/CameraCaptureStateListener.cs @@ -0,0 +1,14 @@ +using System; +using Android.Hardware.Camera2; + +namespace CustomRenderer.Droid +{ + class CameraCaptureStateListener : CameraCaptureSession.StateCallback + { + public Action OnConfigureFailedAction; + public Action OnConfiguredAction; + + public override void OnConfigureFailed(CameraCaptureSession session) => OnConfigureFailedAction?.Invoke(session); + public override void OnConfigured(CameraCaptureSession session) => OnConfiguredAction?.Invoke(session); + } +} diff --git a/CustomRenderers/View/Droid/CameraFragment.cs b/CustomRenderers/View/Droid/CameraFragment.cs new file mode 100644 index 0000000000..15817068af --- /dev/null +++ b/CustomRenderers/View/Droid/CameraFragment.cs @@ -0,0 +1,552 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Android; +using Android.Content; +using Android.Content.PM; +using Android.Graphics; +using Android.Hardware.Camera2; +using Android.Hardware.Camera2.Params; +using Android.OS; +using Android.Runtime; +using Android.Views; +//using AndroidX.Core.Content; +//using AndroidX.Fragment.App; +using Android.Support.V4.Content; +using Android.Support.V4.App; +using Java.Lang; +using Java.Util.Concurrent; +using Xamarin.Forms.Platform.Android; + +namespace CustomRenderer.Droid +{ + class CameraFragment : Fragment, TextureView.ISurfaceTextureListener + { + CameraDevice device; + CaptureRequest.Builder sessionBuilder; + CameraCaptureSession session; + CameraTemplate cameraTemplate; + CameraManager manager; + + bool cameraPermissionsGranted; + bool busy; + bool repeatingIsRunning; + int sensorOrientation; + string cameraId; + LensFacing cameraType; + + Android.Util.Size previewSize; + + HandlerThread backgroundThread; + Handler backgroundHandler = null; + + Java.Util.Concurrent.Semaphore captureSessionOpenCloseLock = new Java.Util.Concurrent.Semaphore(1); + + AutoFitTextureView texture; + + TaskCompletionSource initTaskSource; + TaskCompletionSource permissionsRequested; + + CameraManager Manager => manager ??= (CameraManager)Context.GetSystemService(Context.CameraService); + + bool IsBusy + { + get => device == null || busy; + set + { + busy = value; + } + } + + bool Available; + + public CameraPreview Element { get; set; } + + #region Constructors + + public CameraFragment() + { + } + + public CameraFragment(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer) + { + } + + #endregion + + #region Overrides + + public override Android.Views.View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) => inflater.Inflate(Resource.Layout.CameraFragment, null); + public override void OnViewCreated(Android.Views.View view, Bundle savedInstanceState) => texture = view.FindViewById(Resource.Id.cameratexture); + + public override void OnPause() + { + CloseSession(); + StopBackgroundThread(); + base.OnPause(); + } + + public override async void OnResume() + { + base.OnResume(); + + StartBackgroundThread(); + if (texture is null) + { + return; + } + if (texture.IsAvailable) + { + View?.SetBackgroundColor(Element.BackgroundColor.ToAndroid()); + cameraTemplate = CameraTemplate.Preview; + await RetrieveCameraDevice(force: true); + } + else + { + texture.SurfaceTextureListener = this; + } + } + + protected override void Dispose(bool disposing) + { + CloseDevice(); + base.Dispose(disposing); + } + + #endregion + + #region Public methods + + public async Task RetrieveCameraDevice(bool force = false) + { + if (Context == null || (!force && initTaskSource != null)) + { + return; + } + + if (device != null) + { + CloseDevice(); + } + + await RequestCameraPermissions(); + if (!cameraPermissionsGranted) + { + return; + } + + if (!captureSessionOpenCloseLock.TryAcquire(2500, TimeUnit.Milliseconds)) + { + throw new RuntimeException("Timeout waiting to lock camera opening."); + } + + IsBusy = true; + cameraId = GetCameraId(); + + if (string.IsNullOrEmpty(cameraId)) + { + IsBusy = false; + captureSessionOpenCloseLock.Release(); + Console.WriteLine("No camera found"); + } + else + { + try + { + CameraCharacteristics characteristics = Manager.GetCameraCharacteristics(cameraId); + StreamConfigurationMap map = (StreamConfigurationMap)characteristics.Get(CameraCharacteristics.ScalerStreamConfigurationMap); + + previewSize = ChooseOptimalSize(map.GetOutputSizes(Class.FromType(typeof(SurfaceTexture))), + texture.Width, texture.Height, GetMaxSize(map.GetOutputSizes((int)ImageFormatType.Jpeg))); + sensorOrientation = (int)characteristics.Get(CameraCharacteristics.SensorOrientation); + cameraType = (LensFacing)(int)characteristics.Get(CameraCharacteristics.LensFacing); + + if (Resources.Configuration.Orientation == Android.Content.Res.Orientation.Landscape) + { + texture.SetAspectRatio(previewSize.Width, previewSize.Height); + } + else + { + texture.SetAspectRatio(previewSize.Height, previewSize.Width); + } + + initTaskSource = new TaskCompletionSource(); + Manager.OpenCamera(cameraId, new CameraStateListener + { + OnOpenedAction = device => initTaskSource?.TrySetResult(device), + OnDisconnectedAction = device => + { + initTaskSource?.TrySetResult(null); + CloseDevice(device); + }, + OnErrorAction = (device, error) => + { + initTaskSource?.TrySetResult(device); + Console.WriteLine($"Camera device error: {error}"); + CloseDevice(device); + }, + OnClosedAction = device => + { + initTaskSource?.TrySetResult(null); + CloseDevice(device); + } + }, backgroundHandler); + + captureSessionOpenCloseLock.Release(); + device = await initTaskSource.Task; + initTaskSource = null; + if (device != null) + { + await PrepareSession(); + } + } + catch (Java.Lang.Exception ex) + { + Console.WriteLine("Failed to open camera.", ex); + Available = false; + } + finally + { + IsBusy = false; + } + } + } + + public void UpdateRepeatingRequest() + { + if (session == null || sessionBuilder == null) + { + return; + } + + IsBusy = true; + try + { + if (repeatingIsRunning) + { + session.StopRepeating(); + } + + sessionBuilder.Set(CaptureRequest.ControlMode, (int)ControlMode.Auto); + sessionBuilder.Set(CaptureRequest.ControlAeMode, (int)ControlAEMode.On); + session.SetRepeatingRequest(sessionBuilder.Build(), listener: null, backgroundHandler); + repeatingIsRunning = true; + } + catch (Java.Lang.Exception error) + { + Console.WriteLine("Update preview exception.", error); + } + finally + { + IsBusy = false; + } + } + + #endregion + + void StartBackgroundThread() + { + backgroundThread = new HandlerThread("CameraBackground"); + backgroundThread.Start(); + backgroundHandler = new Handler(backgroundThread.Looper); + } + + void StopBackgroundThread() + { + if (backgroundThread == null) + { + return; + } + + backgroundThread.QuitSafely(); + try + { + backgroundThread.Join(); + backgroundThread = null; + backgroundHandler = null; + } + catch (InterruptedException ex) + { + Console.WriteLine("Error stopping background thread.", ex); + } + } + + Android.Util.Size GetMaxSize(Android.Util.Size[] imageSizes) + { + Android.Util.Size maxSize = null; + long maxPixels = 0; + for (int i = 0; i < imageSizes.Length; i++) + { + long currentPixels = imageSizes[i].Width * imageSizes[i].Height; + if (currentPixels > maxPixels) + { + maxSize = imageSizes[i]; + maxPixels = currentPixels; + } + } + return maxSize; + } + + Android.Util.Size ChooseOptimalSize(Android.Util.Size[] choices, int width, int height, Android.Util.Size aspectRatio) + { + List bigEnough = new List(); + int w = aspectRatio.Width; + int h = aspectRatio.Height; + + foreach (Android.Util.Size option in choices) + { + if (option.Height == option.Width * h / w && option.Width >= width && option.Height >= height) + { + bigEnough.Add(option); + } + } + + if (bigEnough.Count > 0) + { + int minArea = bigEnough.Min(s => s.Width * s.Height); + return bigEnough.First(s => s.Width * s.Height == minArea); + } + else + { + Console.WriteLine("Couldn't find any suitable preview size."); + return choices[0]; + } + } + + string GetCameraId() + { + string[] cameraIdList = Manager.GetCameraIdList(); + if (cameraIdList.Length == 0) + { + return null; + } + + string FilterCameraByLens(LensFacing lensFacing) + { + foreach (string id in cameraIdList) + { + CameraCharacteristics characteristics = Manager.GetCameraCharacteristics(id); + if (lensFacing == (LensFacing)(int)characteristics.Get(CameraCharacteristics.LensFacing)) + { + return id; + } + } + return null; + } + + return (Element.Camera == CameraOptions.Front) ? FilterCameraByLens(LensFacing.Front) : FilterCameraByLens(LensFacing.Back); + } + + async Task PrepareSession() + { + IsBusy = true; + try + { + CloseSession(); + sessionBuilder = device.CreateCaptureRequest(cameraTemplate); + + List surfaces = new List(); + if (texture.IsAvailable && previewSize != null) + { + var texture = this.texture.SurfaceTexture; + texture.SetDefaultBufferSize(previewSize.Width, previewSize.Height); + Surface previewSurface = new Surface(texture); + surfaces.Add(previewSurface); + sessionBuilder.AddTarget(previewSurface); + } + + TaskCompletionSource tcs = new TaskCompletionSource(); + device.CreateCaptureSession(surfaces, new CameraCaptureStateListener + { + OnConfigureFailedAction = captureSession => + { + tcs.SetResult(null); + Console.WriteLine("Failed to create capture session."); + }, + OnConfiguredAction = captureSession => tcs.SetResult(captureSession) + }, null); + + session = await tcs.Task; + if (session != null) + { + UpdateRepeatingRequest(); + } + } + catch (Java.Lang.Exception ex) + { + Available = false; + Console.WriteLine("Capture error.", ex); + } + finally + { + Available = session != null; + IsBusy = false; + } + } + + void CloseSession() + { + repeatingIsRunning = false; + if (session == null) + { + return; + } + + try + { + session.StopRepeating(); + session.AbortCaptures(); + session.Close(); + session.Dispose(); + session = null; + } + catch (CameraAccessException ex) + { + Console.WriteLine("Camera access error.", ex); + } + catch (Java.Lang.Exception ex) + { + Console.WriteLine("Error closing device.", ex); + } + } + + void CloseDevice(CameraDevice inputDevice) + { + if (inputDevice == device) + { + CloseDevice(); + } + } + + void CloseDevice() + { + CloseSession(); + + try + { + if (sessionBuilder != null) + { + sessionBuilder.Dispose(); + sessionBuilder = null; + } + if (device != null) + { + device.Close(); + device = null; + } + } + catch (Java.Lang.Exception error) + { + Console.WriteLine("Error closing device.", error); + } + } + + void ConfigureTransform(int viewWidth, int viewHeight) + { + if (texture == null || previewSize == null || previewSize.Width == 0 || previewSize.Height == 0) + { + return; + } + + var matrix = new Matrix(); + var viewRect = new RectF(0, 0, viewWidth, viewHeight); + var bufferRect = new RectF(0, 0, previewSize.Height, previewSize.Width); + var centerX = viewRect.CenterX(); + var centerY = viewRect.CenterY(); + bufferRect.Offset(centerX - bufferRect.CenterX(), centerY - bufferRect.CenterY()); + matrix.SetRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.Fill); + matrix.PostRotate(GetCaptureOrientation(), centerX, centerY); + texture.SetTransform(matrix); + } + + int GetCaptureOrientation() + { + int frontOffset = cameraType == LensFacing.Front ? 90 : -90; + return (360 + sensorOrientation - GetDisplayRotationDegrees() + frontOffset) % 360; + } + + int GetDisplayRotationDegrees() => + GetDisplayRotation() switch + { + SurfaceOrientation.Rotation90 => 90, + SurfaceOrientation.Rotation180 => 180, + SurfaceOrientation.Rotation270 => 270, + _ => 0 + }; + + SurfaceOrientation GetDisplayRotation() => Android.App.Application.Context.GetSystemService(Context.WindowService).JavaCast().DefaultDisplay.Rotation; + + #region Permissions + + async Task RequestCameraPermissions() + { + if (permissionsRequested != null) + { + await permissionsRequested.Task; + } + + List permissionsToRequest = new List(); + cameraPermissionsGranted = ContextCompat.CheckSelfPermission(Context, Manifest.Permission.Camera) == Permission.Granted; + if (!cameraPermissionsGranted) + { + permissionsToRequest.Add(Manifest.Permission.Camera); + } + + if (permissionsToRequest.Count > 0) + { + permissionsRequested = new TaskCompletionSource(); + RequestPermissions(permissionsToRequest.ToArray(), requestCode: 1); + await permissionsRequested.Task; + permissionsRequested = null; + } + } + + public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults) + { + if (requestCode != 1) + { + base.OnRequestPermissionsResult(requestCode, permissions, grantResults); + return; + } + + for (int i=0; i < permissions.Length; i++) + { + if (permissions[i] == Manifest.Permission.Camera) + { + cameraPermissionsGranted = grantResults[i] == Permission.Granted; + if (!cameraPermissionsGranted) + { + Console.WriteLine("No permission to use the camera."); + } + } + } + permissionsRequested?.TrySetResult(true); + } + + #endregion + + #region TextureView.ISurfaceTextureListener + + async void TextureView.ISurfaceTextureListener.OnSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) + { + View?.SetBackgroundColor(Element.BackgroundColor.ToAndroid()); + cameraTemplate = CameraTemplate.Preview; + await RetrieveCameraDevice(); + } + + bool TextureView.ISurfaceTextureListener.OnSurfaceTextureDestroyed(SurfaceTexture surface) + { + CloseDevice(); + return true; + } + + void TextureView.ISurfaceTextureListener.OnSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) => ConfigureTransform(width, height); + + void TextureView.ISurfaceTextureListener.OnSurfaceTextureUpdated(SurfaceTexture surface) + { + } + + #endregion + } +} diff --git a/CustomRenderers/View/Droid/CameraPreview.cs b/CustomRenderers/View/Droid/CameraPreview.cs deleted file mode 100755 index 70f688d22d..0000000000 --- a/CustomRenderers/View/Droid/CameraPreview.cs +++ /dev/null @@ -1,143 +0,0 @@ -using System; -using System.Collections.Generic; -using Android.Content; -using Android.Hardware; -using Android.Runtime; -using Android.Views; - -namespace CustomRenderer.Droid -{ - public sealed class CameraPreview : ViewGroup, ISurfaceHolderCallback - { - SurfaceView surfaceView; - ISurfaceHolder holder; - Camera.Size previewSize; - IList supportedPreviewSizes; - Camera camera; - IWindowManager windowManager; - - public bool IsPreviewing { get; set; } - - public Camera Preview { - get { return camera; } - set { - camera = value; - if (camera != null) { - supportedPreviewSizes = Preview.GetParameters ().SupportedPreviewSizes; - RequestLayout (); - } - } - } - - public CameraPreview (Context context) - : base (context) - { - surfaceView = new SurfaceView (context); - AddView (surfaceView); - - windowManager = Context.GetSystemService (Context.WindowService).JavaCast (); - - IsPreviewing = false; - holder = surfaceView.Holder; - holder.AddCallback (this); - } - - protected override void OnMeasure (int widthMeasureSpec, int heightMeasureSpec) - { - int width = ResolveSize (SuggestedMinimumWidth, widthMeasureSpec); - int height = ResolveSize (SuggestedMinimumHeight, heightMeasureSpec); - SetMeasuredDimension (width, height); - - if (supportedPreviewSizes != null) { - previewSize = GetOptimalPreviewSize (supportedPreviewSizes, width, height); - } - } - - protected override void OnLayout (bool changed, int l, int t, int r, int b) - { - var msw = MeasureSpec.MakeMeasureSpec (r - l, MeasureSpecMode.Exactly); - var msh = MeasureSpec.MakeMeasureSpec (b - t, MeasureSpecMode.Exactly); - - surfaceView.Measure (msw, msh); - surfaceView.Layout (0, 0, r - l, b - t); - } - - public void SurfaceCreated (ISurfaceHolder holder) - { - try { - if (Preview != null) { - Preview.SetPreviewDisplay (holder); - } - } catch (Exception ex) { - System.Diagnostics.Debug.WriteLine (@" ERROR: ", ex.Message); - } - } - - public void SurfaceDestroyed (ISurfaceHolder holder) - { - if (Preview != null) { - Preview.StopPreview (); - } - } - - public void SurfaceChanged (ISurfaceHolder holder, Android.Graphics.Format format, int width, int height) - { - var parameters = Preview.GetParameters (); - parameters.SetPreviewSize (previewSize.Width, previewSize.Height); - RequestLayout (); - - switch (windowManager.DefaultDisplay.Rotation) { - case SurfaceOrientation.Rotation0: - camera.SetDisplayOrientation (90); - break; - case SurfaceOrientation.Rotation90: - camera.SetDisplayOrientation (0); - break; - case SurfaceOrientation.Rotation270: - camera.SetDisplayOrientation (180); - break; - } - - Preview.SetParameters (parameters); - Preview.StartPreview (); - IsPreviewing = true; - } - - Camera.Size GetOptimalPreviewSize (IList sizes, int w, int h) - { - const double AspectTolerance = 0.1; - double targetRatio = (double)w / h; - - if (sizes == null) { - return null; - } - - Camera.Size optimalSize = null; - double minDiff = double.MaxValue; - - int targetHeight = h; - foreach (Camera.Size size in sizes) { - double ratio = (double)size.Width / size.Height; - - if (Math.Abs (ratio - targetRatio) > AspectTolerance) - continue; - if (Math.Abs (size.Height - targetHeight) < minDiff) { - optimalSize = size; - minDiff = Math.Abs (size.Height - targetHeight); - } - } - - if (optimalSize == null) { - minDiff = double.MaxValue; - foreach (Camera.Size size in sizes) { - if (Math.Abs (size.Height - targetHeight) < minDiff) { - optimalSize = size; - minDiff = Math.Abs (size.Height - targetHeight); - } - } - } - - return optimalSize; - } - } -} diff --git a/CustomRenderers/View/Droid/CameraPreviewRenderer.cs b/CustomRenderers/View/Droid/CameraPreviewRenderer.cs index 37b8e5b994..0088ce2f06 100644 --- a/CustomRenderers/View/Droid/CameraPreviewRenderer.cs +++ b/CustomRenderers/View/Droid/CameraPreviewRenderer.cs @@ -1,65 +1,215 @@ using System; +using System.ComponentModel; +//using AndroidX.Fragment.App; +using Android.Support.V4.App; using Android.Content; -using Android.Hardware; +using Android.Views; +using Android.Widget; using CustomRenderer.Droid; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; +using Xamarin.Forms.Platform.Android.FastRenderers; [assembly: ExportRenderer(typeof(CustomRenderer.CameraPreview), typeof(CameraPreviewRenderer))] namespace CustomRenderer.Droid { - public class CameraPreviewRenderer : ViewRenderer + public class CameraPreviewRenderer : FrameLayout, IVisualElementRenderer, IViewRenderer { - CameraPreview cameraPreview; + int? defaultLabelFor; + bool disposed; + CameraPreview element; + VisualElementTracker visualElementTracker; + VisualElementRenderer visualElementRenderer; + FragmentManager fragmentManager; + CameraFragment cameraFragment; + + FragmentManager FragmentManager => fragmentManager ??= Context.GetFragmentManager(); + + public event EventHandler ElementChanged; + public event EventHandler ElementPropertyChanged; + + CameraPreview Element + { + get => element; + set + { + if (element == value) + { + return; + } + + var oldElement = element; + element = value; + OnElementChanged(new ElementChangedEventArgs(oldElement, element)); + } + } public CameraPreviewRenderer(Context context) : base(context) { + visualElementRenderer = new VisualElementRenderer(this); } - protected override void OnElementChanged(ElementChangedEventArgs e) + void OnElementChanged(ElementChangedEventArgs e) { - base.OnElementChanged(e); + CameraFragment newFragment = null; if (e.OldElement != null) { - // Unsubscribe - cameraPreview.Click -= OnCameraPreviewClicked; + e.OldElement.PropertyChanged -= OnElementPropertyChanged; + cameraFragment.Dispose(); } if (e.NewElement != null) { - if (Control == null) + this.EnsureId(); + + e.NewElement.PropertyChanged += OnElementPropertyChanged; + + ElevationHelper.SetElevation(this, e.NewElement); + newFragment = new CameraFragment { Element = element }; + } + + FragmentManager.BeginTransaction() + .Replace(Id, cameraFragment = newFragment, "camera") + .Commit(); + ElementChanged?.Invoke(this, new VisualElementChangedEventArgs(e.OldElement, e.NewElement)); + } + + async void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + ElementPropertyChanged?.Invoke(this, e); + + switch (e.PropertyName) + { + case "Width": + await cameraFragment.RetrieveCameraDevice(); + break; + } + } + + protected override void Dispose(bool disposing) + { + if (disposed) + { + return; + } + + cameraFragment.Dispose(); + disposed = true; + + if (disposing) + { + SetOnClickListener(null); + SetOnTouchListener(null); + + if (visualElementTracker != null) { - cameraPreview = new CameraPreview(Context); - SetNativeControl(cameraPreview); + visualElementTracker.Dispose(); + visualElementTracker = null; } - Control.Preview = Camera.Open((int)e.NewElement.Camera); - // Subscribe - cameraPreview.Click += OnCameraPreviewClicked; + if (visualElementRenderer != null) + { + visualElementRenderer.Dispose(); + visualElementRenderer = null; + } + + if (Element != null) + { + Element.PropertyChanged -= OnElementPropertyChanged; + + if (Platform.GetRenderer(Element) == this) + { + Platform.SetRenderer(Element, null); + } + } } + + base.Dispose(disposing); } - void OnCameraPreviewClicked(object sender, EventArgs e) + #region IViewRenderer + + void IViewRenderer.MeasureExactly() => MeasureExactly(this, Element, Context); + + static void MeasureExactly(Android.Views.View control, VisualElement element, Context context) { - if (cameraPreview.IsPreviewing) + if (control == null || element == null) { - cameraPreview.Preview.StopPreview(); - cameraPreview.IsPreviewing = false; + return; } - else + + double width = element.Width; + double height = element.Height; + + if (width <= 0 || height <= 0) { - cameraPreview.Preview.StartPreview(); - cameraPreview.IsPreviewing = true; + return; } + + int realWidth = (int)context.ToPixels(width); + int realHeight = (int)context.ToPixels(height); + + int widthMeasureSpec = MeasureSpecFactory.MakeMeasureSpec(realWidth, MeasureSpecMode.Exactly); + int heightMeasureSpec = MeasureSpecFactory.MakeMeasureSpec(realHeight, MeasureSpecMode.Exactly); + + control.Measure(widthMeasureSpec, heightMeasureSpec); } - protected override void Dispose(bool disposing) + #endregion + + #region IVisualElementRenderer + + VisualElement IVisualElementRenderer.Element => Element; + + VisualElementTracker IVisualElementRenderer.Tracker => visualElementTracker; + + ViewGroup IVisualElementRenderer.ViewGroup => null; + + Android.Views.View IVisualElementRenderer.View => this; + + SizeRequest IVisualElementRenderer.GetDesiredSize(int widthConstraint, int heightConstraint) { - if (disposing) + Measure(widthConstraint, heightConstraint); + SizeRequest result = new SizeRequest(new Size(MeasuredWidth, MeasuredHeight), new Size(Context.ToPixels(20), Context.ToPixels(20))); + return result; + } + + void IVisualElementRenderer.SetElement(VisualElement element) + { + if (!(element is CameraPreview camera)) { - Control.Preview.Release(); + throw new ArgumentException($"{nameof(element)} must be of type {nameof(CameraPreview)}"); } - base.Dispose(disposing); + + if (visualElementTracker == null) + { + visualElementTracker = new VisualElementTracker(this); + } + Element = camera; + } + + void IVisualElementRenderer.SetLabelFor(int? id) + { + if (defaultLabelFor == null) + { + defaultLabelFor = LabelFor; + } + LabelFor = (int)(id ?? defaultLabelFor); + } + + void IVisualElementRenderer.UpdateLayout() => visualElementTracker?.UpdateLayout(); + + #endregion + + static class MeasureSpecFactory + { + public static int GetSize(int measureSpec) + { + const int modeMask = 0x3 << 30; + return measureSpec & ~modeMask; + } + + public static int MakeMeasureSpec(int size, MeasureSpecMode mode) => size + (int)mode; } } } diff --git a/CustomRenderers/View/Droid/CameraStateListener.cs b/CustomRenderers/View/Droid/CameraStateListener.cs new file mode 100644 index 0000000000..4e01cdd91d --- /dev/null +++ b/CustomRenderers/View/Droid/CameraStateListener.cs @@ -0,0 +1,18 @@ +using System; +using Android.Hardware.Camera2; + +namespace CustomRenderer.Droid +{ + public class CameraStateListener : CameraDevice.StateCallback + { + public Action OnOpenedAction; + public Action OnDisconnectedAction; + public Action OnErrorAction; + public Action OnClosedAction; + + public override void OnOpened(CameraDevice camera) => OnOpenedAction?.Invoke(camera); + public override void OnDisconnected(CameraDevice camera) => OnDisconnectedAction?.Invoke(camera); + public override void OnError(CameraDevice camera, CameraError error) => OnErrorAction(camera, error); + public override void OnClosed(CameraDevice camera) => OnClosedAction(camera); + } +} diff --git a/CustomRenderers/View/Droid/CustomRenderer.Droid.csproj b/CustomRenderers/View/Droid/CustomRenderer.Droid.csproj index b976a42686..6047328cd9 100644 --- a/CustomRenderers/View/Droid/CustomRenderer.Droid.csproj +++ b/CustomRenderers/View/Droid/CustomRenderer.Droid.csproj @@ -58,7 +58,10 @@ - + + + + @@ -74,12 +77,14 @@ + + + + - - - \ No newline at end of file + diff --git a/CustomRenderers/View/Droid/MainActivity.cs b/CustomRenderers/View/Droid/MainActivity.cs old mode 100755 new mode 100644 index fd6ad5885d..6f80b8f7c4 --- a/CustomRenderers/View/Droid/MainActivity.cs +++ b/CustomRenderers/View/Droid/MainActivity.cs @@ -1,24 +1,16 @@ -using System; - -using Android.App; -using Android.Content; +using Android.App; using Android.Content.PM; -using Android.Runtime; -using Android.Views; -using Android.Widget; using Android.OS; namespace CustomRenderer.Droid { - [Activity (Label = "CustomRenderer.Droid", Icon = "@drawable/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] + [Activity (Label = "CustomRenderer.Droid", Icon = "@drawable/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity { protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); - global::Xamarin.Forms.Forms.Init (this, bundle); - LoadApplication (new App ()); } } diff --git a/CustomRenderers/View/Droid/Properties/AndroidManifest.xml b/CustomRenderers/View/Droid/Properties/AndroidManifest.xml old mode 100755 new mode 100644 index 85ab804c46..eed106b899 --- a/CustomRenderers/View/Droid/Properties/AndroidManifest.xml +++ b/CustomRenderers/View/Droid/Properties/AndroidManifest.xml @@ -1,7 +1,6 @@  - - - + + \ No newline at end of file diff --git a/CustomRenderers/View/Droid/Resources/Resource.designer.cs b/CustomRenderers/View/Droid/Resources/Resource.designer.cs index 7e57160cc2..293b2f061d 100644 --- a/CustomRenderers/View/Droid/Resources/Resource.designer.cs +++ b/CustomRenderers/View/Droid/Resources/Resource.designer.cs @@ -8322,361 +8322,361 @@ public partial class Id public const int buttonPanel = 2131230762; // aapt resource value: 0x7F08002B - public const int cancel_action = 2131230763; + public const int cameratexture = 2131230763; // aapt resource value: 0x7F08002C - public const int center = 2131230764; + public const int cancel_action = 2131230764; // aapt resource value: 0x7F08002D - public const int center_horizontal = 2131230765; + public const int center = 2131230765; // aapt resource value: 0x7F08002E - public const int center_vertical = 2131230766; + public const int center_horizontal = 2131230766; // aapt resource value: 0x7F08002F - public const int checkbox = 2131230767; + public const int center_vertical = 2131230767; // aapt resource value: 0x7F080030 - public const int chronometer = 2131230768; + public const int checkbox = 2131230768; // aapt resource value: 0x7F080031 - public const int clip_horizontal = 2131230769; + public const int chronometer = 2131230769; // aapt resource value: 0x7F080032 - public const int clip_vertical = 2131230770; + public const int clip_horizontal = 2131230770; // aapt resource value: 0x7F080033 - public const int collapseActionView = 2131230771; + public const int clip_vertical = 2131230771; // aapt resource value: 0x7F080034 - public const int container = 2131230772; + public const int collapseActionView = 2131230772; // aapt resource value: 0x7F080035 - public const int content = 2131230773; + public const int container = 2131230773; // aapt resource value: 0x7F080036 - public const int contentPanel = 2131230774; + public const int content = 2131230774; // aapt resource value: 0x7F080037 - public const int coordinator = 2131230775; + public const int contentPanel = 2131230775; + + // aapt resource value: 0x7F080038 + public const int coordinator = 2131230776; // aapt resource value: 0x7F080001 public const int CTRL = 2131230721; - // aapt resource value: 0x7F080038 - public const int custom = 2131230776; - // aapt resource value: 0x7F080039 - public const int customPanel = 2131230777; + public const int custom = 2131230777; // aapt resource value: 0x7F08003A - public const int decor_content_parent = 2131230778; + public const int customPanel = 2131230778; // aapt resource value: 0x7F08003B - public const int default_activity_button = 2131230779; + public const int decor_content_parent = 2131230779; // aapt resource value: 0x7F08003C - public const int design_bottom_sheet = 2131230780; + public const int default_activity_button = 2131230780; // aapt resource value: 0x7F08003D - public const int design_menu_item_action_area = 2131230781; + public const int design_bottom_sheet = 2131230781; // aapt resource value: 0x7F08003E - public const int design_menu_item_action_area_stub = 2131230782; + public const int design_menu_item_action_area = 2131230782; // aapt resource value: 0x7F08003F - public const int design_menu_item_text = 2131230783; + public const int design_menu_item_action_area_stub = 2131230783; // aapt resource value: 0x7F080040 - public const int design_navigation_view = 2131230784; + public const int design_menu_item_text = 2131230784; // aapt resource value: 0x7F080041 - public const int disableHome = 2131230785; + public const int design_navigation_view = 2131230785; // aapt resource value: 0x7F080042 - public const int edit_query = 2131230786; + public const int disableHome = 2131230786; // aapt resource value: 0x7F080043 - public const int end = 2131230787; + public const int edit_query = 2131230787; // aapt resource value: 0x7F080044 - public const int end_padder = 2131230788; + public const int end = 2131230788; // aapt resource value: 0x7F080045 - public const int enterAlways = 2131230789; + public const int end_padder = 2131230789; // aapt resource value: 0x7F080046 - public const int enterAlwaysCollapsed = 2131230790; + public const int enterAlways = 2131230790; // aapt resource value: 0x7F080047 - public const int exitUntilCollapsed = 2131230791; - - // aapt resource value: 0x7F080049 - public const int expanded_menu = 2131230793; + public const int enterAlwaysCollapsed = 2131230791; // aapt resource value: 0x7F080048 - public const int expand_activities_button = 2131230792; + public const int exitUntilCollapsed = 2131230792; // aapt resource value: 0x7F08004A - public const int fill = 2131230794; + public const int expanded_menu = 2131230794; - // aapt resource value: 0x7F08004D - public const int filled = 2131230797; + // aapt resource value: 0x7F080049 + public const int expand_activities_button = 2131230793; // aapt resource value: 0x7F08004B - public const int fill_horizontal = 2131230795; + public const int fill = 2131230795; + + // aapt resource value: 0x7F08004E + public const int filled = 2131230798; // aapt resource value: 0x7F08004C - public const int fill_vertical = 2131230796; + public const int fill_horizontal = 2131230796; - // aapt resource value: 0x7F08004E - public const int @fixed = 2131230798; + // aapt resource value: 0x7F08004D + public const int fill_vertical = 2131230797; // aapt resource value: 0x7F08004F - public const int flyoutcontent_appbar = 2131230799; + public const int @fixed = 2131230799; // aapt resource value: 0x7F080050 - public const int flyoutcontent_recycler = 2131230800; + public const int flyoutcontent_appbar = 2131230800; // aapt resource value: 0x7F080051 - public const int forever = 2131230801; + public const int flyoutcontent_recycler = 2131230801; + + // aapt resource value: 0x7F080052 + public const int forever = 2131230802; // aapt resource value: 0x7F080002 public const int FUNCTION = 2131230722; - // aapt resource value: 0x7F080052 - public const int ghost_view = 2131230802; - // aapt resource value: 0x7F080053 - public const int group_divider = 2131230803; + public const int ghost_view = 2131230803; // aapt resource value: 0x7F080054 - public const int home = 2131230804; + public const int group_divider = 2131230804; // aapt resource value: 0x7F080055 - public const int homeAsUp = 2131230805; + public const int home = 2131230805; // aapt resource value: 0x7F080056 - public const int icon = 2131230806; + public const int homeAsUp = 2131230806; // aapt resource value: 0x7F080057 - public const int icon_group = 2131230807; + public const int icon = 2131230807; // aapt resource value: 0x7F080058 - public const int ifRoom = 2131230808; + public const int icon_group = 2131230808; // aapt resource value: 0x7F080059 - public const int image = 2131230809; + public const int ifRoom = 2131230809; // aapt resource value: 0x7F08005A - public const int info = 2131230810; + public const int image = 2131230810; // aapt resource value: 0x7F08005B - public const int italic = 2131230811; + public const int info = 2131230811; // aapt resource value: 0x7F08005C - public const int item_touch_helper_previous_elevation = 2131230812; + public const int italic = 2131230812; // aapt resource value: 0x7F08005D - public const int labeled = 2131230813; + public const int item_touch_helper_previous_elevation = 2131230813; // aapt resource value: 0x7F08005E - public const int largeLabel = 2131230814; + public const int labeled = 2131230814; // aapt resource value: 0x7F08005F - public const int left = 2131230815; + public const int largeLabel = 2131230815; // aapt resource value: 0x7F080060 - public const int line1 = 2131230816; + public const int left = 2131230816; // aapt resource value: 0x7F080061 - public const int line3 = 2131230817; + public const int line1 = 2131230817; // aapt resource value: 0x7F080062 - public const int listMode = 2131230818; + public const int line3 = 2131230818; // aapt resource value: 0x7F080063 - public const int list_item = 2131230819; + public const int listMode = 2131230819; // aapt resource value: 0x7F080064 - public const int main_appbar = 2131230820; + public const int list_item = 2131230820; // aapt resource value: 0x7F080065 - public const int main_tablayout = 2131230821; + public const int main_appbar = 2131230821; // aapt resource value: 0x7F080066 - public const int main_toolbar = 2131230822; + public const int main_tablayout = 2131230822; // aapt resource value: 0x7F080067 - public const int main_viewpager = 2131230823; + public const int main_toolbar = 2131230823; // aapt resource value: 0x7F080068 - public const int masked = 2131230824; + public const int main_viewpager = 2131230824; // aapt resource value: 0x7F080069 - public const int media_actions = 2131230825; + public const int masked = 2131230825; // aapt resource value: 0x7F08006A - public const int message = 2131230826; + public const int media_actions = 2131230826; + + // aapt resource value: 0x7F08006B + public const int message = 2131230827; // aapt resource value: 0x7F080003 public const int META = 2131230723; - // aapt resource value: 0x7F08006B - public const int middle = 2131230827; - // aapt resource value: 0x7F08006C - public const int mini = 2131230828; + public const int middle = 2131230828; // aapt resource value: 0x7F08006D - public const int mtrl_child_content_container = 2131230829; + public const int mini = 2131230829; // aapt resource value: 0x7F08006E - public const int mtrl_internal_children_alpha_tag = 2131230830; + public const int mtrl_child_content_container = 2131230830; // aapt resource value: 0x7F08006F - public const int multiply = 2131230831; + public const int mtrl_internal_children_alpha_tag = 2131230831; // aapt resource value: 0x7F080070 - public const int navigation_header_container = 2131230832; + public const int multiply = 2131230832; // aapt resource value: 0x7F080071 - public const int never = 2131230833; + public const int navigation_header_container = 2131230833; // aapt resource value: 0x7F080072 - public const int none = 2131230834; + public const int never = 2131230834; // aapt resource value: 0x7F080073 - public const int normal = 2131230835; + public const int none = 2131230835; // aapt resource value: 0x7F080074 - public const int notification_background = 2131230836; + public const int normal = 2131230836; // aapt resource value: 0x7F080075 - public const int notification_main_column = 2131230837; + public const int notification_background = 2131230837; // aapt resource value: 0x7F080076 - public const int notification_main_column_container = 2131230838; + public const int notification_main_column = 2131230838; // aapt resource value: 0x7F080077 - public const int outline = 2131230839; + public const int notification_main_column_container = 2131230839; // aapt resource value: 0x7F080078 - public const int parallax = 2131230840; + public const int outline = 2131230840; // aapt resource value: 0x7F080079 - public const int parentPanel = 2131230841; + public const int parallax = 2131230841; // aapt resource value: 0x7F08007A - public const int parent_matrix = 2131230842; + public const int parentPanel = 2131230842; // aapt resource value: 0x7F08007B - public const int pin = 2131230843; + public const int parent_matrix = 2131230843; // aapt resource value: 0x7F08007C - public const int progress_circular = 2131230844; + public const int pin = 2131230844; // aapt resource value: 0x7F08007D - public const int progress_horizontal = 2131230845; + public const int progress_circular = 2131230845; // aapt resource value: 0x7F08007E - public const int radio = 2131230846; + public const int progress_horizontal = 2131230846; // aapt resource value: 0x7F08007F - public const int right = 2131230847; + public const int radio = 2131230847; // aapt resource value: 0x7F080080 - public const int right_icon = 2131230848; + public const int right = 2131230848; // aapt resource value: 0x7F080081 - public const int right_side = 2131230849; + public const int right_icon = 2131230849; // aapt resource value: 0x7F080082 - public const int save_image_matrix = 2131230850; + public const int right_side = 2131230850; // aapt resource value: 0x7F080083 - public const int save_non_transition_alpha = 2131230851; + public const int save_image_matrix = 2131230851; // aapt resource value: 0x7F080084 - public const int save_scale_type = 2131230852; + public const int save_non_transition_alpha = 2131230852; // aapt resource value: 0x7F080085 - public const int screen = 2131230853; + public const int save_scale_type = 2131230853; // aapt resource value: 0x7F080086 - public const int scroll = 2131230854; - - // aapt resource value: 0x7F08008A - public const int scrollable = 2131230858; + public const int screen = 2131230854; // aapt resource value: 0x7F080087 - public const int scrollIndicatorDown = 2131230855; + public const int scroll = 2131230855; + + // aapt resource value: 0x7F08008B + public const int scrollable = 2131230859; // aapt resource value: 0x7F080088 - public const int scrollIndicatorUp = 2131230856; + public const int scrollIndicatorDown = 2131230856; // aapt resource value: 0x7F080089 - public const int scrollView = 2131230857; + public const int scrollIndicatorUp = 2131230857; - // aapt resource value: 0x7F08008B - public const int search_badge = 2131230859; + // aapt resource value: 0x7F08008A + public const int scrollView = 2131230858; // aapt resource value: 0x7F08008C - public const int search_bar = 2131230860; + public const int search_badge = 2131230860; // aapt resource value: 0x7F08008D - public const int search_button = 2131230861; + public const int search_bar = 2131230861; // aapt resource value: 0x7F08008E - public const int search_close_btn = 2131230862; + public const int search_button = 2131230862; // aapt resource value: 0x7F08008F - public const int search_edit_frame = 2131230863; + public const int search_close_btn = 2131230863; // aapt resource value: 0x7F080090 - public const int search_go_btn = 2131230864; + public const int search_edit_frame = 2131230864; // aapt resource value: 0x7F080091 - public const int search_mag_icon = 2131230865; + public const int search_go_btn = 2131230865; // aapt resource value: 0x7F080092 - public const int search_plate = 2131230866; + public const int search_mag_icon = 2131230866; // aapt resource value: 0x7F080093 - public const int search_src_text = 2131230867; + public const int search_plate = 2131230867; // aapt resource value: 0x7F080094 - public const int search_voice_btn = 2131230868; - - // aapt resource value: 0x7F080096 - public const int selected = 2131230870; + public const int search_src_text = 2131230868; // aapt resource value: 0x7F080095 - public const int select_dialog_listview = 2131230869; + public const int search_voice_btn = 2131230869; // aapt resource value: 0x7F080097 - public const int shellcontent_appbar = 2131230871; + public const int selected = 2131230871; + + // aapt resource value: 0x7F080096 + public const int select_dialog_listview = 2131230870; // aapt resource value: 0x7F080098 - public const int shellcontent_toolbar = 2131230872; + public const int shellcontent_appbar = 2131230872; + + // aapt resource value: 0x7F080099 + public const int shellcontent_toolbar = 2131230873; // aapt resource value: 0x7F080004 public const int SHIFT = 2131230724; - // aapt resource value: 0x7F080099 - public const int shortcut = 2131230873; - // aapt resource value: 0x7F08009A - public const int showCustom = 2131230874; + public const int shortcut = 2131230874; // aapt resource value: 0x7F08009B - public const int showHome = 2131230875; + public const int showCustom = 2131230875; // aapt resource value: 0x7F08009C - public const int showTitle = 2131230876; + public const int showHome = 2131230876; // aapt resource value: 0x7F08009D - public const int sliding_tabs = 2131230877; + public const int showTitle = 2131230877; // aapt resource value: 0x7F08009E public const int smallLabel = 2131230878; @@ -8778,55 +8778,52 @@ public partial class Id public const int title_template = 2131230909; // aapt resource value: 0x7F0800BE - public const int toolbar = 2131230910; + public const int top = 2131230910; // aapt resource value: 0x7F0800BF - public const int top = 2131230911; + public const int topPanel = 2131230911; // aapt resource value: 0x7F0800C0 - public const int topPanel = 2131230912; + public const int touch_outside = 2131230912; // aapt resource value: 0x7F0800C1 - public const int touch_outside = 2131230913; + public const int transition_current_scene = 2131230913; // aapt resource value: 0x7F0800C2 - public const int transition_current_scene = 2131230914; + public const int transition_layout_save = 2131230914; // aapt resource value: 0x7F0800C3 - public const int transition_layout_save = 2131230915; + public const int transition_position = 2131230915; // aapt resource value: 0x7F0800C4 - public const int transition_position = 2131230916; + public const int transition_scene_layoutid_cache = 2131230916; // aapt resource value: 0x7F0800C5 - public const int transition_scene_layoutid_cache = 2131230917; + public const int transition_transform = 2131230917; // aapt resource value: 0x7F0800C6 - public const int transition_transform = 2131230918; + public const int uniform = 2131230918; // aapt resource value: 0x7F0800C7 - public const int uniform = 2131230919; + public const int unlabeled = 2131230919; // aapt resource value: 0x7F0800C8 - public const int unlabeled = 2131230920; + public const int up = 2131230920; // aapt resource value: 0x7F0800C9 - public const int up = 2131230921; + public const int useLogo = 2131230921; // aapt resource value: 0x7F0800CA - public const int useLogo = 2131230922; + public const int view_offset_helper = 2131230922; // aapt resource value: 0x7F0800CB - public const int view_offset_helper = 2131230923; + public const int visible = 2131230923; // aapt resource value: 0x7F0800CC - public const int visible = 2131230924; + public const int withText = 2131230924; // aapt resource value: 0x7F0800CD - public const int withText = 2131230925; - - // aapt resource value: 0x7F0800CE - public const int wrap_content = 2131230926; + public const int wrap_content = 2131230925; static Id() { @@ -9018,124 +9015,121 @@ public partial class Layout public const int browser_actions_context_menu_row = 2131427358; // aapt resource value: 0x7F0B001F - public const int design_bottom_navigation_item = 2131427359; + public const int CameraFragment = 2131427359; // aapt resource value: 0x7F0B0020 - public const int design_bottom_sheet_dialog = 2131427360; + public const int design_bottom_navigation_item = 2131427360; // aapt resource value: 0x7F0B0021 - public const int design_layout_snackbar = 2131427361; + public const int design_bottom_sheet_dialog = 2131427361; // aapt resource value: 0x7F0B0022 - public const int design_layout_snackbar_include = 2131427362; + public const int design_layout_snackbar = 2131427362; // aapt resource value: 0x7F0B0023 - public const int design_layout_tab_icon = 2131427363; + public const int design_layout_snackbar_include = 2131427363; // aapt resource value: 0x7F0B0024 - public const int design_layout_tab_text = 2131427364; + public const int design_layout_tab_icon = 2131427364; // aapt resource value: 0x7F0B0025 - public const int design_menu_item_action_area = 2131427365; + public const int design_layout_tab_text = 2131427365; // aapt resource value: 0x7F0B0026 - public const int design_navigation_item = 2131427366; + public const int design_menu_item_action_area = 2131427366; // aapt resource value: 0x7F0B0027 - public const int design_navigation_item_header = 2131427367; + public const int design_navigation_item = 2131427367; // aapt resource value: 0x7F0B0028 - public const int design_navigation_item_separator = 2131427368; + public const int design_navigation_item_header = 2131427368; // aapt resource value: 0x7F0B0029 - public const int design_navigation_item_subheader = 2131427369; + public const int design_navigation_item_separator = 2131427369; // aapt resource value: 0x7F0B002A - public const int design_navigation_menu = 2131427370; + public const int design_navigation_item_subheader = 2131427370; // aapt resource value: 0x7F0B002B - public const int design_navigation_menu_item = 2131427371; + public const int design_navigation_menu = 2131427371; // aapt resource value: 0x7F0B002C - public const int design_text_input_password_icon = 2131427372; + public const int design_navigation_menu_item = 2131427372; // aapt resource value: 0x7F0B002D - public const int FlyoutContent = 2131427373; + public const int design_text_input_password_icon = 2131427373; // aapt resource value: 0x7F0B002E - public const int mtrl_layout_snackbar = 2131427374; + public const int FlyoutContent = 2131427374; // aapt resource value: 0x7F0B002F - public const int mtrl_layout_snackbar_include = 2131427375; + public const int mtrl_layout_snackbar = 2131427375; // aapt resource value: 0x7F0B0030 - public const int notification_action = 2131427376; + public const int mtrl_layout_snackbar_include = 2131427376; // aapt resource value: 0x7F0B0031 - public const int notification_action_tombstone = 2131427377; + public const int notification_action = 2131427377; // aapt resource value: 0x7F0B0032 - public const int notification_media_action = 2131427378; + public const int notification_action_tombstone = 2131427378; // aapt resource value: 0x7F0B0033 - public const int notification_media_cancel_action = 2131427379; + public const int notification_media_action = 2131427379; // aapt resource value: 0x7F0B0034 - public const int notification_template_big_media = 2131427380; + public const int notification_media_cancel_action = 2131427380; // aapt resource value: 0x7F0B0035 - public const int notification_template_big_media_custom = 2131427381; + public const int notification_template_big_media = 2131427381; // aapt resource value: 0x7F0B0036 - public const int notification_template_big_media_narrow = 2131427382; + public const int notification_template_big_media_custom = 2131427382; // aapt resource value: 0x7F0B0037 - public const int notification_template_big_media_narrow_custom = 2131427383; + public const int notification_template_big_media_narrow = 2131427383; // aapt resource value: 0x7F0B0038 - public const int notification_template_custom_big = 2131427384; + public const int notification_template_big_media_narrow_custom = 2131427384; // aapt resource value: 0x7F0B0039 - public const int notification_template_icon_group = 2131427385; + public const int notification_template_custom_big = 2131427385; // aapt resource value: 0x7F0B003A - public const int notification_template_lines_media = 2131427386; + public const int notification_template_icon_group = 2131427386; // aapt resource value: 0x7F0B003B - public const int notification_template_media = 2131427387; + public const int notification_template_lines_media = 2131427387; // aapt resource value: 0x7F0B003C - public const int notification_template_media_custom = 2131427388; + public const int notification_template_media = 2131427388; // aapt resource value: 0x7F0B003D - public const int notification_template_part_chronometer = 2131427389; + public const int notification_template_media_custom = 2131427389; // aapt resource value: 0x7F0B003E - public const int notification_template_part_time = 2131427390; + public const int notification_template_part_chronometer = 2131427390; // aapt resource value: 0x7F0B003F - public const int RootLayout = 2131427391; + public const int notification_template_part_time = 2131427391; // aapt resource value: 0x7F0B0040 - public const int select_dialog_item_material = 2131427392; + public const int RootLayout = 2131427392; // aapt resource value: 0x7F0B0041 - public const int select_dialog_multichoice_material = 2131427393; + public const int select_dialog_item_material = 2131427393; // aapt resource value: 0x7F0B0042 - public const int select_dialog_singlechoice_material = 2131427394; + public const int select_dialog_multichoice_material = 2131427394; // aapt resource value: 0x7F0B0043 - public const int ShellContent = 2131427395; + public const int select_dialog_singlechoice_material = 2131427395; // aapt resource value: 0x7F0B0044 - public const int support_simple_spinner_dropdown_item = 2131427396; + public const int ShellContent = 2131427396; // aapt resource value: 0x7F0B0045 - public const int Tabbar = 2131427397; - - // aapt resource value: 0x7F0B0046 - public const int Toolbar = 2131427398; + public const int support_simple_spinner_dropdown_item = 2131427397; static Layout() { diff --git a/CustomRenderers/View/Droid/Resources/layout/CameraFragment.xml b/CustomRenderers/View/Droid/Resources/layout/CameraFragment.xml new file mode 100644 index 0000000000..7188bc19a7 --- /dev/null +++ b/CustomRenderers/View/Droid/Resources/layout/CameraFragment.xml @@ -0,0 +1,10 @@ + + + diff --git a/CustomRenderers/View/Droid/Resources/layout/Tabbar.axml b/CustomRenderers/View/Droid/Resources/layout/Tabbar.axml deleted file mode 100644 index 1187962a08..0000000000 --- a/CustomRenderers/View/Droid/Resources/layout/Tabbar.axml +++ /dev/null @@ -1,12 +0,0 @@ - - \ No newline at end of file diff --git a/CustomRenderers/View/Droid/Resources/layout/Toolbar.axml b/CustomRenderers/View/Droid/Resources/layout/Toolbar.axml deleted file mode 100644 index eee97c6941..0000000000 --- a/CustomRenderers/View/Droid/Resources/layout/Toolbar.axml +++ /dev/null @@ -1,9 +0,0 @@ - - \ No newline at end of file diff --git a/CustomRenderers/View/UWP/CustomRenderer.UWP.csproj b/CustomRenderers/View/UWP/CustomRenderer.UWP.csproj index 409cc2a124..84bffbfd89 100755 --- a/CustomRenderers/View/UWP/CustomRenderer.UWP.csproj +++ b/CustomRenderers/View/UWP/CustomRenderer.UWP.csproj @@ -137,4 +137,4 @@ 14.0 - \ No newline at end of file + diff --git a/CustomRenderers/View/iOS/CustomRenderer.iOS.csproj b/CustomRenderers/View/iOS/CustomRenderer.iOS.csproj index 880e734ed3..7b543ce274 100644 --- a/CustomRenderers/View/iOS/CustomRenderer.iOS.csproj +++ b/CustomRenderers/View/iOS/CustomRenderer.iOS.csproj @@ -21,7 +21,7 @@ 4 false x86_64 - SdkOnly + None true true iPhone Developer @@ -116,4 +116,4 @@ - \ No newline at end of file +