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.
Related
I was trying to enable speaker for my VoIP call using Linphone in Xamarin forms. I'm using the following code
public void PlayUsingEarSpeaker()
{
AudioManager mAudioManager = (AudioManager)Android.App.Application.Context.GetSystemService(Context.AudioService);
mAudioManager.Mode = Mode.InCall;
mAudioManager.SpeakerphoneOn = true;
mAudioManager.SetStreamVolume(Stream.VoiceCall, mAudioManager.GetStreamMaxVolume(Stream.VoiceCall), VolumeNotificationFlags.ShowUi);
}
I have also added the permission for modifying the audio settings.
protected override void OnResume()
{
base.OnResume();
//RegisterReceiver(receiver, new IntentFilter("org.linphone.core.action.PUSH_RECEIVED"));
System.Console.WriteLine("MyBroadcastReceiver registered");
if (Int32.Parse(global::Android.OS.Build.VERSION.Sdk) >= 23)
{
List<string> Permissions = new List<string>();
if (this.CheckSelfPermission(Manifest.Permission.Camera) != Permission.Granted)
{
Permissions.Add(Manifest.Permission.Camera);
}
if (this.CheckSelfPermission(Manifest.Permission.RecordAudio) != Permission.Granted)
{
Permissions.Add(Manifest.Permission.RecordAudio);
}
if (this.CheckSelfPermission(Manifest.Permission.ModifyAudioSettings) != Permission.Granted)
{
Permissions.Add(Manifest.Permission.ModifyAudioSettings);
}
if (Permissions.Count > 0)
{
this.RequestPermissions(Permissions.ToArray(), PERMISSIONS_REQUEST);
}
}
and in Android Manifest
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
I even tried to set the stream type to music but still my audio doesn't come from the phone speaker. I have no clue how to fix this. Any suggestions?
in my Xamarin.Forms project I use OneSignal for notifications. In iOS Xamarin.Forms.Application.Current.MainPage = new NavigationPage(new NotificationPage()); worked but in Android this not work. I tried to use messaging center to communicate with PCL project. It worked when app is background but not working when app is closed. How can I redirect notification specific page when notification received in Android? Thanks
Note : Code edited and issue solved, I used shared preferences to control if app launched from notification or not. Then I Load xamarin.Forms application.
You can use below method before LoadApplication() method is called.
if (Intent.Extras != null)
{
foreach (var key in Intent.Extras.KeySet())
{
if (key != null)
{
var value = Intent.Extras.GetString(key);
Log.Debug(TAG, "Key: {0} Value: {1}", key, value);
}
}
}
LoadApplication(new App());
You have to set intent.putExtra() method in OnMessageReceivedMethod().
intent.PutExtra("Key", "value");
Then you can use redirection in App.xaml.cs file based on this key value. Because in android when notification is open while app is reinitialized.
I think you don't need to put below condition.
if (extrasList[0] == "true")
{
LoadApplication(new App(true));
}
else
{
LoadApplication(new App(false));
}
First Store your value global level so you can use it in App.cs file. Just use below code to handle page navigation in App() class like.
if (Device.RuntimePlatform == Device.Android)
{
if (YourKey == "true")
{
//handle that page navigation
}
else
{
//Default Page of App
}
}
Note : Code edited and issue solved, I used shared preferences to control if app launched from notification or not. Then I Load xamarin.Forms application
public class MainActivity :
global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
InitializeUI();
global::Xamarin.Forms.Forms.Init(this, bundle);
global::Xamarin.FormsMaps.Init(this, bundle);
ImageCircleRenderer.Init();
tV = new TextView(this);
resources = this.Resources;
OneSignal.Current.StartInit("***APP ID***")
.InFocusDisplaying(OSInFocusDisplayOption.Notification)
.HandleNotificationReceived(HandleNotificationReceived)
.HandleNotificationOpened(HandleNotificationOpened)
.EndInit();
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(this);
var LaunchFromNotification = prefs.GetString("is_notification_received", "false");
if (LaunchFromNotification == "true")
{
LoadApplication(new App(true));
}
else
{
LoadApplication(new App(false));
}
OneSignal.Current.IdsAvailable(IdsAvailable); //Lets you retrieve the OneSignal player id and push token.
}
}
private static void HandleNotificationOpened(OSNotificationOpenedResult result)
{
ISharedPreferences prefs = PreferenceManager.GetDefaultSharedPreferences(Android.App.Application.Context);
ISharedPreferencesEditor editor = prefs.Edit();
editor.PutString("is_notification_received", "true");
editor.Apply();
}
protected override void OnResume()
{
base.OnResume();
ISharedPreferences prefs =
PreferenceManager.GetDefaultSharedPreferences(this);
ISharedPreferencesEditor editor = prefs.Edit();
editor.Remove("is_notification_received");
editor.PutString("is_notification_received", "false");
editor.Apply();
}
I am using a custom button renderer for google sign In in xamarin forms page its working fine I get the signin resultin MainActivity Now i want to send this data from MainActivity and AppDelegate to the Particular page in Xamarin Forms.
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == 9001)
{
Utilities.Configuration.UpdateConfigValue(Utilities.Constants.loggedInflag,string.Empty);
GoogleSignInResult result = Android.Gms.Auth.Api.Auth.GoogleSignInApi.GetSignInResultFromIntent(data);
if (result.IsSuccess)
{
GoogleSignInAccount acct = result.SignInAccount;
var token = acct.IdToken;
//I wan to send the 'accnt' to a Page in xamarin forms
}
else
{
//Signin Failure send response to Page in xamarin forms
}
}
}
Xamarin.Forms runs only in one Activity on Android. So if your url request comes out in a different Activity, you have to switch back to the MainActivity before you can use the normal XF navigation.
I do this when a user opens a file associated with my app.
[Activity(Label = "LaunchFileActivity")]
public class LaunchFileActivity : Activity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
if (Intent.Data != null)
{
var uri = Intent.Data;
if (uri != null)
{
Intent i = new Intent(this, typeof(MainActivity));
i.AddFlags(ActivityFlags.ReorderToFront);
i.PutExtra("fileName", uri.Path);
this.StartActivity(i);
}
}
this.FinishActivity(0);
}
}
And in MainActivity:
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
Intent = intent;
}
protected override void OnPostResume()
{
base.OnPostResume();
if (Intent.Extras != null)
{
string fileName = Intent.Extras.GetString("fileName");
if (!string.IsNullOrEmpty(fileName))
{
// do something with fileName
}
Intent.RemoveExtra("fileName");
}
}
Xamarin forms runs on one activity, which is most like your main activity.
There are two sample projects that show you how to communicate between native and form parts of the code, which can be found here
https://github.com/xamarin/xamarin-forms-samples/tree/master/Forms2Native
https://github.com/xamarin/xamarin-forms-samples/tree/master/Native2Forms
However, to answer your question, you would do something like the following
private const int MyRequestCode = 101;
//Start activity for result
var contactPickerIntent = new Intent(Intent.ActionPick, Android.Provider.ContactsContract.Contacts.ContentUri);
context.StartActivityForResult(contactPickerIntent, MyRequestCode);
and then in your main activity (the activity that initializes your xamarin forms application (using global::Xamarin.Forms.Forms.Init(this, bundle);)
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
if (requestCode == MyRequestCode && resultCode == Result.Ok)
{
}
}
Im always getting an error of Java.Lang.SecurityException: getLine1NumberForDisplay: Neither user 10710 nor current process has android.permission.READ_SMS. Even if I already Added the READ_SMS in AndroidManifest.xml
MyCode:
public string GetNumber()
{
TelephonyManager telephonyManager = (TelephonyManager)GetSystemService(TelephonyService);
return telephonyManager.Line1Number;
}
Thanks in Advance and Good Day :D
This is a really simple runtime permission request example.
I would highly recommend reading the Xamarin blog post and the Android doc linked below as you should show the user "why" you are requesting permission before the system dialog shows up.
[Activity(Label = "RunTimePermissions", MainLauncher = true, Icon = "#mipmap/icon")]
public class MainActivity : Activity
{
const int PermissionSMSRequestCode = 99;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
Button button = FindViewById<Button>(Resource.Id.myButton);
button.Click += delegate {
if ((int)Build.VERSION.SdkInt < 23) // Permissions accepted by the user during app install
DoSomeWork();
var permission = BaseContext.CheckSelfPermission(Manifest.Permission.ReadSms);
if (permission == Android.Content.PM.Permission.Granted) // Did the user already grant permission?
DoSomeWork();
else // Ask the user to allow/deny permission request
RequestPermissions(new string[] { Manifest.Permission.ReadSms }, PermissionSMSRequestCode);
};
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Android.Content.PM.Permission[] grantResults)
{
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PermissionSMSRequestCode)
{
if ((grantResults.Count() > 0) && (grantResults[0] == Android.Content.PM.Permission.Granted))
DoSomeWork();
else
Log.Debug("PERM", "The user denied access!");
}
}
protected void DoSomeWork()
{
Log.Debug("PERM", "We have permission, so do something with it");
}
}
Ref: Requesting Runtime Permissions in Android Marshmallow
Ref: Requesting Permissions at Run Time
Our application should have the functionality to save Application files to Google Drive. Of course, using the local configured account.
From Android API i tried to figure out some clue. But android API with Xamarin implementation seems very tough for me.
I have installed Google Play Services- Drive from Xamarin Components but there are no examples listed from which we can refer the flow and functionality.
The basic steps (see the link below for full details):
Create GoogleApiClient with the Drive API and Scope
Try to connect (login) the GoogleApiClient
The first time you try to connect it will fail as the user has not selected a Google Account that should be used
Use StartResolutionForResult to handle this condition
When GoogleApiClient is connected
Request a Drive content (DriveContentsResult) to write the file contents to.
When the result is obtained, write data into the Drive content.
Set the metadata for the file
Create the Drive-based file with the Drive content
Note: This example assumes that you have Google Drive installed on your device/emulator and you have registered your app in Google's Developer API Console with the Google Drive API Enabled.
C# Example:
[Activity(Label = "DriveOpen", MainLauncher = true, Icon = "#mipmap/icon")]
public class MainActivity : Activity, GoogleApiClient.IConnectionCallbacks, IResultCallback, IDriveApiDriveContentsResult
{
const string TAG = "GDriveExample";
const int REQUEST_CODE_RESOLUTION = 3;
GoogleApiClient _googleApiClient;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Main);
Button button = FindViewById<Button>(Resource.Id.myButton);
button.Click += delegate
{
if (_googleApiClient == null)
{
_googleApiClient = new GoogleApiClient.Builder(this)
.AddApi(DriveClass.API)
.AddScope(DriveClass.ScopeFile)
.AddConnectionCallbacks(this)
.AddOnConnectionFailedListener(onConnectionFailed)
.Build();
}
if (!_googleApiClient.IsConnected)
_googleApiClient.Connect();
};
}
protected void onConnectionFailed(ConnectionResult result)
{
Log.Info(TAG, "GoogleApiClient connection failed: " + result);
if (!result.HasResolution)
{
GoogleApiAvailability.Instance.GetErrorDialog(this, result.ErrorCode, 0).Show();
return;
}
try
{
result.StartResolutionForResult(this, REQUEST_CODE_RESOLUTION);
}
catch (IntentSender.SendIntentException e)
{
Log.Error(TAG, "Exception while starting resolution activity", e);
}
}
public void OnConnected(Bundle connectionHint)
{
Log.Info(TAG, "Client connected.");
DriveClass.DriveApi.NewDriveContents(_googleApiClient).SetResultCallback(this);
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_RESOLUTION)
{
switch (resultCode)
{
case Result.Ok:
_googleApiClient.Connect();
break;
case Result.Canceled:
Log.Error(TAG, "Unable to sign in, is app registered for Drive access in Google Dev Console?");
break;
case Result.FirstUser:
Log.Error(TAG, "Unable to sign in: RESULT_FIRST_USER");
break;
default:
Log.Error(TAG, "Should never be here: " + resultCode);
return;
}
}
}
void IResultCallback.OnResult(Java.Lang.Object result)
{
var contentResults = (result).JavaCast<IDriveApiDriveContentsResult>();
if (!contentResults.Status.IsSuccess) // handle the error
return;
Task.Run(() =>
{
var writer = new OutputStreamWriter(contentResults.DriveContents.OutputStream);
writer.Write("Stack Overflow");
writer.Close();
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.SetTitle("New Text File")
.SetMimeType("text/plain")
.Build();
DriveClass.DriveApi
.GetRootFolder(_googleApiClient)
.CreateFile(_googleApiClient, changeSet, contentResults.DriveContents);
});
}
public void OnConnectionSuspended(int cause)
{
throw new NotImplementedException();
}
public IDriveContents DriveContents
{
get
{
throw new NotImplementedException();
}
}
public Statuses Status
{
get
{
throw new NotImplementedException();
}
}
}
Ref: https://developers.google.com/drive/android/create-file