Open phone dialer using a Android Xamarin WebView html link - c#

I'm trying to use the ShouldOverrideUrlLoading() method but the app crashes when I call it.
Below is my code:
private class HybridWebViewClient : WebViewClient
{
public override bool ShouldOverrideUrlLoading(WebView webView, string url)
{
var tel = "tel:";
if (url.StartsWith(tel))
{
var uri = Android.Net.Uri.Parse(url);
var intent = new Intent(Intent.ActionDial, uri);
var act = new Activity();
act.StartActivity(intent);
}
}
}
Thanks in Advance!

The problem lies in the following codes snippet:
var act = new Activity();
act.StartActivity(intent);
The method StartActivity should be called from current context instead of a new Activity. So you need to pass the current context to HybridWebViewClient:
public class HybridWebViewClient : WebViewClient
{
Context context;
public HybridWebViewClient(Context context)
{
this.context = context;
}
public override bool ShouldOverrideUrlLoading(WebView view, string url)
{
var tel = "tel:";
if (url != null)
{
if (url.StartsWith(tel))
{
var uri = Android.Net.Uri.Parse(url);
var intent = new Intent(Intent.ActionDial, uri);
context.StartActivity(intent);
}
}
return true;
}
}
And in the OnCreate method:
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
...
webview.SetWebViewClient(new HybridWebViewClient(this));
webview.LoadUrl("http://example.com");
...
}

What is in the craash dump? Is this related?
shouldOverrideUrlLoading(WebView view, String url)
This method was deprecated in API level 24. Use shouldOverrideUrlLoading(WebView, WebResourceRequest) instead.

Related

How to show webpage on a url in a Windows Service Application in c#?

What I'm trying to achieve is that if someone visits a particular URL, it will show a webpage (some html). This is what I have created until now:
How would you be able to check the URL like https://127.0.0.1/webpage and then create the webpage?
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
}
protected override void OnStop()
{
}
public HttpResponseMessage Get()
{
var response = new HttpResponseMessage();
response.Content = new StringContent("<html><body>Hello World</body></html>");
response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
return response;
}
}

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);
});
}
});
}
}

how to access public Members(variable or method) of MainActivity class in fragment class

i have a Xamarin-Android app in which i used fragments .i want to access public Method of MainActivity in my fragment class.
webview_download+=mWebViewDownload
but this method mWebViewDownload is defined in MainActivity.i cannot access this method from fragment.
i tried to make this method static but this method uses services which can't be accessed without instance.
i tried to access through this.mWebViewDownload but the error is mWebViewDownload is not defined in this scope like that.
i searched stackoverflow for it most of question suggest getActivity() but this is java related solution but i need c# related solution.
i tried to access it through MainActivity.mWebViewDownload but it also gives error that cann't access non-static without object reference like that.please help.fragment class is as follows:
internal class WebviewFragment : Fragment
{
public const string ARG_NUMBER = "number";
public WebviewFragment()
{
// Empty constructor required for fragment subclasses
}
public static Fragment NewInstance(int position)
{
Fragment fragment = new WebviewFragment();
Bundle args = new Bundle();
args.PutInt(WebviewFragment.ARG_NUMBER, position);
fragment.Arguments = args;
return fragment;
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View rootView = inflater.Inflate(Resource.Layout.fragment_content2, container, false);
var i = this.Arguments.GetInt(ARG_NUMBER);
var url = this.Resources.GetStringArray(Resource.Array.weburls_array)[i];
var title = this.Resources.GetStringArray(Resource.Array.contents_array)[i];
// show progress bar
progressBar = (ProgressBar)rootView.FindViewById<ProgressBar>(Resource.Id.progressBar1);
var web_view = rootView.FindViewById<WebView>(Resource.Id.webview);
web_view.SetWebViewClient(new HelloWebViewClient());
web_view.Settings.JavaScriptCanOpenWindowsAutomatically = true;
web_view.Settings.JavaScriptEnabled = true;
web_view.Download += Mwebview_Download;// here is error
//set the custom web client
web_view.SetWebViewClient(new JavaScriptWebViewClient());
web_view.LoadUrl(url);
this.Activity.Title = title;
return rootView;
}
}
here is the mWebView_Download Method in MainActivity class
// Download
public void Mwebview_Download(object sender, DownloadEventArgs e)
{
var listPermissions = new System.Collections.Generic.List<string>();
if (CheckSelfPermission(Android.Manifest.Permission.WriteExternalStorage) != Permission.Granted)
{
Log.Warn(LOG_TAG, "CheckSelfPermission(WriteExternalStorage) not yet granted - will prompt user for permission");
listPermissions.Add(Android.Manifest.Permission.WriteExternalStorage);
// Make the request with the permissions needed...and then check OnRequestPermissionsResult() for the results
RequestPermissions(listPermissions.ToArray(), PERMISSION_Write_External_Storage);
}
else
{
var url = e.Url;
DownloadManager.Request request = new DownloadManager.Request(Android.Net.Uri.Parse(url));
request.AllowScanningByMediaScanner();
string filename = System.IO.Path.GetFileName(url);
request.SetNotificationVisibility(DownloadVisibility.VisibleNotifyCompleted);
// request.SetNotificationVisibility(DownloadManager.Request.VisibilityVisibleNotifyCompleted); //Notify client once download is completed!
request.SetDestinationInExternalPublicDir(Android.OS.Environment.DirectoryDownloads, filename);
DownloadManager dm = (DownloadManager)GetSystemService("download");
dm.Enqueue(request);
Toast.MakeText(ApplicationContext, "Downloading File", ToastLength.Long//To notify the Client that the file is being downloaded
).Show();
}
}
```
Solutions:
1.Define a static property in your MainActivity and use it in Fragment, for example:
public class MainActivity : AppCompatActivity
{
public static MainActivity Instance;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_main);
Instance = this;
}
public void test()
{
}
}
And then in your fragment, you can access the method by:
MainActivity.Instance.test();
2.getActivity() method in C# is ((ActivityType)Activity).yourPublicMethod();
:
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Use this to return your custom view for this Fragment
// return inflater.Inflate(Resource.Layout.YourFragment, container, false);
((MainActivity)Activity).test();
return base.OnCreateView(inflater, container, savedInstanceState);
}

How to pass device token to PCL from Android MainActivity?

I have added code in MainActivity.cs file for generating Device Id. Now I want to pass that device token to my PCL project main page, How's that possible? I also want to know about how to generate Device Token in IOS app? and how to pass that token to Portable Class Library?
Code Sample :
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);
global::Xamarin.Forms.Forms.Init(this, bundle);
LoadApplication(new App());
if (Intent.Extras != null)
{
foreach (var key in Intent.Extras.KeySet())
{
var value = Intent.Extras.GetString(key);
Log.Debug("Key: {0} Value: {1}", key, value);
}
}
FirebaseApp.InitializeApp(this);
var instanceId = FirebaseInstanceId.Instance;
if (FirebaseInstanceId.Instance.Token != null)
Log.Debug("MyToken", FirebaseInstanceId.Instance.Token.ToString());
}
}
I need this "My Token" data on login page button Click event. Hows this possible?
My Login Page Code is
public partial class LoginPage : ContentPage
{
private readonly DataService _dataService = new DataService();
public LoginPage()
{
InitializeComponent();
}
private async Task BtnLogin_ClickedAsync(object sender, EventArgs e)
{
var result = await _dataService.Authentication(TxtUserName.Text, TxtPassword.Text,"MyToken");
if (result.AccessToken != null)
{
await Navigation.PushModalAsync(new MainMasterPage());
GlobalClass.userToken = result;
}
else
await DisplayAlert("", Resource.InvalidMessage, Resource.OkText);
}
}
Welcome to the Realm of Dependency Injection :)
documentation can be found here
You need to create a interface on your PCL then reference that on your Native project
Example:
Create class DeviceToke.cs in your PCL
public interface ITextToSpeech
{
void Speak(string text);
}
Then in your native project, you can do the following:
sample code:
[assembly: Dependency(typeof(TextToSpeechAndroidImpl))]
namespace IocAndDiXamarinForms.Droid
{
public class TextToSpeechAndroidImpl : Java.Lang.Object, ITextToSpeech, TextToSpeech.IOnInitListener
{
TextToSpeech speaker;
string toSpeak;
public void Speak(string text)
{
var ctx = Forms.Context; // useful for many Android SDK features
toSpeak = text;
if (speaker == null)
{
speaker = new TextToSpeech(ctx, this);
}
else
{
var p = new Dictionary<string, string>();
speaker.Speak(toSpeak, QueueMode.Flush, p);
}
}
#region IOnInitListener implementation
public void OnInit(OperationResult status)
{
if (status.Equals(OperationResult.Success))
{
var p = new Dictionary<string, string>();
speaker.Speak(toSpeak, QueueMode.Flush, p);
}
}
#endregion
}
}
You can use the Xamarin messaging center to pass a message back from your platform-specific classes to your PCL ViewModel. You'll need to subscribe to the message in your VM, and send the message from your Android or iOS class. Then you can store the value in your VM and use it when the user clicks login.
Sending the message:
Xamarin.Forms.MessagingCenter.Send(FirebaseInstanceId.Instance.Token.ToString(), "InstanceId");
Subscribing in your VM:
MessagingCenter.Subscribe<string> (this, "InstanceId", (InstanceId) => {
// use the InstanceId as required
});
});
A handy solution is to define a publicly accessible static StrToken property in some public class, e.g. App:
public static Size Token;
and OnCreate on Android:
App.StrToken = FirebaseInstanceId.Instance;

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