Uploading file not working using a webview in Xamarin Android App - c#

I created mobile application with Xamarin. I can login with nfc card and i can access true page. but i cant open file chooser with html file upload button. if i can use webchromeclient its working. but i should upload file with mobile application. File chooser is not opening when i press upload file button
namespace MYPROJECTMOBILE
{
[Activity(Label = "#string/app_name", Theme = "#style/AppTheme.NoActionBar")]
public class MainActivity : Activity
{
WebView webView;
static MainActivity context;
static string cardNo = "";
static string pernr = "";
public static IValueCallback mUploadCallbackAboveL;
public static Android.Net.Uri imageUri;
public static MainActivity Instance;
public static int PHOTO_REQUEST = 10023;
public static IValueCallback mUploadMessage;
public static int FILECHOOSER_RESULTCODE = 1;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_main);
Instance = this;
context = this;
cardNo = Intent.GetStringExtra("CARD_ID") ?? string.Empty;
pernr = Intent.GetStringExtra("PERNR") ?? string.Empty;
webView = FindViewById<WebView>(Resource.Id.webView1);
webView.SetWebViewClient(new ExtendWebViewClient());
webView.SetWebChromeClient(new WebChromeClient());
webView.ClearCache(true);
webView.ClearFormData();
CookieManager.Instance.RemoveAllCookie();
webView.Settings.JavaScriptEnabled = true;
webView.Settings.DomStorageEnabled = true;
webView.Settings.AllowFileAccess = true;
webView.Settings.AllowFileAccessFromFileURLs = true;
webView.Settings.AllowUniversalAccessFromFileURLs = true;
webView.Settings.AllowContentAccess = true;
webView.LoadUrl("https://website.com.tr/");
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
MenuInflater.Inflate(Resource.Menu.menu_main, menu);
return true;
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
int id = item.ItemId;
if (id == Resource.Id.action_settings)
{
return true;
}
return base.OnOptionsItemSelected(item);
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
public override void OnBackPressed()
{
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
{
if (requestCode == FILECHOOSER_RESULTCODE)
{
if (null == mUploadMessage) return;
Android.Net.Uri result = intent == null || resultCode != Result.Ok ? null : intent.Data;
mUploadMessage.OnReceiveValue(result);
mUploadMessage = null;
}
else if (requestCode == PHOTO_REQUEST)
{
Android.Net.Uri result = intent == null || resultCode != Result.Ok ? null : intent.Data;
if (mUploadCallbackAboveL != null)
{
onActivityResultAboveL(requestCode, resultCode, intent);
}
else if (mUploadMessage != null)
{
mUploadMessage.OnReceiveValue(result);
mUploadMessage = null;
}
}
}
private void onActivityResultAboveL(int requestCode, Result resultCode, Intent data)
{
if (requestCode != PHOTO_REQUEST || mUploadCallbackAboveL == null)
{
return;
}
Android.Net.Uri[] results = null;
if (resultCode == Result.Ok)
{
results = new Android.Net.Uri[] { imageUri };
results[0] = MainActivity.imageUri;
}
mUploadCallbackAboveL.OnReceiveValue(results);
mUploadCallbackAboveL = null;
}
public class ExtendWebViewClient : WebViewClient
{
bool isLoggedIn;
bool redirected;
[Obsolete]
public override bool ShouldOverrideUrlLoading(WebView view, string url)
{
view.LoadUrl(url);
return true;
}
public override void OnPageFinished(WebView view, string url)
{
if (url.Contains("https://website.com.tr/Login"))
{
if (pernr != "") {
view.EvaluateJavascript("javascript:{" +
"WaitScreen.Show();" +
"ins=document.getElementsByTagName('input');" +
"ins[0].value='" + pernr + "';" +
"ins[1].value='" + cardNo + "';" +
"ins[2].value=false;" +
"document.getElementsByTagName('button')[0].click();" +
"};", new JavascriptResult());
}
else
{
Intent activityIntent = new Intent(context, typeof(NfcActivity));
context.StartActivity(activityIntent);
}
pernr = "";
cardNo = "";
}
}
}
internal class JavascriptResult : Java.Lang.Object, IValueCallback
{
public string result;
public void OnReceiveValue(Java.Lang.Object value)
{
result = ((Java.Lang.String)value).ToString();
}
}
}
}
" i can upload photo in web with this part
<input type="file" id="myFile" name="filename">
<input type="submit">
"
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:showIn="#layout/activity_main"
android:minWidth="25px"
android:minHeight="25px">
<android.webkit.WebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/webView1" />
</RelativeLayout>
"
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.NFC_TRANSACTION_EVENT" />
<uses-permission android:name="android.permission.BIND_NFC_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
android:value="true" />

In your custom WebChromeClient you have to override OnShowFileChooser(...) as here
https://learn.microsoft.com/en-us/answers/questions/198451/problem-opening-camera-with-webview-and-input-type.html
i check also Camera permission and i ask to user before use it in this way:
var activity = Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity;
int CAMERA_PERMISSION_REQUEST_CODE = 2323;
if (activity.PackageManager.CheckPermission(Android.Manifest.Permission.Camera, activity.PackageName) != Android.Content.PM.Permission.Granted)
{
var permissions = new string[] { Android.Manifest.Permission.Camera }; activity.RequestPermissions(permissions, CAMERA_PERMISSION_REQUEST_CODE);
}

Related

Finish foreground service thread

I have a foreground service with a thread that is continuously checking data from a server.
So the service works well and it doesn't closes when I close the app, but when I start the app again the service creates another thread. Is posible abort the thread of the previous instance?
This is my MainActivity
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static Activity ActivityCurrent { get; private set; }
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Window.SetStatusBarColor(Android.Graphics.Color.Rgb(0, 120, 71));
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
System.Diagnostics.Debug.WriteLine("Iniciado");
LoadApplication(new App());
ActivityCurrent = this;
DependencyService.Get<IServicio>().Start();
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
This is my service class
[Service(ForegroundServiceType = Android.Content.PM.ForegroundService.TypeDataSync)]
public class Servicio : Service, IServicio
{
Uri link = new Uri(new Url().Dir + "/estado_alarma.php");
public override IBinder OnBind(Intent intent)
{
throw new NotImplementedException();
}
[return: GeneratedEnum]
Thread refrescar;
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int starId)
{
if (intent.Action == "START_SERVICE")
{
System.Diagnostics.Debug.WriteLine("Se ha iniciado el servicio");
RegisterNotification();
}
else if (intent.Action == "STOP_SERVICE")
{
StopForeground(true);
StopSelfResult(starId);
}
return StartCommandResult.NotSticky;
}
private void RegisterNotification()
{
NotificationChannel channel = new NotificationChannel("ServicioChannel", "Demo de servicio", NotificationImportance.Max);
NotificationManager manager = (NotificationManager)MainActivity.ActivityCurrent.GetSystemService(Context.NotificationService);
manager.CreateNotificationChannel(channel);
Notification notification = new Notification.Builder(this, "ServicioChannel").SetContentTitle("Servicio Trabajando").SetOngoing(true).Build();
StartForeground(200, notification);
_ = IniciarServicio();
}
public void Start()
{
Intent startService = new Intent(MainActivity.ActivityCurrent, typeof(Servicio));
startService.SetAction("START_SERVICE");
MainActivity.ActivityCurrent.StartService(startService);
}
public void Stop()
{
Intent startService = new Intent(MainActivity.ActivityCurrent, typeof(Servicio));
startService.SetAction("STOP_SERVICE");
MainActivity.ActivityCurrent.StartService(startService);
}
public int IniciarServicio()
{
refrescar = new Thread(EsperaAlarmaAsync);
refrescar.Start();
return 1;
}
public async void EsperaAlarmaAsync()
{
WebClient cliente = new WebClient();
byte[] respuesta;
string resultado;
var a = true;
while (a)
{
respuesta = cliente.DownloadData(link);
resultado = System.Text.Encoding.ASCII.GetString(respuesta);
cliente.Dispose();
if (resultado == "Alerta")
{
setNotificationVolumeToMax();
Device.BeginInvokeOnMainThread(() =>
{
App.Current.MainPage = new Alerta();
var powerManager = (PowerManager)GetSystemService(PowerService);
var wakeLock = powerManager.NewWakeLock(WakeLockFlags.ScreenDim | WakeLockFlags.AcquireCausesWakeup, "Ejemplo");
wakeLock.Acquire();
Intent x = new Intent(MainActivity.ActivityCurrent.Intent);
StartActivity(x);
});
a = false;
}
Thread.Sleep(3000);
}
refrescar.Abort();
}
private void setNotificationVolumeToMax()
{
try
{
var audioManager = (AudioManager)GetSystemService(Context.AudioService);
int max_volume = audioManager.GetStreamMaxVolume(Android.Media.Stream.Music);
audioManager.SetStreamVolume(Android.Media.Stream.Music, max_volume, 0);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
When the server return 'Alerta' it opens the app

Play Notification Sound like a Phone Call Ringtone sound

I am building a prototype in Xamarin.Forms that sends a push notification to the same App installed on other devices. I'm using OneSignal libraries.
Everything works perfectly. The push notification is sent by one device and received by the others correctly. Whether the App is in the foreground or in the background.
When the push notification arrives on the other devices, I capture it in HandleNotificationReceived and call my service that plays the Ringtone sound.
When push notification arrives on other devices and the app is in the foreground, everything works perfectly. However, when it arrives on the other devices, and the App is in the background, only Push Notification arrives, but the Ringtone sound does not play.
I read about limitations when trying to run some things in Background and I believe my problem has something to do with it.
can anybody help me?
Here is my code:
APPLICATION
namespace SaferProject
{
public partial class App : Application
{
public App()
{
InitializeComponent();
var page = new NavigationPage(new Views.MainMenu());
MainPage = page;
IPlaySoundService playSoundService;
playSoundService = DependencyService.Get<IPlaySoundService>();
OneSignal.Current.SetLogLevel(LOG_LEVEL.VERBOSE, LOG_LEVEL.NONE);
OneSignal.Current.StartInit("417d7b75-ed42-4ca5-aed3-42612d260235")
.Settings(new Dictionary<string, bool>() {
{ IOSSettings.kOSSettingsKeyAutoPrompt, false },
{ IOSSettings.kOSSettingsKeyInAppLaunchURL, false } })
.InFocusDisplaying(OSInFocusDisplayOption.Notification)
.HandleNotificationOpened((result) =>
{
if (playSoundService.GetServiceStatus())
{
playSoundService.StopSystemSound();
}
Debug.WriteLine("HandleNotificationOpened: {0}", result.notification.payload.body);
})
//################ Here I call the PlaySystemSound service###############
//################ Here I call the PlaySystemSound service###############
//################ Here I call the PlaySystemSound service###############
.HandleNotificationReceived((notification) =>
{
playSoundService.PlaySystemSound();
DeviceDisplay.KeepScreenOn = true;
Debug.WriteLine("HandleNotificationReceived: {0}", notification.payload.body);
})
//########################################################################
//########################################################################
//########################################################################
.HandleInAppMessageClicked((action) =>
{
// Example IAM click handling for IAM elements
Debug.WriteLine("HandledInAppMessageClicked: {0}", action.clickName);
})
.EndInit();
// The promptForPushNotificationsWithUserResponse function will show the iOS push notification prompt. We recommend removing the following code and instead using an In-App Message to prompt for notification permission (See step 7)
OneSignal.Current.RegisterForPushNotifications();
}
protected override void OnStart()
{
IPlaySoundService playSoundService;
playSoundService = DependencyService.Get<IPlaySoundService>();
if (playSoundService.GetServiceStatus())
{
playSoundService.StopSystemSound();
}
//Handle when your app sleeps
Task.Run(async () => { await SaferProjectService.GetInstance().GetOut(UserManager.GetLogUser()); });
}
protected override void OnSleep()
{
//Handle when your app sleeps
Task.Run(async()=> { await SaferProjectService.GetInstance().GetOut(UserManager.GetLogUser()); });
}
protected override void OnResume()
{
//Handle when your app resumes
Task.Run(async () => { await SaferProjectService.GetInstance().GetIn(UserManager.GetLogUser()); });
}
}
}
INTERFACE
namespace SaferProject.Services
{
public interface IPlaySoundService
{
Task PlaySystemSound();
Task StopSystemSound();
void SetServiceStatus(bool isPlaying);
bool GetServiceStatus();
}
}
ANDROID SERVICE
[assembly: Xamarin.Forms.Dependency(typeof(PlaySoundService))]
namespace SaferProject.Droid.Services
{
[Activity(Label = "PlaySoundService", NoHistory = true, Theme = "#style/MyTheme", ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class PlaySoundService : IPlaySoundService
{
private static PlaySoundService _instance;
readonly Ringtone _rt;
bool IsPlaying;
public static PlaySoundService GetInstance()
{
if (_instance == null)
{
_instance = new PlaySoundService();
}
return _instance;
}
public PlaySoundService()
{
AudioManager am = (AudioManager)Application.Context.GetSystemService(Context.AudioService);
if (!am.RingerMode.Equals(2))
{
am.RingerMode = RingerMode.Normal;
//am.SetVibrateSetting(VibrateType.Ringer, VibrateSetting.On);
}
Android.Net.Uri uri = RingtoneManager.GetDefaultUri(RingtoneType.Ringtone);
_rt = RingtoneManager.GetRingtone(MainActivity.instance.ApplicationContext, uri);
}
public Task PlaySystemSound()
{
_rt.Play();
Vibration.Vibrate(500);
SetServiceStatus(true);
return Task.CompletedTask;
}
public Task StopSystemSound()
{
Vibration.Cancel();
_rt.Stop();
SetServiceStatus(false);
return Task.CompletedTask;
}
public void SetServiceStatus(bool isPlaying)
{
IsPlaying = isPlaying;
}
public bool GetServiceStatus()
{
return IsPlaying;
}
}
}
MAIN ACTIVITY
namespace SaferProject.Droid
{
[Activity(Label = "SaferProject.Droid", Theme = "#style/MainTheme", MainLauncher = false,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation,
ScreenOrientation = ScreenOrientation.Portrait, LaunchMode = LaunchMode.SingleTop)] //This is what controls orientation
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
public static MainActivity instance { set; get; }
protected override void OnCreate(Bundle savedInstanceState)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(savedInstanceState);
instance = this;
// Radio Button configuration
Xamarin.Forms.Forms.SetFlags(new string[] { "RadioButton_Experimental" });
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
//CreateNotificationFromIntent(Intent); //Local Notification - CRIADO NA MADRUGADA
//################ Início OneSignal #################
// Remove this method to stop OneSignal Debugging
OneSignal.Current.SetLogLevel(LOG_LEVEL.VERBOSE, LOG_LEVEL.NONE);
OneSignal.Current.StartInit("417d7b75-ed42-4ca5-aed3-42612d260235")
.InFocusDisplaying(OSInFocusDisplayOption.Notification)
.EndInit();
//################# Fim OneSignal ###################
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}
ANDROID MANIFEST
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="6" android:versionName="1.2.0" package="br.com.safertechnology.saferproject" android:installLocation="auto">
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="30" />
<permission android:name="$br.com.safertechnology.saferproject.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<application android:label="Safer" android:icon="#mipmap/ic_launcher" android:fullBackupContent="#xml/auto_backup_rules">
<receiver android:name="com.onesignal.GcmBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="$br.com.safertechnology.saferproject" />
</intent-filter>
</receiver>
</application>
<uses-permission android:name="$com.aolserra.sirene.permission.C2D_MESSAGE" />
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
</manifest>
IOS SERVICE
[assembly: Xamarin.Forms.Dependency(typeof(PlaySoundService))]
namespace SaferProject.iOS.Services
{
public class PlaySoundService : IPlaySoundService
{
private static PlaySoundService _instance;
readonly SystemSound Sound;
bool IsPlaying;
public static PlaySoundService GetInstance()
{
if (_instance == null)
{
_instance = new PlaySoundService();
}
return _instance;
}
public PlaySoundService()
{
AVAudioSession audioSession = AVAudioSession.SharedInstance();
NSError error;
audioSession.OverrideOutputAudioPort(AVAudioSessionPortOverride.Speaker, out error);
Sound = new SystemSound(1151); //1304 (Alarm)
}
public Task PlaySystemSound()
{
Sound.PlaySystemSound();
Vibration.Vibrate(500);
SetServiceStatus(true);
return Task.CompletedTask;
}
public Task StopSystemSound()
{
Sound.Close();
Vibration.Cancel();
SetServiceStatus(false);
return Task.CompletedTask;
}
public void SetServiceStatus(bool isPlaying)
{
IsPlaying = isPlaying;
}
public bool GetServiceStatus()
{
return IsPlaying;
}
}
}
APPDELEGATE
namespace SaferProject.iOS
{
// The UIApplicationDelegate for the application. This class is responsible for launching the
// User Interface of the application, as well as listening (and optionally responding) to
// application events from iOS.
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
//return base.FinishedLaunching(app, options);
base.FinishedLaunching(app, options);
//################ Início OneSignal #################
// Remove this method to stop OneSignal Debugging
OneSignal.Current.SetLogLevel(LOG_LEVEL.VERBOSE, LOG_LEVEL.NONE);
OneSignal.Current.StartInit("417d7b75-ed42-4ca5-aed3-42612d260235")
.Settings(new Dictionary<string, bool>() {
{ IOSSettings.kOSSettingsKeyAutoPrompt, false },
{ IOSSettings.kOSSettingsKeyInAppLaunchURL, false } })
.InFocusDisplaying(OSInFocusDisplayOption.Notification)
.EndInit();
// The promptForPushNotificationsWithUserResponse function will show the iOS push notification prompt. We recommend removing the following code and instead using an In-App Message to prompt for notification permission (See step 7)
OneSignal.Current.RegisterForPushNotifications();
return true;
//################# Fim OneSignal ###################
}
}
}
INFO.PLIST
Info.Plist

The parameter came from broadcast receiver constructor is always nullable

I want to update my main activity UI in my foreground service with broadcast receiver. In BroadcastReceiver.OnReceive method,I passed the instance of main activity,but it is always nullable. How can I fix it? Many thanks in advance!
In my broadcast receiver:
public override void OnReceive(Context context, Intent intent)
{
Core.Music music = intent.GetParcelableExtra("selectedMusic") as Core.Music;
mMainActivity.mTxtSongName.Text = Core.MusicHelper.GetTitleAndAuthor(music.Title);
mMainActivity.mTxtAuthorName.Text = Core.MusicHelper.GetTitleAndAuthor(music.Author);
System.Threading.ThreadPool.QueueUserWorkItem(o =>
{
string imageUrl = music.Url.Replace(#"\", "").Replace("http", "https");
var task = Core.MusicHelper.GetSongPic(imageUrl, 35, 35);
var pic = task.Result;
if (pic != null)
{
mMainActivity.RunOnUiThread(() =>
{
mMainActivity.mImageViewSongPic.SetImageBitmap(pic);
});
}
});
}
In my service:
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
Core.Music music = intent.GetParcelableExtra("selectedMusic") as Core.Music;
BroadcastStarted(music);
//To start the service
return StartCommandResult.NotSticky;
}
In my MainActivity.OnResume:
IntentFilter filter = new IntentFilter();
mReceive = new Service.Boradcast.MusicChangedBroadcastReceiver() { mMainActivity=this};
RegisterReceiver(mReceive,filter);
how do you defined mMainActivity ?
however the simplest example of a reference to MainActivity is to use static
define in your MainActivity OnCreate() method:
public static MainActivity Instance;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
...
Instance = this;
}
then call in your broadcast receiver
public override void OnReceive(Context context, Intent intent)
{
Core.Music music = intent.GetParcelableExtra("selectedMusic") as Core.Music;
MainActivity.Instance.mTxtSongName.Text = Core.MusicHelper.GetTitleAndAuthor(music.Title);
MainActivity.Instance.mTxtAuthorName.Text = Core.MusicHelper.GetTitleAndAuthor(music.Author);
System.Threading.ThreadPool.QueueUserWorkItem(o =>
{
string imageUrl = music.Url.Replace(#"\", "").Replace("http", "https");
var task = Core.MusicHelper.GetSongPic(imageUrl, 35, 35);
var pic = task.Result;
if (pic != null)
{
MainActivity.Instance.RunOnUiThread(() =>
{
MainActivity.Instance.mImageViewSongPic.SetImageBitmap(pic);
});
}
});
}
Or pass MainActivity as a parameter to the constructor :
in your MainActivity.OnResume():
IntentFilter filter = new IntentFilter();
mReceive = new Service.Boradcast.MusicChangedBroadcastReceiver(this);
RegisterReceiver(mReceive,filter);
then in your broadcast receiver:
[BroadcastReceiver]
public class MusicChangedBroadcastReceiver: BroadcastReceiver
{
public MainActivity mMainActivity;
public MusicChangedBroadcastReceiver()
{
}
public MusicChangedBroadcastReceiver(MainActivity activity)
{
this.mMainActivity= activity;
}
public override void OnReceive(Context context, Intent intent)
{
Core.Music music = intent.GetParcelableExtra("selectedMusic") as Core.Music;
mMainActivity.mTxtSongName.Text = Core.MusicHelper.GetTitleAndAuthor(music.Title);
mMainActivity.mTxtAuthorName.Text = Core.MusicHelper.GetTitleAndAuthor(music.Author);
System.Threading.ThreadPool.QueueUserWorkItem(o =>
{
string imageUrl = music.Url.Replace(#"\", "").Replace("http", "https");
var task = Core.MusicHelper.GetSongPic(imageUrl, 35, 35);
var pic = task.Result;
if (pic != null)
{
mMainActivity.RunOnUiThread(() =>
{
mMainActivity.mImageViewSongPic.SetImageBitmap(pic);
});
}
});
}
}

Android - InstanceID.GetToken Throws Java.Lang.IncompatibleClassChangeError

I am currently adding notifications to my Android app. I am using VS2015 and Xamarin. I figured I'd create a side project to test it first. I used the documentation from Xamarin and Firebase.
When my app launches, it tries to create a new token, but I get an error instead:
Java.Lang.IncompatibleClassChangeError: The method 'java.io.File
android.support.v4.content.ContextCompat.getNoBackupFilesDir(android.content.Context)'
was expected to be of type virtual but instead was found to be of type
direct
My code for fetching the token is
MainActivity
namespace ClientApp
{
[Activity(Label = "ClientApp", MainLauncher = true, Icon = "#drawable/icon")]
public class MainActivity : Activity
{
TextView msgText;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
msgText = FindViewById<TextView>(Resource.Id.msgText);
if (IsPlayServicesAvailable())
{
var intent = new Intent(this, typeof(RegistrationIntentService3));
StartService(intent);
}
}
public bool IsPlayServicesAvailable()
{
int resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.Success)
{
if (GoogleApiAvailability.Instance.IsUserResolvableError(resultCode))
msgText.Text = GoogleApiAvailability.Instance.GetErrorString(resultCode);
else
{
msgText.Text = "Sorry, this device is not supported";
Finish();
}
return false;
}
else
{
msgText.Text = "Google Play Services is available.";
return true;
}
}
}
}
RegistrationIntentService.cs
using System;
using Android.App;
using Android.Content;
using Android.Util;
using Android.Gms.Gcm;
using Android.Gms.Gcm.Iid;
using Android.Support.V4.Content;
namespace ClientApp
{
[Service(Exported = false)]
class RegistrationIntentService : IntentService
{
static object locker = new object();
public RegistrationIntentService() : base("RegistrationIntentService") { }
protected override void OnHandleIntent(Intent intent)
{
try
{
Log.Info("RegistrationIntentService", "Calling InstanceID.GetToken");
lock (locker)
{
var token = InstanceID.GetInstance(this).GetToken("My_Sender_ID", GoogleCloudMessaging.InstanceIdScope, null);
Log.Info("RegistrationIntentService", "GCM Registration Token: " + token);
SendRegistrationToAppServer(token);
Subscribe(token);
}
}
catch (Exception e)
{
Log.Debug("RegistrationIntentService", "Failed to get a registration token");
return;
}
}
void SendRegistrationToAppServer(string token)
{
// Add custom implementation here as needed.
}
void Subscribe(string token)
{
var pubSub = GcmPubSub.GetInstance(this);
pubSub.Subscribe(token, "/topics/global", null);
}
}
}
My Manifest looks like this
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="MY_PACKAGE" android:versionCode="1" android:versionName="1.0" android:installLocation="auto">
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="MY_PACKAGE.permission.C2D_MESSAGE" />
<permission android:name="MY_PACKAGE.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<application android:label="RemoteNotifications" android:icon="#drawable/Icon">
<receiver android:name="com.google.android.gms.gcm.GcmReceiver" android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="MY_PACKAGE" />
</intent-filter>
</receiver>
</application>
<uses-sdk />
</manifest>
All my packages are up to date and using the latest stable:
Xamarin.GooglePlayServices.Gcm Version 9.0.0.2 with all its dependencies
Xamarin.Android.Support.v4 Version 24.2.1

How to bind to AccessibilityService in Mono (Xamarin)

I've tried and got OnServiceConnected event in ServiceConnection class. But casting IBinder to my class (it inherits Binder, that inherits IBinder) returns null if I try to do that as "var myObject = IBinderObject as myBinderClass;" or casting error if I try to do that as "var myObject = (myBinderClass) IBinderObject;".
Please, any working example...
MainActivity code:
namespace MyApp.Droid
{
[Activity(Label = "MyApp", Icon = "#drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsApplicationActivity
{
AccessibilityServiceBinder binder;
AccessibilityServiceConnection accessibilityServiceConnection;
bool isBound = false, isBoundAC = false, isConfigurationChange = false;
protected Intent ServiceIntent;
AccessibilityServiceClass ServiceClass;
protected override void OnStart()
{
base.OnStart();
if (!isBoundAC)
{
accessibilityServiceConnection = new AccessibilityServiceConnection(this);
isBoundAC = BindService(ServiceIntent, accessibilityServiceConnection, Bind.AutoCreate);
if (isBoundAC)
{
Toast.MakeText(this, "AccessibilityService is Bound", ToastLength.Long);
}
}
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
ServiceIntent = new Intent(this, typeof(AccessibilityServiceClass));
ServiceIntent.SetPackage("android.accessibilityservice.AccessibilityService");
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
}
public class AccessibilityServiceConnection : Java.Lang.Object, IServiceConnection
{
MainActivity activity;
AccessibilityServiceBinder binder;
public AccessibilityServiceBinder Binder
{
get
{
return (AccessibilityServiceBinder)binder;
}
}
public AccessibilityServiceConnection(MainActivity activity)
{
this.activity = activity;
}
public void OnServiceConnected(ComponentName name, IBinder service)
{
var accessibilityServiceBinder = service as AccessibilityServiceBinder;
//accessibilityServiceBinder is always null here
if (accessibilityServiceBinder != null)
{
activity.binder = accessibilityServiceBinder;
activity.isBound = true;
this.binder = accessibilityServiceBinder;
}
}
public void OnServiceDisconnected(ComponentName name)
{
activity.isBound = false;
}
}
public override Java.Lang.Object OnRetainNonConfigurationInstance()
{
base.OnRetainNonConfigurationInstance();
isConfigurationChange = true;
return accessibilityServiceConnection;
}
}
}
Service code:
namespace MyApp.Droid
{
[Service(Label = "MyApp", Permission = Manifest.Permission.BindAccessibilityService), IntentFilter(new[] { "android.accessibilityservice.AccessibilityService" }), MetaData("android.accessibility-service", Resource = "#xml/accessibilityserviceconfig")]
public class AccessibilityServiceClass : Android.AccessibilityServices.AccessibilityService
{
public override void OnAccessibilityEvent(AccessibilityEvent e)
{
Toast.MakeText(this, "OnAccessibilityEvent", ToastLength.Short);
}
public override void OnInterrupt()
{
Toast.MakeText(this, "OnInterrupt", ToastLength.Short);
}
protected override void OnServiceConnected()
{
Toast.MakeText(this, "OnServiceConnected", ToastLength.Short);
}
}
public class AccessibilityServiceBinder : Binder
{
AccessibilityServiceClass service;
public AccessibilityServiceBinder(AccessibilityServiceClass service)
{
this.service = service;
}
public AccessibilityServiceClass GetAccessibilityService()
{
return service;
}
}
}
You aren't overriding the OnBind event of your service, thus the binder is null as you haven't provided an instance.
Add this to your service:
AccessibilityServiceBinder binder;
public override IBinder OnBind (Intent intent)
{
if(binder == null)
binder = new AccessibilityServiceBinder (this);
return binder;
}
For more info read the Xamarin docs on bound services, they're very well explained: https://developer.xamarin.com/guides/android/application_fundamentals/services/part_2_-_bound_services/
EDIT: Total wrong answer (I will delete it in some minutes). It seems it's not possible to bind to an Accessibility service. More info here: android bind to AccessibilityService

Categories