How do I pass an Activity via MethodChannel in Flutter? - flutter

I'm making a Flutter app that needs to call a third party library (jar file provided).
The Java documentation for the third-party API object is created by calling something
public class MainActivity extends Activity {
private ThirdPartyAPI mAPI;
private ThirdPartyAPICallbacks mCallbacks = new ThirdPartyAPICallbacks(){
#Override
public void Connected() {
}
};
protected void onCreate(Bundle savedInstanceState) {
mAPI = new ThirdPartyAPI(this, mCallbacks); // how do we do the equivalent in Flutter?
}
}
How do I do this in Flutter?
I tried MethodChannel, but I don't know what to pass as the Activity instance to the ThirdPartyAPI constructor.

MethodChannel doesn't and will never allow to send something like an Activity.
The only types allowed are the following from the official DOCS:
If you really need to send something to Flutter, you'd need to create a method on Flutter which will call Java/Kotlin side, then get anything important you need from that 3rd party API/Library/Etc and send that info back to Flutter using a MethodChannel.

You don't transfer to Activity across the method channel. It only ever exists at the Java end. Instead it gets 'attached' to the Java end of the channel.
When creating a plugin (this is probably preferred as you can re-use it in different projects), make sure the plugin class also implements ActivityAware. Create stub implementation for the four methods required, in particular onAttachedToActivity.
For example:
public class ThirdPartyApiPlugin implements FlutterPlugin, ActivityAware {
#Override
public void onAttachedToActivity(ActivityPluginBinding binding) {
Activity activity = binding.getActivity();
mAPI = new ThirdPartyAPI(activity, mCallbacks);
}
If not using a plugin, modify the MainActivity class as described here.
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "someChannelName";
#Override
public void configureFlutterEngine(#NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
mAPI = new ThirdPartyAPI(this, mCallbacks);
new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL)
.setMethodCallHandler(
(call, result) -> {
// Note: this method is invoked on the main thread.
// TODO
}
);
}
}

Related

Why is onChanged() not called when observing LiveData

This question is a follow up to this problem here: How to make retrofit API call using ViewModel and LiveData
The mistakes 1 and 2 highlighted in that post's response have been fixed. For mistake 3, I haven't yet moved the API call to a repository, but I will once the code start working properly.
So I'm trying to make an API call using Retrofit, using MVVM with LiveData and ViewModel. The API call (which currently is in the ViewModel), is working properly, but the changes is not being picked up by the Observer in the Activity.
I've setup my ViewModel observer as follow:
public class PopularGamesActivity extends AppCompatActivity {
private static final String igdbBaseUrl = "https://api-endpoint.igdb.com/";
private static final String FIELDS = "id,name,genres,cover,popularity";
private static final String ORDER = "popularity:desc";
private static final int LIMIT = 30;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_popular_games);
PopularGamesViewModel popViewModel = ViewModelProviders.of(this).get(PopularGamesViewModel.class);
popViewModel.getGameList().observe(this, new Observer<List<Game>>() {
#Override
public void onChanged(#Nullable List<Game> gameList) {
String firstName = gameList.get(0).getName();
Timber.d(firstName);
}
And my ViewModel code is as follow:
public class PopularGamesViewModel extends AndroidViewModel {
private static final String igdbBaseUrl = "https://api-endpoint.igdb.com/";
private static final String FIELDS = "id,name,genres,cover,popularity";
private static final String ORDER = "popularity:desc";
private static final int LIMIT = 30;
public PopularGamesViewModel(#NonNull Application application) {
super(application);
}
public LiveData<List<Game>> getGameList() {
final MutableLiveData<List<Game>> gameList = new MutableLiveData<>();
// Create the retrofit builder
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(igdbBaseUrl)
.addConverterFactory(GsonConverterFactory.create());
// Build retrofit
Retrofit retrofit = builder.build();
// Create the retrofit client
RetrofitClient client = retrofit.create(RetrofitClient.class);
Call<List<Game>> call = client.getGame(FIELDS,
ORDER,
LIMIT);
call.enqueue(new Callback<List<Game>>() {
#Override
public void onResponse(Call<List<Game>> call, Response<List<Game>> response) {
Timber.d("api call sucesss");
if (response.body() != null) {
Timber.d("First game: " + response.body().get(0).getName());
gameList.setValue(response.body());
}
}
#Override
public void onFailure(Call<List<Game>> call, Throwable t) {
Timber.d("api call failed");
}
});
return gameList;
}
}
When I run the code, the onResponse in the ViewModel class will output the correct response from the API call, so the call is working properly. But the onChanged() in the PopularGamesActivity class will never get called. Can someone shed some light on what I'm doing wrong? Thank you!
Ok, so this turned out to be a weird android studio bug. I was initially running the code on my real nexus 4 device, and the onChange never gets called. However, after running it on an emulated device, it started working immediately. And now, it's working on my real device too.
I don't know the actual reason behind it, but if anyone in the future run into a problem where onChange won't get called, try switching device/emulators.
Cheers.
I debug the code on a device and have the same problem.
M'n solution was simply activate the device screen.
The screensaver was the problem..

Resty-GWT custom callback on async start and end

I use resty gwt for all server communication. I would like some indicator that would show the operation is in progress.
I consider 2 aproaches:
progressbar, which will show in progress percentage;
animation, that will be showed while operation is in progress, but without any percantage.
I've assumed that I need to add custom filter with callback.
I would like to fire events like: RestyGwtComunicationStart and RestyGwtComunicationEnd, or callback to fire onComunicationStarted and
onComunicationEnded. I would like to have this declared in one place, RestyGWT Dispatcher configuration. Also if there was an error I would like to fetch the error.
But I don't know where to start. There is no word about it in documentations.
Can I ask You for help? How can I do this?
So if you want to know that a request has been sent it is up to you in your GWT app to treat that. You can send an event when you trigger your request. You have multiple way of doing this.
Have a look at Request Dispatcher inside the doc https://resty-gwt.github.io/documentation/restygwt-user-guide.html
Then if you want to get progress info, as HTTP calls are synchronous. So there is no way to do this easily.
The way I have been doing it is the following:
1) Create a first call to initiate a processing on the backend with a POST, this will return the ID of your processing
2) Then do a GET on your processing ID that will return the progress. Once the progress is 100% it will return the ID of the result
3) GET the result with the result ID
(You can mix 2 and 3 together eventually and return result when progress is 100% in the same DTO)
Another option is to replace 2) by pushing info from backend to front end (html5 websocket)
Someone already did it as a pull-request to resty. Guess you can give it a try:
https://github.com/resty-gwt/resty-gwt/pull/151
Unfortunately "Dispatcher/Callback filters" feature does not described in the official documentation. But I can suggest next solution (this code should be placed in EntryPoint implementation of your module):
public void onModuleLoad() {
//...
//used to show busy indicator before send HTTP request
DispatcherFilter busyIndicatorDispatcherFilter = new DispatcherFilter() {
#Override
public boolean filter(Method method, RequestBuilder builder) {
BusyIndicator.show();
return true;
}
};
//used to show busy indicator after HTTP response recieved
CallbackFilter busyIndicatorCallbackFilter = new CallbackFilter() {
#Override
public RequestCallback filter(Method method, Response response, RequestCallback callback) {
BusyIndicator.hide();
return callback;
}
};
//registering FilterawareDispatcher (and busy indicator filters) as default Dispatcher
Defaults.setDispatcher(new DefaultFilterawareDispatcher(
busyIndicatorDispatcherFilter,
new DefaultDispatcherFilter(new DefaultCallbackFactory(busyIndicatorCallbackFilter))));
//...
}
Unfortunately I did not get adequate answer, So I developed my own solution.
At first I've added Resty configuration RestyGwtConfig to my Module configuration
public class ClientModule extends AbstractPresenterModule {
#Override
protected void configure() {
bind(RestyGwtConfig.class).asEagerSingleton();
install(new DefaultModule.Builder()
.defaultPlace(Routing.HOME.url)
.errorPlace(Routing.ERROR.url)
.unauthorizedPlace(Routing.LOGIN.url)
.tokenFormatter(RouteTokenFormatter.class).build());
install(new AppModule());
install(new GinFactoryModuleBuilder().build(AssistedInjectionFactory.class));
bind(ResourceLoader.class).asEagerSingleton();
}
}
then I've set Custom distpatcher for all my comunication requests of resty gwt.
import org.fusesource.restygwt.client.Defaults;
import org.fusesource.restygwt.client.Resource;
import pl.korbeldaniel.cms.shared.ServiceRouting;
import com.google.gwt.core.client.GWT;
import com.google.inject.Inject;
public class RestyGwtConfig {
#Inject
public RestyGwtConfig(RestyDispatcher dispatcher) {
Defaults.setDispatcher(dispatcher);
}
}
Then I've added custom filter (ProgressIndicatorFilter) to handle communication's start and end callbacks:
import org.fusesource.restygwt.client.Method;
import org.fusesource.restygwt.client.dispatcher.DefaultFilterawareDispatcher;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestException;
import com.google.inject.Inject;
public class RestyDispatcher extends DefaultFilterawareDispatcher {
#Inject
public RestyDispatcher(ProgressIndicatorFilter progressIndicatorFilter) {
addFilter(progressIndicatorFilter);
}
}
in filter class method overriden filter I've added an event trigger (eventBus.fireEvent(new IndicatorEvent("Rest-Gwt Comunication started"));) and registered callback, here is whole code:
import org.fusesource.restygwt.client.Method;
import org.fusesource.restygwt.client.dispatcher.DispatcherFilter;
import pl.korbeldaniel.cms.client.template.progressIndicator.IndicatorEvent;
import com.google.gwt.http.client.RequestBuilder;
import com.google.inject.Inject;
import com.google.web.bindery.event.shared.EventBus;
class ProgressIndicatorFilter implements DispatcherFilter {
private AssistedInjectionFactory factory;
private EventBus eventBus;
#Inject
public ProgressIndicatorFilter(AssistedInjectionFactory factory, EventBus eventBus) {
this.factory = factory;
this.eventBus = eventBus;
}
#Override
public boolean filter(Method method, RequestBuilder builder) {
builder.setCallback(factory.createProgressIndicatorCallback(method));
eventBus.fireEvent(new IndicatorEvent("Resty-Gwt Comunication started"));
return true;
}
}
Registering a callback couldn't be done straight forward, like
new ProgressIndicatorDispatcherCallback()
cause I use dependency injection. So I've created a factory to assist injection as follow:
public interface AssistedInjectionFactory {
ProgressIndicatorDispatcherCallback createProgressIndicatorCallback(Method method);
}
Here and here You can find more Assisted Injection info.
Here is the callback code:
class ProgressIndicatorDispatcherCallback implements RequestCallback {
private RequestCallback requestCallback;
private EventBus eventBus;
#Inject
public ProgressIndicatorDispatcherCallback(#Assisted Method method, EventBus eventBus) {
this.requestCallback = method.builder.getCallback();
this.eventBus = eventBus;
}
#Override
public void onResponseReceived(Request request, Response response) {
endComunicationFireIvent();
requestCallback.onResponseReceived(request, response);
}
#Override
public void onError(Request request, Throwable exception) {
endComunicationFireIvent();
requestCallback.onError(request, exception);
}
private void endComunicationFireIvent() {
eventBus.fireEvent(new IndicatorEvent("Rest-Gwt Comunication ended"));
}
}

Pushing data from one user to another in Vaadin web app

I get the fact that it might take more than 10 lines of code (hopefully not more than 50), but I was wondering if you could help me anyway.
I'm trying to update one user's UI thread at runtime, based on another user's input. I've created a basic project which implements three predefined users (jim, tom and threeskin). I'd like to send a message from jim to tom and have it appear as a new Label object in tom's UI, without threeskin ever knowing about it, even though they're all logged in. Oh, and jim shouldn't have to refresh his page. The label should just spawn on screen out of it's own accord.
To say that I'd appreciate some help would be the understatement of the decade.
public class User {
public String nume;
public User(String nume) {
super();
this.nume = nume;
}
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public class Engine implements ServletContextListener {
public static ArrayList<User>userbase;
public void contextDestroyed(ServletContextEvent arg0) { }
public void contextInitialized(ServletContextEvent arg0) {
System.out.println("This code is running at startup");
userbase =new ArrayList<User>();
userbase.add(new User("jim"));userbase.add(new User("tom"));userbase.add(new User("threeskin"));
}
}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
public class InfigeUI extends UI {
User us3r;
#WebServlet(value = "/*", asyncSupported = true)
#VaadinServletConfiguration(productionMode = false, ui = InfigeUI.class)
public static class Servlet extends VaadinServlet {}
protected void init(VaadinRequest request) {
VerticalLayout everything=new VerticalLayout();
setContent(everything);
if (us3r==null){everything.addComponent(auth());}else{everything.addComponent(main());}
}
ComponentContainer auth(){
final VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
TextField userField=new TextField();
Button login = new Button("Log in");
login.addClickListener(new Button.ClickListener() {
public void buttonClick(ClickEvent event) {
us3r=login(userField.getValue());
if (us3r!=null){
saveValue(InfigeUI.this, us3r);
layout.removeAllComponents();
layout.addComponent(main());
}else{Notification.show("I only know jim, tom and threeskin. Which one are you?");}}
});
layout.addComponent(userField);
layout.addComponent(login);
return layout;
}
User login(String nume){
for (int i=0;i<Engine.userbase.size();i++){
if (nume.equals(Engine.userbase.get(i).nume)){return Engine.userbase.get(i);}
}
return null;
}
static void saveValue(InfigeUI ui,User value){
ui.us3r=value;
ui.getSession().setAttribute("something", value);
VaadinService.getCurrentRequest().getWrappedSession().setAttribute("something", value);
}
ComponentContainer main(){
VerticalLayout vl=new VerticalLayout();
Label label=new Label("This is the post-login screen");
String name=new String(us3r.nume);
Label eticheta=new Label(name);
TextField to=new TextField("Send to");
TextField message=new TextField("Message");
Button sendNow=new Button("Send now!");
vl.addComponent(eticheta);
vl.addComponent(label);
vl.addComponent(eticheta);
vl.addComponent(to);
vl.addComponent(message);
vl.addComponent(sendNow);
return vl ;
}
}
Basically you want three things
UI updates for a user which does no action himself, or in other words a message sent from the server to the browser. To enable this, you need to annotate the UI class using #Push. Otherwise, the update will only be shown when the user does something which causes a server visit, e.g. clicks a button
Some way of sending messages between UI instances (there is one UI instance per user). You can use some message bus implementation for this (CDI, Spring, ...) or you can make a simple on using a static field (static fields are shared between all users). See e.g. https://github.com/Artur-/SimpleChat for one way of doing it. It's also a good idea here to avoid all *.getCurrent methods as they in many cases will refer to another UI than you think (e.g. sender when you are in the receiver code), and you will do something else than you intend.
Safely update a UI when a message arrives. This is done using UI.access, also visible in the chat example.
First of all you need to enable the server push on your project help
based on Vaadin Documentation.
However, below code example will give what you want:
Create an Broadcast Listener Interface:
public interface BroadcastListener {
public void receiveBroadcast(final String message);
}
The Broadcaster Class:
public class Broadcaster {
private static final List<BroadcastListener> listeners = new CopyOnWriteArrayList<BroadcastListener>();
public static void register(BroadcastListener listener) {
listeners.add(listener);
}
public static void unregister(BroadcastListener listener) {
listeners.remove(listener);
}
public static void broadcast(final String message) {
for (BroadcastListener listener : listeners) {
listener.receiveBroadcast(message);
}
}
}
Your UI with Push Enalbed (via Annotation):
#Push
public class BroadcasterUI extends UI implements BroadcastListener {
#Override
protected void init(VaadinRequest request) {
final VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
setContent(layout);
final TextArea message = new TextArea("",
"The system is going down for maintenance in 10 minutes");
layout.addComponent(message);
final Button button = new Button("Broadcast");
layout.addComponent(button);
button.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
Broadcaster.broadcast(message.getValue());
}
});
// Register broadcast listener
Broadcaster.register(this);
}
#Override
public void detach() {
Broadcaster.unregister(this);
super.detach();
}
#Override
public void receiveBroadcast(final String message) {
access(new Runnable() {
#Override
public void run() {
Notification n = new Notification("Message received",
message, Type.TRAY_NOTIFICATION);
n.show(getPage());
}
});
}
you can find the full link here.

Using HeaderResponseContainer: No FilteringHeaderResponse is present in the request cycle

I'm trying to add a custom HeaderResponseContainer in my wicket application. The tutorial looks quite simple (see Positioning of contributions), but when I add these lines and run the application I alwas get an IllegalStateException:
java.lang.IllegalStateException: No FilteringHeaderResponse is present in the request cycle. This may mean that you have not decorated the header response with a FilteringHeaderResponse. Simply calling the FilteringHeaderResponse constructor sets itself on the request cycle
at org.apache.wicket.markup.head.filter.FilteringHeaderResponse.get(FilteringHeaderResponse.java:165)
at org.apache.wicket.markup.head.filter.HeaderResponseContainer.onComponentTagBody(HeaderResponseContainer.java:64)
at org.apache.wicket.markup.html.panel.DefaultMarkupSourcingStrategy.onComponentTagBody(DefaultMarkupSourcingStrategy.java:71)
...
Yes, I already saw the note about FilteringHeaderResponse. But I am not sure where I should call the constructor. I already tried to add it in renderHead before calling response.render but I still get the same exception:
public void renderHead(IHeaderResponse response) {
super.renderHead(response);
FilteringHeaderResponse resp = new FilteringHeaderResponse(response);
resp.render(new FilteredHeaderItem(..., "myKey"));
}
You can create a decorator that wraps responses in a FilteringHeaderResponse:
public final class FilteringHeaderResponseDecorator implements IHeaderResponseDecorator {
#Override
public IHeaderResponse decorate(IHeaderResponse response) {
return new FilteringHeaderResponse(response);
}
}
And that set it during application initialization:
Override
public void init() {
super.init();
setHeaderResponseDecorator(new FilteringHeaderResponseDecorator());
}
I just ran into this same problem and found that the Wicket In Action tutorial leaves out the part about setting up a custom IHeaderResponseDecorator in your main Wicket Application init. The Wicket guide has a more thorough example:
Apache Wicket User Guide - Put JavaScript inside page body
You need something like this in your wicket Application:
#Override
public void init()
{
setHeaderResponseDecorator(new JavaScriptToBucketResponseDecorator("myKey"));
}
/**
* Decorates an original IHeaderResponse and renders all javascript items
* (JavaScriptHeaderItem), to a specific container in the page.
*/
static class JavaScriptToBucketResponseDecorator implements IHeaderResponseDecorator
{
private String bucketName;
public JavaScriptToBucketResponseDecorator(String bucketName) {
this.bucketName = bucketName;
}
#Override
public IHeaderResponse decorate(IHeaderResponse response) {
return new JavaScriptFilteredIntoFooterHeaderResponse(response, bucketName);
}
}

How to access an already-running Application Context from a Sync Adapter service in Android?

I have an app that consists of several activities, and I use the Application Context (entended from the Application Class, and I made it persistent) to share data and objects between all the activities. I use the Application Class instead of a background service for several good reasons, which I won't go into here.
I also recently added an custom contact sync adapter to my app. It's under the same package, in the same APK. So, I set it up to access the Application Context just like everything else in my app to give it access to all the shared data and objects. However, even though it works (mostly), it creates a new instance of the Application Context. So there are basically 2 separate instances of my application running, and the data isn't shared between them.
I think that the problem is that my Applicattion never starts the sync service, the OS does. All my other activities are either started by the application, or the main activity accesses the Application Context when it launches, and then the App Context controls everything else. Is there a way to have the sync service access the existing Application Context, instead of creating the new instance of it?
Here's the basic structure of my app:
The application
package com.mycomany.myapp;
public class MyApp extends Application{
...
}
Activity1
package com.mycomany.myapp;
public class MyActivity1 extends Activity{
MyApp a;
#Override
public void onCreate(Bundle savedInstanceState){
a = (MyApp) getApplicationContext();
...
}
}
SyncAdapterService
package com.mycomany.myapp;
public class SyncAdapterService extends Service {
private static SyncAdapterImpl sSyncAdapter = null;
private static final Object sSyncAdapterLock = new Object();
private static ContentResolver mContentResolver = null;
private static MyApp a;
public SyncAdapterService() {
super();
}
private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
private Context mContext;
public SyncAdapterImpl(Context context) {
super(context, true);
mContext = context;
}
#Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
try {
SyncAdapterService.performSync(mContext, account, extras, authority, provider, syncResult);
} catch (OperationCanceledException e) {}
}
}
#Override
public void onCreate() {
super.onCreate();
synchronized (sSyncAdapterLock) {
if(a == null){
a = (MyApp) getApplicationContext();
}
if (sSyncAdapter == null) {
sSyncAdapter = new SyncAdapterImpl(getApplicationContext());
}
}
}
#Override
public IBinder onBind(Intent intent) {
return sSyncAdapter.getSyncAdapterBinder();
}
private static void performSync(Context context, Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult)
throws OperationCanceledException {
...
}
}
Have you copy&pasted this training for the SyncAdapter http://developer.android.com/training/sync-adapters/creating-sync-adapter.html?
At the end there is this XML Snippet:
<service
android:name="com.example.android.datasync.SyncService"
android:exported="true"
android:process=":sync">
<intent-filter>com.example.android.datasync.provider
<action android:name="android.content.SyncAdapter"/>
</intent-filter>
<meta-data android:name="android.content.SyncAdapter"
android:resource="#xml/syncadapter" />
</service>
With the attribute android:process=":sync" meaning you create a separate sync process. Remove it and you're good to go.
You might want to look into binding the service to your Application context. That way, if your application context does not exist, the service won't exist, as it runs in the same process (that of the Application) . See bindSerivce()
If your service is a remote one try using callbacks
Are you still having this problem?
If the service is declared in your manifest file without specifying a different android:process, isn't it supposed to run in the default process defined by your task?
Can't you in that case just use getApplicationContext to get what you need?
I have my sync adapter implemented in this way and it is working