In capacitor 3, I've build a minimal plugin in Android which can send events from the native Android to javascript, whenever something is happening, by listening for events in javascript.
It's working fine from within the class, i.e. overriding OnStart of the class. But when I want to call the class and notify listeners from another class, how do I call the plugin class without making a new instance, so that whoever is listening, can still get the event.
I tried implementing it as singleton, but it is not allowed, I get a error in javascript error handler when doing so, just when I start listening to events. Also I can't make it static.
So how do I call the plugin class without making a new instance?
The javascriptcode:
IonicNativePlugin.addListener('EVENT_LISTENER_NAME', (message) => {
alert(message.message);
}).catch((error) => {
alert('Error in EventListener');
});
The java MainActivity code:
public class MainActivity extends BridgeActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerPlugin(IonicNativeLogisPlugin.class);
}
}
The plugin java code:
#CapacitorPlugin(name = "IonicNativePlugin")
public class IonicNativePlugin extends Plugin {
#PluginMethod
public void NotifyListeners(PluginCall call){
JSObject result = new JSObject();
result.put("message", "Hello!");
notifyListeners("EVENT_LISTENER_NAME", result);
}
public void TheMethodIWantToCall(){
JSObject ret = new JSObject();
ret.put("message", "Hello from LISTENER!!");
notifyListeners("EVENT_LISTENER_NAME", ret);
}
}
The class I like to call from:
public class NotificationServiceExtension implements OSRemoteNotificationReceivedHandler {
#Override
public void remoteNotificationReceived(Context context, OSNotificationReceivedEvent notificationReceivedEvent) {
OSNotification notification = notificationReceivedEvent.getNotification();
notificationReceivedEvent.complete(notification);
<-- call should be here
}
}
Related
I try to do list of usb devices, connected by serial odt with smartphone, within xamarin.forms.
To do that I use this project https://github.com/anotherlab/UsbSerialForAndroid
How to do listview in shared project with devices from Project.Droid.MainActivity? I tried to do that with dependency service:
This is my Page1(where I want to have listview):
public partial class Page1 : ContentPage {
public Page1()
{
InitializeComponent();
DependencyService.Get<Interface1>().moj();
}
}
My interface:
namespace SensDxMobileApp.Views.MainWindow {
public interface Interface1 {
void moj();
}
}
And MyActivity(Droid project):
[assembly: Xamarin.Forms.Dependency(typeofProject.Droid.MainActivity))]
namespace Project.Droid {
public class MainActivity: Interface
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
listView = new Android.Widget.ListView;
}
public async void moj()
{
adapter = new UsbSerialPortAdapter(this);
listview.Adapter = adapter;
listView.ItemClick += async (sender, e) =>
{
await OnItemClick(sender, e);
};
await PopulateListAsync();
detachedReceiver = new UsbDeviceDetachedReceiver(this);
RegisterReceiver(detachedReceiver, new IntentFilter(UsbManager.ActionUsbDeviceDetached));
}
}
But I have an error "Object reference not set to an instance of an object.", on " DependencyService.Get().moj()" in Page1();
Did someone do something similar? Thanks
If you going the DependencyService route, then you'll want to create a separate class that implements Interface1 and registering that as a dependency service. I don't think you can register the MainActivity as a DependencyService implementation.
One problem that you are going to hit this is mostly async code and callbacks.
You also shouldn't be newing up an Android ListView as a DependencyService call. That would be better suited as a custom renderer. As a DependencyService implementation, you would want the moj() method to return data that can be consumed by the Xamarin.Forms code. You would need more than just that method. You would need code to initialize the UsbSerialPort class, code to query the list of devices, and then invoke a callback that sends back that list, In theory anyway. I never tested that library with Forms.
I have a use case in which I need to update points(markers) on the map when a certain event occurs inside a service. For that am using an interface callback method.
public interface draw
{
void updatePoints(Context context) throws JSONException;
}
Now am using this interface in my Service like this :
draw dfc;
...............
//Inside a constructor of a class
dfc=new draw() {
#Override
public void updateFloatingPoints(Context context) throws JSONException {
System.out.println("Callback in Service");
CustomViewView.registerDPCListener(dfc);
}
};
Now within the same Service, under a certain function, am passing the interface as an input parameter i.e callback and inside that function am calling
callback.updateFloatingPoints(context);
Now when am running the service, I can see the callback in Service statement. Till here everything is okay.
Now problem is that, when the callback is received, I want to do some work another class. Let's call it CustomView. I implemented the interface in that class too but I am not able to receive the callbacks over there. Follow is the bare bone code of the class
public class CustomView extends ABCD implements draw {
private draw dfcListner=null;
public void registerDPCListener(drawPath listener) throws JSONException {
this.dfcListner = listener;
if(dfcListner!=null)
{
System.out.println("Listener initialized");
//dfcListner.updateFloatingPoints(mcontext);
}
else
{
System.out.println("Listener is not initialized");
}
}
//Another functions in between over here
#Override
public void updateFloatingPoints(Context context) throws JSONException {
System.out.println("Inside CustomView callback");
}
}
How do I go about it?
I have an android application that is connected to the computer via USB cable. I use a TCPServer Class to send messages and listen. For example:
When I send a message like: request:x
I get the response: response:x:55
I need to make changes on my activity according to the response I get. At the moment I temporarily solved the problem by passing activity and activity class object to the TCPServer's constructor
public TCPServer(int portNum, Activity activity, IntroActivity ia) {
super();
port = portNum;
this.activity = activity;
this.ia = ia;
}
Then after I receive the response:
void updateButton(final int color, final String txt) {
activity.runOnUiThread(new Runnable() {
public void run() {
ia.getConnectionButton().setBackgroundColor(color);
ia.getConnectionButton().setText(txt);
}
});
}
As you see, this is not effective at all. I need to somehow notify the activity whenever a relevant variable is received. I use a Class for GlobalVariables and change those static variables after listen(), however I am having troubles notifying the activity.
First of all, it is almost always bad practice to pass Activity instances around. This is a time when it's bad.
Define an interface and use a callback to let the activity know that a response has been received.
public interface ResponseReceivedListener {
void onResponseReceived(int arg1, string arg2); // <- add arguments you want to pass back
}
In your TCPServer class:
ArrayList<ResponseReceivedListener> listeners = new ArrayList<>();
// ...
public void setResponseReceivedListener(ResponseReceivedListener listener) {
if (!listeners.contains(listener) {
listeners.add(listener);
}
}
public void removeResponseReceivedListener(ResponseReceivedListener listener) {
if (listeners.contains(listener) {
listeners.remove(listener);
}
}
When you receive a response:
for (ResponseReceivedListener listener : listeners) {
listener.onResponseReceived(arg1, arg2);
}
In your Activity:
public class MainActivity extends Activity implements ResponseReceivedListener {
// ...
#Override
public void onCreate(Bundle savedInstanceState) {
// ...
tcpServer.setResponseReceivedListener(this);
// ...
}
public void onResponseReceived(int arg1, string arg2) {
// do whatever you need to do
}
// ...
}
All from memory so please excuse typos.
This approach decouples the classes. The TCP Server has no knowledge of the activities. It simply calls back to any listeners registered. Those listeners might be Activities, they might be services. They might be instances of MySparklyUnicorn. The server neither knows nor cares. It simply says "if anyone's interested, I've received a response and here are the details".
I hope this will be simple question.
I have main activity, on this activity I create an instance of some class. How to send some event form one class to main one? How to setup some kind a listener to send notifications between classes. Only option what I know/use right now is to keep reference to parent class and call directly some function from child class.
I'm wonder if it possible to create something like is in ActionScript, where I can call to dispatchEvent(new Event("name")) and later setup addEventlistener("name" function) ??
If "I implement some class" means that you have declared a nested class inside your Activity class than nested non-static class will have a reference to parent class object.
In general, you can always create dispatcher/listener pattern your self. Create listener interface and add either addListener or setListener method to class that will dispatch event.
Example of listener:
public interface IAsyncFetchListener extends EventListener {
void onComplete(String item);
void onError(Throwable error);
}
Example of event dispatcher:
public class FileDownloader {
IAsyncFetchListener fetchListener = null;
...
private void doInBackground(URL url) {
...
if (this.fetchListener != null)
this.fetchListener.onComplete(result);
}
public void setListener(IAsyncFetchListener listener) {
this.fetchListener = listener
}
}
Example of class with event listener:
public class MyClass {
public void doSomething() {
FileDownloader downloader = new FileDownloader();
downloader.setListener(new IAsyncFetchListener() {
public void onComplete(String item) {
// do something with item
}
public void onError(Throwable error) {
// report error
}
});
downloader.start();
}
}
Just implement a listener(or a list of listeners) on the class that generates the events.
When an event is generated iterate over this list and call a method that all the listeners must implement (via an interface maybe?)
Hope it helped,
JQCorreia
During the most recent Google IO, there was a presentation about implementing restful client applications. Unfortunately, it was only a high level discussion with no source code of the implementation.
In this diagram, on the return path there are various different callbacks to other methods.
How do I declare what these methods are?
I understand the idea of a callback - a piece of code that gets called after a certain event has happened, but I don't know how to implement it. The only way I've implemented callbacks so far have been overriding various methods (onActivityResult for example).
I feel like I have a basic understanding of the design pattern, but I keep on getting tripped up on how to handle the return path.
In many cases, you have an interface and pass along an object that implements it. Dialogs for example have the OnClickListener.
Just as a random example:
// The callback interface
interface MyCallback {
void callbackCall();
}
// The class that takes the callback
class Worker {
MyCallback callback;
void onEvent() {
callback.callbackCall();
}
}
// Option 1:
class Callback implements MyCallback {
void callbackCall() {
// callback code goes here
}
}
worker.callback = new Callback();
// Option 2:
worker.callback = new MyCallback() {
void callbackCall() {
// callback code goes here
}
};
I probably messed up the syntax in option 2. It's early.
When something happens in my view I fire off an event that my activity is listening for:
// DECLARED IN (CUSTOM) VIEW
private OnScoreSavedListener onScoreSavedListener;
public interface OnScoreSavedListener {
public void onScoreSaved();
}
// ALLOWS YOU TO SET LISTENER && INVOKE THE OVERIDING METHOD
// FROM WITHIN ACTIVITY
public void setOnScoreSavedListener(OnScoreSavedListener listener) {
onScoreSavedListener = listener;
}
// DECLARED IN ACTIVITY
MyCustomView slider = (MyCustomView) view.findViewById(R.id.slider)
slider.setOnScoreSavedListener(new OnScoreSavedListener() {
#Override
public void onScoreSaved() {
Log.v("","EVENT FIRED");
}
});
If you want to know more about communication (callbacks) between fragments see here:
http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
No need to define a new interface when you can use an existing one: android.os.Handler.Callback. Pass an object of type Callback, and invoke callback's handleMessage(Message msg).
Example to implement callback method using interface.
Define the interface, NewInterface.java.
package javaapplication1;
public interface NewInterface {
void callback();
}
Create a new class, NewClass.java. It will call the callback method in main class.
package javaapplication1;
public class NewClass {
private NewInterface mainClass;
public NewClass(NewInterface mClass){
mainClass = mClass;
}
public void calledFromMain(){
//Do somthing...
//call back main
mainClass.callback();
}
}
The main class, JavaApplication1.java, to implement the interface NewInterface - callback() method. It will create and call NewClass object. Then, the NewClass object will callback it's callback() method in turn.
package javaapplication1;
public class JavaApplication1 implements NewInterface{
NewClass newClass;
public static void main(String[] args) {
System.out.println("test...");
JavaApplication1 myApplication = new JavaApplication1();
myApplication.doSomething();
}
private void doSomething(){
newClass = new NewClass(this);
newClass.calledFromMain();
}
#Override
public void callback() {
System.out.println("callback");
}
}
to clarify a bit on dragon's answer (since it took me a while to figure out what to do with Handler.Callback):
Handler can be used to execute callbacks in the current or another thread, by passing it Messages. the Message holds data to be used from the callback. a Handler.Callback can be passed to the constructor of Handler in order to avoid extending Handler directly. thus, to execute some code via callback from the current thread:
Message message = new Message();
<set data to be passed to callback - eg message.obj, message.arg1 etc - here>
Callback callback = new Callback() {
public boolean handleMessage(Message msg) {
<code to be executed during callback>
}
};
Handler handler = new Handler(callback);
handler.sendMessage(message);
EDIT: just realized there's a better way to get the same result (minus control of exactly when to execute the callback):
post(new Runnable() {
#Override
public void run() {
<code to be executed during callback>
}
});
You can also use LocalBroadcast for this purpose. Here is a quick guide
Create a broadcast receiver:
LocalBroadcastManager.getInstance(this).registerReceiver(
mMessageReceiver, new IntentFilter("speedExceeded"));
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Double currentSpeed = intent.getDoubleExtra("currentSpeed", 20);
Double currentLatitude = intent.getDoubleExtra("latitude", 0);
Double currentLongitude = intent.getDoubleExtra("longitude", 0);
// ... react to local broadcast message
}
This is how you can trigger it
Intent intent = new Intent("speedExceeded");
intent.putExtra("currentSpeed", currentSpeed);
intent.putExtra("latitude", latitude);
intent.putExtra("longitude", longitude);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
unRegister receiver in onPause:
protected void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
}
asume the main function is the activity that is triggering the event:
fun main() {
val worker = Worker()
worker.setOnCallListener(
object: OnCallListener {
override fun onCall() {
// here we define what should happen
// when the event accures
print("event happend")
}
}
)
// most events will be called from Android system itself
// but in our case we have to call it manually
worker.listener.onCall()
}
the Worker class has an instance of Type OnCallListener interface
and a method to set its value:
class Worker() {
lateinit var listener: OnCallListener
fun setOnCallListener(listener: OnCallListener) {
this.listener = listener
}
}
and the OnCallListener interface looks like this:
interface OnCallListener {
fun onCall()
}