searching nearby available bluetooth in Xamarin android 9 - c#

I want to search for available bluetooth but I can't find a solution that works in Xamarin android 9,

Firstly, add the following permissions to AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
And for Bluetooth scan you could use the plugin Plugin.BLE from Nuget .
Usage
ObservableCollection<Plugin.BLE.Abstractions.Contracts.IDevice> deviceList;
deviceList = new ObservableCollection<Plugin.BLE.Abstractions.Contracts.IDevice>();
var adapter = CrossBluetoothLE.Current.Adapter;
adapter.DeviceDiscovered += (s, a) =>
{
Console.WriteLine(a.Device.Name);
deviceList.Add(a.Device);
};
await adapter.StartScanningForDevicesAsync();
For more details and usage of the plugin you could check https://github.com/xabre/xamarin-bluetooth-le .

Related

.net Maui camera throwing error about write external storage

Anyone worked out how to take a photo in Maui on android api 31 using MediaPicker?
Lots of people getting the same error as this but I haven’t found a solution yet. I believe this version of Android doesn’t have the same permissions and so MediaPicker doesn’t seem to work with it. As soon as I call the camera I get the following:
Error: Microsoft.Maui.ApplicationModel.PermissionException: 'StorageWrite permission was not granted: Denied'
Android.Manifest entries:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACTION_OPEN_DOCUMENT" />
<uses-permission android:name="android.permission.ACTION_OPEN_DOCUMENT_TREE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<!-- Required to maintain app compatibility. -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<queries>
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
</queries>
Code:
private async void TakePhotoPressed()
{
MediaPickerOptions options = new MediaPickerOptions();
options.Title = "Add Attachment";
FileResult result = await MediaPicker.Default.CapturePhotoAsync(options);
...
}
Note that if I don't request StorageWrite permission I get the following error:
Microsoft.Maui.ApplicationModel.PermissionException: 'You need to declare using the permission: android.permission.WRITE_EXTERNAL_STORAGE in your AndroidManifest.xml'
So, bit of a catch 22 situation...
Thanks

Failing to request permissions in .NET MAUI

Good day.
In all my applications, requesting user permissions is not showing the permissions popup in Android.
I am using the FileSaver in the .NET MAUI CommunityToolkit and as I understand, the SaveAsync method is supposed to ask for permission and save to disk.
async Task SaveDocument(MemoryStream memoryStream)
{
try
{
//var fileLocation = await FileSaver.Default.SaveAsync($"{Guid.NewGuid()}.docx", memoryStream, new CancellationToken());
var someLocation = await FileSaver.SaveAsync($"{Guid.NewGuid()}.docx", memoryStream, new CancellationToken());
await Toast.Make($"File is saved: {someLocation}").Show();
}
catch (Exception ex)
{
await Toast.Make($"File is not saved, {ex.Message}").Show();
}
}
The catch statement is being hit with error message permissions not granted.
Here is my AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="#mipmap/appicon" android:roundIcon="#mipmap/appicon_round" android:supportsRtl="true">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" />
</provider>
</application>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
As such, I thought I would do it using Permission API in .NET MAUI as implemented below
public static async Task<bool> CheckForStoragePermission()
{
bool permitted = false;
var status = await Permissions.CheckStatusAsync<Permissions.StorageWrite>();
if (status == PermissionStatus.Granted)
permitted = true;
else
{
status = await Permissions.RequestAsync<Permissions.StorageWrite>();
}
return permitted;
}
In both cases, the permissions popup doesn't show up and it moves on with the code.
What am I doing wrong?
I have tested your code on the emulator with the Android 12.0 and Android 11.0. The two devices both worked and will show the the permissions popup.
Did you test your application on the Android 13.0? According to this issue about .NET MAUI does not work with Android 13 Android.permission.write_external_storage on the github. The maui project's default target android framework is Android 13.0(Api 33). And then if you set it on the device which is android 13.0. The storage permission popup will not show because the adnrodi 13.0 has removed this permission.
You can try the workaround in the issue which is adding <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" /> in the AndroidManifest.xml. You can also use the device which android version is 12.0 or lower to test your application.

Xamarin Forms Android Ad-Hoc apk update There was a problem parsing the package

Trying to create an android autoupdater for an ahoc app in Xamarin forms.
here is my code and manifest and paths xml.
The app launches fine and i can get the updater dialog. it then seems to download the apk but when it comes to install it i get the 'There was a problem parsing the package' error.
I have tried loads of different ways to do this so some pointers would be cool.
i also tried to look in logcat in release and its saying file permission error but im not sure if this is a red herring or not.
Anyone got any ideas?
```
public async void LaunchActivityInAndroid()
{
string documentsPath = $"{Android.App.Application.Context.GetExternalFilesDir("").AbsolutePath}";
string m_uri = $"{GlobalVariables.ApkPrefix}{GlobalVariables.ApkDomain}{GlobalVariables.ApkEndpoint}";
string fileName = m_uri.Substring(m_uri.LastIndexOf("/") + 1);
HttpResponseMessage httpResponse = await new HttpClient().GetAsync(m_uri);
byte[] data = await httpResponse.Content.ReadAsByteArrayAsync();
string directory = Android.App.Application.Context.GetExternalFilesDir("").AbsolutePath.ToString();
string path = Path.Combine(directory, fileName);
foreach (string file in Directory.GetFiles(directory))
{
if (Path.GetExtension(file) == ".apk")
{
File.Delete(file);
File.Delete("app.db3");
}
}
File.WriteAllBytes(path, data);
Android.Net.Uri fileUri;
if ((int)Build.VERSION.SdkInt < 23) {
fileUri = Android.Net.Uri.FromFile(new Java.IO.File(path));
}
else {
fileUri = FileProvider.GetUriForFile(Xamarin.Forms.Forms.Context as Context, Xamarin.Forms.Forms.Context.ApplicationContext.PackageName + ".fileprovider", new Java.IO.File(documentsPath + "/app.apk"));
}
Intent promptInstall = new Intent(Intent.ActionView).SetFlags(ActivityFlags.ClearTop).SetFlags(ActivityFlags.GrantReadUriPermission).SetFlags(ActivityFlags.NewTask).PutExtra(Intent.ExtraNotUnknownSource, true).SetDataAndType(fileUri, "application/vnd.android.package-archive");
Android.App.Application.Context.StartActivity(promptInstall);
//webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressCallback);
}
'''
Manifest
```
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.99" package="app" android:installLocation="auto" android:versionCode="1">
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="29" />
<application android:label="app.Android" android:icon="#mipmap/ic_launcher" android:networkSecurityConfig="#xml/network_security_config" android:allowBackup="false" android:requestLegacyExternalStorage="true">
<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="#xml/provider_paths"></meta-data>
</provider>
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.BIND_DEVICE_ADMIN" />
<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
<uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
<uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
<uses-permission android:name="android.permission.WRITE_PROFILE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
</manifest>
```
provider paths xml
```
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external"
path="." />
<external-files-path
name="external_files"
path="." />
<cache-path
name="cache"
path="." />
<external-cache-path
name="external_cache"
path="." />
<files-path
name="files"
path="." />
</paths>
```
Thanks in advance guys!

How to get provider authorities value from an Android manifest file programmatically in Xamarin Forms?

I'm developing an Android with Xamarin.Forms, and I'm using the AndroidManifest.xml file shown below.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1" android:versionName="1.0">
<uses-sdk android:minSdkVersion="26" android:targetSdkVersion="26" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application android:label="MyApp.Android">
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="myapp.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_file_paths" />
</provider>
</application>
I'd like to get the android:authorities value of the provider directive (in my case "myapp.fileprovider" as string) programmatically.
How is this possible with Xamarin.Forms?
I found an answer.
var context = Android.App.Application.Context;
var component = new Android.Content.ComponentName(context, Java.Lang.Class.FromType(typeof(Android.Support.V4.Content.FileProvider)));
var info = context.PackageManager.GetProviderInfo(component, Android.Content.PM.PackageInfoFlags.MetaData);
var authority = info.Authority;
Kotlin version as a helper function
fun getFileProviderAuthorities(context: Context): String {
val component = ComponentName(context, FileProvider::class.java)
val info = context.packageManager.getProviderInfo(component, android.content.pm.PackageManager.GET_META_DATA)
return info.authority
}

Xamarin Android - BroadcastReceiver Issue - Unable to instantiate receiver

My application is using a Geofencing Service to send http requests to an API when a user enters and exits a Geofence. I need that Service to be stopped or started depending on if the user has GPS access or not. I will also need this to occur after the application has been completely closed as disabling location service will crash the background service.
The problem is that whenever I enable or disable Location Services the application crashes with this stack trace:
java.lang.RuntimeException: Unable to instantiate receiver com.MyCompany.MyApp.GeofenceBroadcastReceiver: java.lang.ClassNotFoundException: Didn't find class "com.MyCompany.MyApp.GeofenceBroadcastReceiver" on path: DexPathList[[zip file "/data/app/com.MyCompany.MyApp-1/base.apk"],nativeLibraryDirectories=[/data/app/com.MyCompany.MyApp-1/lib/arm, /vendor/lib, /system/lib]]
Interestingly, this only occurs when Location Services is changed, not when Airplane mode is changed. In either case OnReceive in my BroadcastReceiver is being called.
I did manually edit my AndroidManifest.xml file. Not sure if that might have something to do with it. According to this it might.
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.MyCompany.MyApp" android:versionCode="1" android:versionName="1.0" android:installLocation="auto">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" />
<application android:label="MyApp" android:icon="#drawable/Icon">
<meta-data android:name="come.google.android.maps.v2.API_KEY" android:value="SomeKeyValueForGoogleAPI" />
<receiver android:name=".GeofenceBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_AIRPLANE_MODE_CHANGED"></action>
<action android:name="android.intent.action.ACTION_BOOT_COMPLETED"></action>
<action android:name="android.location.PROVIDERS_CHANGED" />
</intent-filter>
</receiver>
</application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.LOCATION_HARDWARE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.ACCESS_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.ACCESS_GPS" />
</manifest>
GeofenceBroadcastReceiver.cs
// using blah blah blah
namespace MyApp.Geofence
{
[BroadcastReceiver]
[IntentFilter(new[] { Intent.ActionBootCompleted, Intent.ActionAirplaneModeChanged, LocationManager.ProvidersChangedAction })]
public class GeofenceBroadcastReceiver : BroadcastReceiver
{
public GeofenceBroadcastReceiver()
{
}
public override void OnReceive(Context context, Intent intent)
{
LocationManager leManager = (LocationManager)context.GetSystemService(Context.LocationService);
bool gpsEnabled = leManager.IsProviderEnabled(LocationManager.GpsProvider);
bool networkEnabled = leManager.IsProviderEnabled(LocationManager.NetworkProvider);
if (gpsEnabled)
{
// bool from the application preferences
if (Preferences.getBool(Preferences.GEOLOCATION))
GeofenceApp.startService();
}
else
GeofenceApp.stopService();
}
}
}
MainActivity.cs
// things...
private GeofenceBroadcastReceiver receiver;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
//...
// some other stuff
//...
receiver = new GeofenceBroadcastReceiver();
RegisterReceiver(receiver, new IntentFilter(Intent.ActionBootCompleted));
RegisterReceiver(receiver, new IntentFilter(LocationManager.ProvidersChangedAction));
RegisterReceiver(receiver, new IntentFilter(Intent.ActionAirplaneModeChanged));
}
Gah, I figured it out.
I had to actually remove
<receiver android:name=".GeofenceBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.ACTION_AIRPLANE_MODE_CHANGED"></action>
<action android:name="android.intent.action.ACTION_BOOT_COMPLETED"></action>
<action android:name="android.location.PROVIDERS_CHANGED" />
</intent-filter>
</receiver>
from my manifest.

Categories

Resources