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.
Related
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
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!
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
}
I have an issue with launching my app in Release mode.
I have enabled MultiDex for Debug and Release modes. When deploying in Debug mode, my app is working fine and everything OK. But when I launch it Release mode, it crashes at startup. Here's a Device Log:
Android manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionName="17.2" android:versionCode="1"
android:installLocation="auto" package="com.myapp">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="28" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application android:label="Test Application" android:largeHeap="true" android:icon="#drawable/AppIcon" android:supportsRtl="true">
<provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="#xml/provider_paths" />
</provider>
</application>
</manifest>
Make your Application extend MultiDexApplication. If you don't have one already just create a BaseApplication class like so:
public class BaseApplication extends MultiDexApplication {
#Override
public void onCreate() {
super.onCreate();
}
#Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}
also, add the following to your build.gradle:
multiDexEnabled true
for your dependencies:
dependencies {
implementation 'androidx.multidex:multidex:2.0.1'
}
or if not using AndroidX
dependencies {
implementation 'com.android.support:multidex:1.0.3'
}
Finally in your manifest add within your application tag:
android:name="android.support.multidex.MultiDexApplication"
or
android:name="androidx.multidex.MultiDexApplication"
if you've migrated to AndroidX
So i want to make an android app to monitor incoming data. Know how much data each network (Wifi,4g) consumes. I am currently using Xamarin.Android and i'm pretty new to android. I study comp sci in college and have had a year's worth of experience using C# (although i haven't really coded in a year) and SQL.
I have worked out how to get which network is in use, but i am not able to make the Broadcastreceiver() for detecting network change to work. The only solutions i have found are in Java which makes it a little harder. I have translated most of the java solutions and this is what i've come to:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.Net;
namespace ProjectData
{
[BroadcastReceiver()]
public class NetworkReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
ConnectivityManager connectivityManager = (ConnectivityManager)
context.GetSystemService(Context.ConnectivityService);
NetworkInfo activeNetInfo =connectivityManager.ActiveNetworkInfo;
if (activeNetInfo != null)
Toast.MakeText(context, "Active Network Type : " + activeNetInfo.TypeName, ToastLength.Short).Show();
}
}
}
Now my main problem is how to use it. This is what i've been trying so far :
public void Network()
{
NetworkReceiver networkReceiver = new NetworkReceiver();
Intent intent = new Intent(this, networkReceiver.Class);
RegisterReceiver(networkReceiver, new IntentFilter(ConnectivityManager.ConnectivityAction));
}
I haven't been able to test it because i get this error :
Tag < receiver > attribute name has invalid character ' '.
For line 10 :
<intent-filter><action android:name="android.net.conn.CONNECTIVITY_CHANGE" /></intent-filter></receiver>
This is the whole manifest :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ProjectData.ProjectData" android:versionCode="1" android:versionName="1.0">
<!--suppress UsesMinSdkAttributes-->
<uses-sdk android:minSdkVersion="21" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application android:allowBackup="true" android:label="#string/app_name" android:name="android.app.Application" android:icon="#drawable/dataicon" android:debuggable="true">
<receiver android:name=".NetworkReceiver"> android:exported=true
<intent-filter><action android:name="android.net.conn.CONNECTIVITY_CHANGE" /></intent-filter></receiver>
<receiver android:name="md5f626beda2f87a7aeee80e4f58cdf3b35.NetworkReceiver" />
<activity android:icon="#drawable/dataicon" android:label="ProjectData" android:name="md5f626beda2f87a7aeee80e4f58cdf3b35.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<provider android:name="mono.MonoRuntimeProvider" android:exported="false" android:initOrder="2147483647" android:authorities="ProjectData.ProjectData.mono.MonoRuntimeProvider.__mono_init__" />
<!--suppress ExportedReceiver-->
<receiver android:name="mono.android.Seppuku">
<intent-filter>
<action android:name="mono.android.intent.action.SEPPUKU" />
<category android:name="mono.android.intent.category.SEPPUKU.ProjectData.ProjectData" />
</intent-filter>
</receiver>
</application>
</manifest>
I am debugging this on my phone which runs on Android 7.0
Thanks for the help!
try change it
<receiver android:name=".NetworkReceiver"> android:exported=true
to :
<receiver android:name=".NetworkReceiver"
android:exported=true>
You can use Xam.Plugin.Connectivity from nugget, works perfectly