I must use Android 7 for my app because I am using .NETStandard libraries. Media plugin is not compatible with Android 7. Is there any way to keep my code and use Android 7?
using Plugin.Media;
using Plugin.Media.Abstractions;
private async void TakePhoto_Clicked(object sender, EventArgs e)
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await DisplayAlert("No camera", "No available camera", "OK");
return;
}
var file = await CrossMedia.Current.TakePhotoAsync(new StoreCameraMediaOptions
{
SaveToAlbum = true,
Directory = "",
Name = ""
});
if (file == null) return;
PathLabel.Text = file.AlbumPath;
MainImage.Source = ImageSource.FromStream(() =>
{
var stream = file.GetStream();
file.Dispose();
return stream;
});
}
Any suggestions?
Related
I'm working in a project which receives notification using Firebase Cloud Messaging and plays custom sounds when the payload reach the app.It works perfectly while running on Android, but on iOS it only plays the pattern sound of iOS, never plays the custom sound. I already put the audios inside Resource folder, try copying the audio files to the ios folder but even this way, the app never find the .wav files or maybe for some reason it only plays the pattern sound. Here some excerpt of my notification.service.dart class:
class NotificationService
late FlutterLocalNotificationsPlugin plugin;
late AndroidNotificationDetails androidDetails;
```
NotificationService() {
plugin = FlutterLocalNotificationsPlugin();
_setupNotification();
}
_setupNotification() async {
print('_setupNotification');
await _setupTimeZone();
await _initialize();
}
_setupTimeZone() async {
print('_setupTimeZone');
tz.initializeTimeZones();
final String? timeZoneName = await FlutterNativeTimezone.getLocalTimezone();
tz.setLocalLocation(tz.getLocation(timeZoneName!));
}
_initialize() async {
print('notification _initialize');
void _requestPermissions(){
plugin.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()
?._requestPermissions(
alert: true,
badge: true,
sound: true,
);
}
const ios = IOSInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
)
const android = AndroidInitializationSettings('#mipmap/ic_launcher');
await plugin.initialize(
const InitializationSettings(
android: android,
iOS: ios,
),
onSelectNotification: _onSelectNotification,
);
}
_onSelectNotification(String? payload) {
print('_onSelectNotification');
if (payload != null && payload.isNotEmpty) {
var array = payload.split('/');
if (array.length == 2) {
var acao = int.tryParse(array[0]);
var idDispositivo = array[1].toString();
if (acao != null && idDispositivo.isNotEmpty) {
int disIndex = serverConnection.equipamentos
.indexWhere((x) => x.serverId == idDispositivo);
if (disIndex != -1) {
DispositivoModel dis = serverConnection.equipamentos[disIndex];
if (dis.sep != null) {
sepService.showDialogAlertaAcionado(dis);
} else if (dis.gate != null) {
gateService.showGateStatus(
Routes.navigatorKey!.currentContext!, dis);
}
}
}
}
Navigator.of(Routes.navigatorKey!.currentContext!)
.pushReplacementNamed(payload);
}
}
showNotification(RemoteMessage message) async {
print('showNotification');
var user = await accountService.getUser();
print('isLoggedIn ${accountService.isLoggedIn}');
print('isLoggedIn ${user}');
if (accountService.isLoggedIn == true && user != null) {
_conteudoNotificacao(message);
}
}
_conteudoNotificacao(RemoteMessage message) {
RemoteMessageData data = RemoteMessageData.fromJson(message.data);
sound = data.sound != 'default' && data.sound != null
? data.sound ?? 'gate'
: 'gate';
print('sound $sound');
AndroidNotificationChannel channel = AndroidNotificationChannel(
'Adviser $sound',
'AdviserChannel',
description: '',
importance: Importance.high,
sound: RawResourceAndroidNotificationSound(sound),
enableVibration: true,
enableLights: true,
);
plugin = FlutterLocalNotificationsPlugin();
String title = 'SouHi';
if (data.route != null) {
var array = data.route!.split('/');
if (array.length == 2) {
var acao = int.tryParse(array[0]);
var idDispositivo = array[1].toString();
if (acao != null && idDispositivo.isNotEmpty) {
int disIndex = serverConnection.equipamentos
.indexWhere((x) => x.serverId == idDispositivo);
if (disIndex != -1) {
DispositivoModel dis = serverConnection.equipamentos[disIndex];
if (dis.sep != null) {
title = 'SEP';
} else if (dis.gate != null) {
title = 'Portão';
}
}
}
}
}
plugin.show(
message.hashCode,
title,
data.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription: channel.description,
importance: channel.importance,
priority: Priority.high,
showProgress: true,
enableLights: true,
enableVibration: true,
visibility: NotificationVisibility.public,
sound: RawResourceAndroidNotificationSound(sound),
),
iOS: IOSNotificationDetails(
presentSound: true,
presentBadge: true,
presentAlert: true,
sound: sound,
),
),
payload: data.route,
);
}
checkForNotifications() async {
print('checkForNotifications');
final details = await plugin.getNotificationAppLaunchDetails();
if (details != null && details.didNotificationLaunchApp) {
_onSelectNotification(details.payload);
}
}
stopNotification(int? id) async {
print('stopNotification');
if (id != null) {
await plugin.cancel(id);
} else {
await plugin.cancelAll();
}
}
}
I have an app that uses Oidc to login. This works fine in most cases. But there is one iphone 8+ running iOS 15.6.1 that keeps downloading the return url from the login request.
i use Xamarin forms v 5.0.0.2515, Xamarin.Essentials 1.7.3, Xamarin.CommunityToolkit 2.0.5
IdentityModel.OidcClient 3.1.2
The problem is that it works on all other devices and i can't replicate it in a dev environment.
public class WebauthenticatorBrowser : IBrowser
{
private readonly string callbackUrl;
public WebauthenticatorBrowser(string? callbackUrl = null)
{
this.callbackUrl = callbackUrl;
}
public async Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default)
{
try
{
var callBack = string.IsNullOrWhiteSpace(callbackUrl) ? options.EndUrl : callbackUrl;
var authResult = await WebAuthenticator.AuthenticateAsync(new WebAuthenticatorOptions
{
Url = new Uri(options.StartUrl),
CallbackUrl = new Uri(callBack),
PrefersEphemeralWebBrowserSession = false
});
var authorizeResponse = ToRawIdentityUrl(options.EndUrl, authResult);
return new BrowserResult()
{
Response = authorizeResponse
};
}
catch (Exception ex)
{
return new BrowserResult()
{
Error = ex.Message
};
}
}
private string ToRawIdentityUrl(string redirectUrl, WebAuthenticatorResult result)
{
IEnumerable<string> parameters = result.Properties.Select(x => $"{x.Key}={x.Value}");
var values = string.Join("&", parameters);
return $"{redirectUrl}#{values}" ;
}
}
In my oidc class where i create my client
private async Task createOidcClient() {
document = await httpClient.GetDiscoveryDocumentAsync(idsUrl);
if (document.IsError) {
alertPresenter.Alert("Connection issue", "please try again");
return;
}
var options = new OidcClientOptions {
Authority = document.Issuer,
ClientId = "---clientID-",
Scope = "-- all the scopes --",
RedirectUri = "-- callback url--",
PostLogoutRedirectUri = " -- callback url--",
Browser = new WebauthenticatorBrowser(),
ResponseMode = OidcClientOptions.AuthorizeResponseMode.Redirect
};
client = new OidcClient(options);
}
the login call in the OicdClass
public async Task<LoginResult> Login() {
await createOidcClient();
var result = await client.LoginAsync(new LoginRequest());
if (result.IsError) return result;
setAuthHeader(result.AccessToken);
await accessTokenUpdater.SetAllTokens(result.AccessToken, result.RefreshToken, result.IdentityToken);
return result;
}
Trying to get my App to work on iOS now - and wow! This is way harder than I thought.
OK, So I have a filepicker that picks a file. pretty simple (code below.) . With iOs. The file picker dialog opens, and I can click the file I want , but nothing happens - until I hit cancel, then the code continues to run... (pickresult != null).. it is null - code stops...
https://photos.app.goo.gl/fGD5SPtCqdMYE8AS7
var customFileType =
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.iOS, new[] { "com.microsoft.word.doc" } }, // or general UTType values
{ DevicePlatform.Android, new[] { "application/doc" } },
{ DevicePlatform.UWP, new[] { ".doc" } },
{ DevicePlatform.Tizen, new[] { "*/*" } },
{ DevicePlatform.macOS, new[] { "doc" } }, // or general UTType values
});
string OutupPath;
var pickResult = await FilePicker.PickAsync(new PickOptions
{
FileTypes = customFileType,
PickerTitle = "Select Doc File"
}) ;
if (pickResult != null)
{
OutupPath = pickResult.FullPath.ToString();
App.docFilePath = OutupPath;
LoadingText = pickResult.FileName.ToString();
DbFileName = LoadingText;
}
else {
return;
}
Note - I am developing on Win10 vs 2022. And I am new to iOs. I just got my apple dev account up and running. The Android version and Windows version of this work flawlessly.
Not sure where I should be looking to solve this glitch.
https://photos.app.goo.gl/fGD5SPtCqdMYE8AS7
Original code with no options
private async void Button_Clicked(object sender, EventArgs e)
{
var pickResult = await FilePicker.PickAsync();
if (pickResult != null)
{
FileLabel.Text = pickResult.FileName.ToString();
}
else {
await DisplayAlert("Alert", "No File Selected", "OK");
}
}
What about adding org.openxmlformats.wordprocessingml.document in file types as well ?
The actual type for the file may be docx, so just have a try .
Refer to
https://learn.microsoft.com/en-us/answers/questions/362776/xamarin-file-picker-file-types-for-ios.html
https://stackoverflow.com/a/51592769/8187800
Update (specify doc type in PickOptions)
var customFileType =
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.iOS, new[] { "com.microsoft.word.doc","org.openxmlformats.wordprocessingml.document" } }, // or general UTType values
{ DevicePlatform.Android, new[] { "application/doc" } },
{ DevicePlatform.UWP, new[] { ".doc" } },
{ DevicePlatform.Tizen, new[] { "*/*" } },
{ DevicePlatform.macOS, new[] { "doc" } }, // or general UTType values
});
var pickResult = await FilePicker.PickAsync(new PickOptions
{
FileTypes = customFileType,
PickerTitle = "Select Doc File"
}) ;
I have a Xamarin mobile app who include a WebView.
This one display an Angular Website.
When I change my mobile orientation, our Angular code doesn't catch this event on iOS (WKWebView).
But I don't have any troubles with the Android WebView, Resize and screen orientation events are catched.
Then I supposed my problem is on the iOS Xamarin part.
Angular window resize event catch :
#HostListener('window:resize', ['$event'])
resizeMap() {
this.mapWidth = window.innerWidth;
this.mapHeight = window.innerHeight;
this.changeDetectorRef.detectChanges();
this.mapService.map.updateSize();
}
Angular screen orientation event catch
screen.orientation.addEventListener('change', () => {
const { type } = screen.orientation;
if (!type.startsWith('portrait')) {
Swal.fire(
this.l('Information'),
this.l('LandscapeOrientationNotRecommended'),
'info'
);
// alert(this.l('LandscapeOrientationNotRecommended'));
}
});
Custom Xamarin iOS WKWebView
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == "Source")
{
if (Element.Source != null)
{
Control.LoadRequest(new NSUrlRequest(new NSUrl(Element.Source)));
}
}
}
protected override void OnElementChanged(ElementChangedEventArgs<CustomWebView> e)
{
base.OnElementChanged(e);
this.AutoresizingMask = UIViewAutoresizing.FlexibleDimensions;
this.ContentMode = UIViewContentMode.ScaleToFill;
if (e.OldElement != null)
{
userController.RemoveAllUserScripts();
userController.RemoveScriptMessageHandler("invokeAction");
var hybridWebView = e.OldElement as CustomWebView;
hybridWebView.Cleanup();
}
if (e.NewElement != null)
{
if (Control == null)
{
userController = new WKUserContentController();
var script = new WKUserScript(new NSString(JavaScriptFunction), WKUserScriptInjectionTime.AtDocumentEnd, false);
userController.AddUserScript(script);
userController.AddScriptMessageHandler(this, "invokeAction");
var config = new WKWebViewConfiguration { UserContentController = userController };
var webView = new WKWebView(Frame, config);
SetNativeControl(webView);
}
if (Element.Source != null)
{
string contentDirectoryPath = Path.Combine(NSBundle.MainBundle.BundlePath);
Control.LoadHtmlString(Element.Source, new NSUrl(contentDirectoryPath, true));
}
}
}
public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
{
if (message.Body != null)
{
Element.InvokeAction(message.Body.ToString());
}
}
}
}
If someone have any suggestions ?
Thanks for your help.
I am working on office365 authentication , took the code from github, for android build it is working fine, but in iOS not getting data response (token) from office365.
project link - https://github.com/Azure-Samples/active-directory-xamarin-native-v2
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
async void OnSignInSignOut(object sender, EventArgs e)
{
AuthenticationResult authResult = null;
IEnumerable<IAccount> accounts = await App.PCA.GetAccountsAsync();
try
{
if (btnSignInSignOut.Text == "Sign in")
{
// let's see if we have a user in our belly already
try
{
IAccount firstAccount = accounts.FirstOrDefault();
authResult = await App.PCA.AcquireTokenSilent(App.Scopes, firstAccount)
.ExecuteAsync();
await RefreshUserDataAsync(authResult.AccessToken).ConfigureAwait(false);
Device.BeginInvokeOnMainThread(() => { btnSignInSignOut.Text = "Sign out"; });
}
catch (MsalUiRequiredException ex)
{
try
{
authResult = await App.PCA.AcquireTokenInteractive(App.Scopes)
.WithParentActivityOrWindow(App.ParentWindow)
.ExecuteAsync();
await RefreshUserDataAsync(authResult.AccessToken);
Device.BeginInvokeOnMainThread(() => { btnSignInSignOut.Text = "Sign out"; });
}
catch(Exception ex2)
{
slUser.IsVisible = true;
}
}
}
else
{
while (accounts.Any())
{
await App.PCA.RemoveAsync(accounts.FirstOrDefault());
accounts = await App.PCA.GetAccountsAsync();
}
slUser.IsVisible = false;
Device.BeginInvokeOnMainThread(() => { btnSignInSignOut.Text = "Sign in"; });
}
}
catch (Exception ex)
{
}
}
public async Task RefreshUserDataAsync(string token)
{
//get data from API
slUser.IsVisible = true;
HttpClient client = new HttpClient();
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me");
message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", token);
HttpResponseMessage response = await client.SendAsync(message);
string responseString = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
JObject user = JObject.Parse(responseString);
slUser.IsVisible = true;
Device.BeginInvokeOnMainThread(() =>
{
lblDisplayName.Text = user["displayName"].ToString();
lblGivenName.Text = user["givenName"].ToString();
lblId.Text = user["id"].ToString();
lblSurname.Text = user["surname"].ToString();
lblUserPrincipalName.Text = user["userPrincipalName"].ToString();
// just in case
btnSignInSignOut.Text = "Sign out";
});
}
else
{
await DisplayAlert("Something went wrong with the API call", responseString, "Dismiss");
}
}
}
}
I should get the same as android build, but in iOS not getting token from office365 server, seems like certificate issue, Below lines I found which are need to follow, in order to work in iOS
Also, in order to make the token cache work and have the AcquireTokenSilentAsync work multiple steps must be followed :
1)Enable Keychain access in your Entitlements.plist file and specify in the Keychain Groups your bundle identifier.
2)In your project options, on iOS Bundle Signing view, select your Entitlements.plist file for the Custom Entitlements field.
3) When signing a certificate, make sure XCode uses the same Apple Id.
Above are from Microsoft website
below is Entitlements.plist file details -
keychain-access-groups I am using $(AppIdentifierPrefix)com.oauth.office365 where com.oauth.office365 is my bundle identifier