Starting an Activity from Android Wear Watchface - wear-os

I am trying to start an activity from a watchface in Android Wear on a click using:
Intent myIntent = new Intent(this, com.jorc.android.wearable.watchface.watchface.MainActivity.class);
getApplicationContext().startActivity(myIntent);
However, I get this error.
Error:(564, 39) error: no suitable constructor found for Intent(DigitalWatchFaceService.Engine,Class<MainActivity>)
constructor Intent.Intent(String,Uri) is not applicable
(argument mismatch; DigitalWatchFaceService.Engine cannot be converted to String)
constructor Intent.Intent(Context,Class<?>) is not applicable
(argument mismatch; DigitalWatchFaceService.Engine cannot be converted to Context)
My question is: "Is there a way to start an activity from a watchface"? Watchface uses CanvasWatchFaceService.Engine which extends CanvasWatchFaceService.

There was a small missing part when using
Intent myIntent = new Intent(this, ...MainActivity.class) MainActivity.class)
The intent is created inside another class, here an anonymous inner class canvas click listener. The code does not refer the instance of the Activity (or Context) as intended but the instance of the anonymous inner class canvas ClickListener.
So the right way was to provide the correct context of class.
Intent myIntent = new Intent(DigitalWatchFaceService.this, com.jorc.android.wearable.watchface.watchface.MainActivity.class);
getApplicationContext().startActivity(myIntent)
;

Related

How to implement an Alert box in xamarin forms android

I implemented a Dependency Service to display alert box in my xamarin forms app.My app crashes when I call the alert box in android.
Here is My code
Android.App.AlertDialog.Builder _dialog = new AlertDialog.Builder(Android.App.Application.Context);
AlertDialog _alertDialog = _dialog.Create();
_alertDialog.SetTitle("Unauthorized");
_alertDialog.SetMessage("Please login again to continue using the App);
_alertDialog.SetButton("OK", (c, ev) => { CloseApp(); });
_alertDialog.Show();
It throws an exception:-Unable to add window -- token null is not for an application in android.
How to fix this Please help me
Unable to add window -- token null is not valid; is your activity running?
You are using a Application context and you need to use an Activity based one.
So you need the current Activity's context within your Forms' dependancy class which you can obtain that via multiple methods; A static var on the MainActivity, using the "CurrentActivityPlugin", etc...
As a quick fix, add a static Context variable to your MainActivity class and set it in the OnResume override.
public static Context context;
protected override void OnResume()
{
context = this;
base.OnResume();
}
Then change your context to that static one:
Android.App.AlertDialog.Builder _dialog = new Android.App.AlertDialog.Builder(MainActivity.context);

Xamarin.UITest Backdoor with Splash Screen

I have an application that I'm attempting to put Xamarin UI Tests on. I need to Backdoor the app to bypass my login process.
My Backdoor method fires just fine.
[Activity(Label = "AppName", Icon = "#drawable/icon", Theme = "#style/Theme.Splash", MainLauncher = true, NoHistory = true)]
public class SplashActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
StartActivity(typeof(MainActivity));
}
[Java.Interop.Export("BackDoor")]
public void BackDoor()
{
var myActivity = {Magic code to get reference to the the instance of MainActivity goes here}
}
}
However its firing in my Splash screen and I need it get a reference to my actual MainActivity not my SplashActivity. How do I get a reference to the MainActivity in my BackDoor method?
Xamarin Backdoor Docs:
https://developer.xamarin.com/recipes/testcloud/start-activity-with-backdoor/
https://developer.xamarin.com/guides/testcloud/uitest/working-with/backdoors/
According to the guide for a backdoor method for Android, it can not return object type, only string, Java.Lang.String, or void. See: https://developer.xamarin.com/guides/testcloud/uitest/working-with/backdoors/
Don't you want to start the next Activity from the backdoor as in the guide? If so, just follow the guide you linked more closely.
Also, just double checked and returning object from the BackDoor method fails on build with a NullReferenceException. However, for "{Magic code to get reference to the the instance of MainActivity goes here}" you can do:
ActivityManager am = (ActivityManager)this.GetSystemService(Context.ActivityService);
var myActivity = am.GetRunningTasks(1)[0].TopActivity;
the myActivity will be a reference to the top most activity, but you can't return it from the BackDoor method anyway. You can return a string description of course. I do not know why you need a reference to the activity in your test code anyway as there is not much you can do with it in the test code.
How To Retrieve Current Activity
To retrieve the MainActivity, you can use #JamesMontemagno's CurrentActivityPlugin.
Add the Current Activity NuGet Package into your Xamarin.Android project, and then, in your Xamarin.Android Project, you can use the following line of code to retrieve the current activity and check that it is the MainActivity.
Activity currentActivity = Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity as MainActivity;
if (!(currentActivity is MainActivity))
throw new System.Exception("Current Activity is not MainActivity");
This plugin is open-sourced on GitHub.

RegisterInstance can't register with IntstancePerRequestMode in Autofac

I have a code in Web Api Delegating Handler that extract data from request header.
However, I can't register instance in Autofac container because Autofac container require SingleInstance only.
public class ExtractUserNameMessageHandler : DelegatingHandler
{
protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
var userNameFromFrontEnd = request.GetDependencyScope().GetService(typeof (IUserNameFromFrontEnd));
if (userNameFromFrontEnd == null)
{
var updatedContainerBuilder = new ContainerBuilder();
userNameFromFrontEnd = ExtractUserName(request);
if (userNameFromFrontEnd == null)
{
throw new Exception("We've got a request without UserName header");
}
updatedContainerBuilder.RegisterInstance(userNameFromFrontEnd)
.As<IUserNameFromFrontEnd>()
.InstancePerRequest();
var autofacDependencyResolver = GlobalConfiguration.Configuration.DependencyResolver as AutofacWebApiDependencyResolver;
if (autofacDependencyResolver == null)
{
throw new Exception("We can work with Autofac DI container");
}
updatedContainerBuilder.Update(autofacDependencyResolver.Container as IContainer);
}
When I try to update container I get an exception with message - registration can support singleinstance() sharing only.
What does it mean? I can't understand why we have this limitation. But in any cases my first goal - update container with new dependency.
Does anybody have ideas?
(Note: This question was cross-posted to the Autofac forums as well.)
When you register a specific instance, it's effectively a singleton - it's one instance, the instance you provided.
When you try to assign it InstancePerRequest or, really, any other lifetime scope besides SingleInstance, it doesn't make logical sense because you're not going to get a different instance per request (or whatever). You're going to get the exact same instance you registered, which is a singleton.
The exception message is trying to tell you how to avoid incorrect expectations: that it can't provide you a different instance per request even though you told it to because you didn't tell it how to create a new instance, you instead provided a specific instance.
If you need a different instance of an object per lifetime scope/request/whatever, you need to register a type, a delegate, or something else that tells Autofac how to create that new instance.
What that means is that if you want a different IUserNameFromFrontEnd per request, you need to move that logic out of a DelegatingHandler and into an Autofac registration delegate.
// Make sure to register the HttpRequestMessage in the container
// so you can resolve it...
builder.RegisterHttpRequestMessage(httpConfiguration);
// Then, whilst building your root container...
builder
.Register(ctx =>
{
var request = ctx.Resolve<HttpRequestMessage>();
return ExtractUserName(request);
})
.As<IUserNameFromFrontEnd>()
.InstancePerRequest();
Now it will probably do what you're looking to do - because you told Autofac how to create the instance that belongs in each request. It also means you don't need that DelegatingHandler anymore because Autofac will just do the right thing.
More advanced (and probably not useful here, but for completeness):
If, for whatever reason, you still feel like you need to modify the registration directly in the lifetime scope, instead of updating the container you should add the registration when the request lifetime scope is created.
Again, do not update the root container for per-lifetime-scope or per-request dependencies. It's not going to work how you think.
When a new lifetime scope is created, you can add registrations on the fly.
using(var scope = container.BeginLifetimeScope(
builder => builder.RegisterInstance(myfoo).As<IFoo>()))
{
// This will use the registrations in the container
// and the scope. f == myfoo
var f = scope.Resolve<IFoo>();
}
The AutofacDependencyResolver is the thing that creates the request lifetime scope and hands it off to Web API. You can see the full source here. The key method is BeginScope:
public IDependencyScope BeginScope()
{
var lifetimeScope = _container.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
return new AutofacWebApiDependencyScope(lifetimeScope);
}
If you create your own AutofacDependencyResolver you can modify how the scope is created:
public IDependencyScope BeginScope()
{
var lifetimeScope = _container.BeginLifetimeScope(
MatchingScopeLifetimeTags.RequestLifetimeScopeTag,
builder => builder.RegisterInstance(myfoo).As<IFoo>());
return new AutofacWebApiDependencyScope(lifetimeScope);
}
This isn't an explicitly supported extension point in the Autofac Web API integration right now - that's why you'd have to create your own resolver.
However, this seems like overkill to solve the thing it appears you're trying to solve. I strongly recommend just registering the delegate with Autofac rather than trying to update existing containers or scopes. You will have far more luck using the path of least resistance.

Xamarin Android two way communication between Javascript and C#

I am developing an app using Xamarin Android which has a WebView displaying a web page. I want to implement a two way communication between Javascript from WebView to c#. I could call C# from Javascript using this link. However i couldn't find a way to send data back from C# to Javascript.
Is there a way to send data back and forth in this approach. I thought writing a callback in Javascript would work but how to fire it from C# code.
Now, My problem is how to call WebView from a javascript interface class. I have a Javascript interface class as mentioned https://developer.xamarin.com/recipes/android/controls/webview/call_csharp_from_javascript/
namespace ScannerAndroid
{
public class JSInterface: Java.Lang.Object
{
Context context;
WebView webView;
public JSInterface (Context context, WebView webView1)
{
this.context = context;
this.webView = webView1;
}
[Export]
[JavascriptInterface]
public void ShowToast()
{
Toast.MakeText (context, "Hello from C#", ToastLength.Short).Show ();
this.webView.LoadUrl ("javascript:callback('Hello from Android Native');");
}
}
}
The code throws an exception at LoadUrl line. java.lang.Throwable: A WebView method was called on thread 'Thread-891'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 1) {42ce58a0} called on null, FYI main Looper is Looper (main, tid 1) {42ce58a0})
Now i am struggling how to refer the WebView from this Java script interface class
Yes. That is possible. If you are targeting KitKat or higher you can use:
webView.EvaluateJavascript("enable();", null);
Where in this case enable(); is a JS function.
If you are targeting lower API levels you can use LoadUrl();:
webView.LoadUrl("javascript:enable();");
EDIT:
The error you get where it complains on LoadUrl is because it for some reason happens on a non-UI thread.
Since you have already passed on the Context into your JavascriptInterface class, then you can simply wrap the contents of ShowToast in:
context.RunOnUiThread(() => {
// stuff here
});
Just change signature from Context to Activity and it should help you marshal you back on UI thread.

Unable to send a json message from android app to chromecast receiver app

Can't send a json message from my android app to the receiver app.
Android App
I've created my custom MessageStream and I'm using this namespace "com.jujuy.chromecast".
Once I get the channel from the session I attach MyCustomMessageStream to it then and call the method to send the message.
MyCustomMessageStream cm = new MyCustomMessageStream();
channel.attachMessageStream(cm);
cm.sendTestMessage("Hello!");
Receiver App
var receiver = new cast.receiver.Receiver(
APP-ID,
["com.jujuy.chromecast"],
"",
5);
var channelHandler = new cast.receiver.ChannelHandler("com.jujuy.chromecast"); // I think it's not necessary to use com.jujuy.chromecast
channelHandler.addEventListener(cast.receiver.Channel.EventType.MESSAGE, onMessage.bind(this));
channelHandler.addChannelFactory(receiver.createChannelFactory("com.jujuy.chromecast"));
receiver.start();
// message listener
function onMessage(event) {
document.getElementById("messageLabel").innerHTML = event.message.type;
}
After start the session () I receive this message
"failed to start application: no channel info received"
on onSessionStartFailed() method and the tv screen turns black.
I think something is wrong with the world "com.jujuy.chromecast", I saw in other examples they use cast.receiver.RemoteMedia.NAMESPACE, I'm not sure if I can change it with the namespace used in MyCustomMessageStream.
I saw in TicTacToe example they use a different way to get de CastDevice object than the documentation says. Could be this the problem?
My chromecast is whitelisted and I was able to run many examples without problem.
I used a custom receiver app to test play video and audio. Any idea?
This is most likely because your namespace on the sender doesn't match the namespace on the receiver. You need to make sure you pass it in as a parameter to the constructor for your custom MessageStream.
Here's an example:
public class MyCustomMessageStream extends MessageStream {
private static final String APP_NAMESPACE = "com.jujuy.chromecast";
protected MyCustomMessageStream(){
super(APP_NAMESPACE);
}
public final void sendTestMessage(String message){
// ...
}
//...
}
You shouldn't need to use the remote media namespace to send messages, that's for media playback. Here's a more in depth answer: https://stackoverflow.com/a/18499253/1839298
At first I couldn't get my package namespace to work, you might try a single word namespace, like 'TEST', to see if you can get that working then proceed from there.

Resources