embedding golang server with flutter - flutter

I have web server written in golang which uses graphql package gqlgen and gorm for database.
Since golang can be compiled and run on android I wanted to create offline version of my app where sqlite can be used for offline storage and import my whole server as an aar.
I have successfully built aar and add it on my flutter using gomobile by following instructions here
When I run my app, server is started on android it seems to work just fine and when opening http://localhost:8080/ on emulator's chrome app I am seeing graphql playground runs without any problem just like I see on browser in windows.
The only problem I face is that flutter app just shows a blank screen while server runs in background. The following are the logs printed when app is started
Launching lib\main.dart on sdk gphone64 x86 64 in debug mode...
Running Gradle task 'assembleDebug'...
√ Built build\app\outputs\flutter-apk\app-debug.apk.
Installing build\app\outputs\flutter-apk\app.apk...
Debug service listening on ws://127.0.0.1:62561/DyGpOhyuekw=/ws
Syncing files to device sdk gphone64 x86 64...
I/GoLog ( 6295): connect to http://localhost:8080/ for GraphQL playground
W/ux.offline( 6295): type=1400 audit(0.0:38): avc: denied { read } for name="somaxconn" dev="proc" ino=74990 scontext=u:r:untrusted_app:s0:c149,c256,c512,c768 tcontext=u:object_r:proc_net:s0 tclass=file permissive=0 app=com.nux.offline
I think maybe problem lies on the above logs avc: denied { read } for name="somaxconn" or something is causing the blocking of ui thread since its like flutter don't render a thing.
I am using flutter plugin to start server and this is ServerPlugin.kt
package com.mahesabu.server.server
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import lib.Lib.startServer
/** ServerPlugin */
class ServerPlugin : FlutterPlugin, MethodCallHandler {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel: MethodChannel
override fun onAttachedToEngine(#NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "server")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(#NonNull call: MethodCall, #NonNull result: Result) {
if (call.method == "startServer") {
try {
val port = startServer() //from golang bindings
result.success(port)
} catch (e: Exception) {
e.printStackTrace();
result.error("Error in starting server", "${e.message}", null);
}
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(#NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
}
And this is is dart code
class Server {
static const MethodChannel _channel = MethodChannel('server');
static Future<String?> startServer() async {
try {
final String? port = await _channel.invokeMethod('startServer');
return port;
} catch (e) {
log('startServer error: ${e.toString()}');
return null;
}
}
}
and my app's main is as follows
import 'package:flutter/material.dart';
import 'package:server/server.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final port = await Server.startServer(); //problem
print('port $port'); //This don't print
runApp(const MyApp());
}
On go side this how i start server
//start.go
package util
import (
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/playground"
"log"
"my-project/graph"
"my-project/graph/generated"
"net/http"
"os"
)
const defaultPort = "8080"
// StartServer This way so that it can be invoked via libs
func StartServer(Offline bool) string {
port := os.Getenv("PORT")
if port == "" {
port = defaultPort
}
db := InitDB(Offline)
config := generated.Config{Resolvers: &graph.Resolver{
DB: db,
}}
srv := handler.NewDefaultServer(generated.NewExecutableSchema(config))
http.Handle("/", playground.Handler("GraphQL playground", "/query"))
http.Handle("/query", srv)
log.Printf("connect to http://localhost:%s/ for GraphQL playground", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
return port
}
and this is lib for generating bindings with gomobile
//lib.go
package lib
import "my-project/util"
// StartServer This way so that it can be invoked via libs
func StartServer() string {
return util.StartServer(true)
}
Any help on fixing this will be appreciated.
Edit
I think problem occurs when embedded server tries to create a new port. I don't know if it is possible for an app to open a new port in android just like nodejs, golang open things like http://localhost:8080.
Now I think if there is a way to create port then I can run my app successfully but I don't know how exactly.
I was thinking if I can find any available port on android and use to start server maybe this stack could be possible. In kotlin something like this may work in finding port.
import java.net.ServerSocket
fun main() {
val serverPort = ServerSocket(0)
print(serverPort.toString())
}
but it crashes on android app when I try similar thing.
I have uploaded a repository on GitHub showing what I intend to do. It's just a simple golang server using gin and android app (with no flutter) it is available here.

"I don't know if it is possible for an app to open a new port in android just like nodejs, golang open things like localhost:8080"
To find out the root cause, try to run an HTTP server in android, such as How to create a HTTP server in Android?. If that succeeds, try to find the differences about how they deal with ports.
In addition, please be sure you have correct permission in androidmanifest.xml.
(Rephrased from my comments)

Related

Using SignalR.Client in Unity - The method or operation is not implemented

I want to use SignalR in a UWP project on the HoloLens 2 and my application is partially running (one way works, one not).
The way it doesn't work
It does not run, when I have HTTPS-Redirection on my .Net-6-Web-API Server enabled and try to ignore it via the ServerCertificateCustomValidationCallback line on unity side.
.Net 6-Project:
app.UseHttpsRedirection(); is active
Unity:
private void Start()
{
_hubConnection = new HubConnectionBuilder()
.WithUrl("http://192.168.178.41:5266/Signaler", (opts) =>
{
opts.HttpMessageHandlerFactory = (message) =>
{
// bypass SSL certificate
if (message is HttpClientHandler clientHandler)
{
clientHandler.ServerCertificateCustomValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;
}
return message;
};})
.WithAutomaticReconnect()
.Build();
Connect();
}
private async Task Connect()
{
_hubConnection.On<string>("SomeKey", (key) =>
{
Debug.Log($"New Key: {key}");
});
try
{
await _hubConnection.StartAsync(tokenSource.Token);
}
catch (Exception ex)
{ ... }
}
The error (shorted text) I get:
System.NotImplementedException: The method or operation is not implemented.
at XXXXX.Network.MySignalRClient+<>c.<Start>b__1_1 (System.Net.Http.HttpMessageHandler message) [0x0000a] in D:\Repositories\XXXXX\Unity\XXXXX\Assets\Scripts\NetworkClients\MySignalRClient.cs:31
at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection.CreateHttpClient () [0x00118] in <d4f814eb48994df3870fe7c8338cc73a>:0
at Microsoft.AspNetCore.Http.Connections.Client.HttpConnection..ctor (Microsoft.AspNetCore.Http.Connections.Client.HttpConnectionOptions httpConnectionOptions, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) [0x0009f] in <d4f814eb48994df3870fe7c8338cc73a>:0
The mentioned line 31 is the line with the ServerCertificateCustomValidationCallback.
How does the application work?
If I disable the HTTPS-Redirection and remove the lines with the callback:
_hubConnection = new HubConnectionBuilder()
.WithUrl("http://192.168.178.41:5266/Signaler")
.WithAutomaticReconnect()
.Build();
Expected Behavior
HTTPS redirection should not be disabled extra on the server side, but the line with the callback on unity side should be enough.
SignalR Version
Microsoft.AspNetCore.SignalR.Client.7.0.0 and all needed referenced Libraries for .Net Standard 2.0
Anything else?
Typical Setup:
Some DLLs are missing in the screenshot, because they are already included by other plugins that I use in Unity. The following packages are not shown: System.Buffers, System.Memory, System.Numerics.Vectors, System.Runtime.CompilerServices.Unsafe
Another project:
I also created another empty .Net-6-Project in Visual Studio with Microsoft.AspNetCore.SignalR.Client.7.0.0 to check if I can connect, trigger the hub and get notified by the hub. There I use exactly the same lines to bypass the ssl certification and it works. So I guess there is something wrong with the .Net Standard 2.0 DLLs?

Ask user to connect to a specific WiFi flutter

I need to connect the user's device to a specific WiFi. I have used some of packages like wifi_iot but According to google, In Android API Version > 29, We can not directly connect the user device to a specific WiFi.
Instead google suggests that using WifiNetworkSuggestion
I have 2 options :
Use MethodChannel in flutter and implement WifiNetworkSuggestion in Kotlin and use it in my project.
Inform user to connect to my specific wifi manually(and of course I have to show the password to user).
Which of these options you recommend?
and I don't have any experience on implementing option 1. is It possible to implement something like this in flutter project ?
And if you can come up with another option, I would be glad to share it with me.
For anyone who want to Implement something like this in flutter : Before Android API Version 29, you can directly connect user's device to specific WiFi without any problem. but in API Version after 29 (Android >= 10), you cannot do this. you have to ask user if he/she wants to connect to your WiFi. So for example in flutter (note: you can not ask user to connect to wifi directly in dart code. so you have to write your logic in Kotlin or Java) you have to define Platform and configure channel to run kotlin or java code. (Writing custom platform-specific code
After that you can have something like this :
import io.flutter.embedding.android.FlutterActivity
import androidx.annotation.NonNull
import android.net.wifi.WifiNetworkSpecifier
import android.net.wifi.WifiConfiguration
import android.net.wifi.WifiManager
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import android.os.PatternMatcher
import android.net.NetworkRequest
import android.net.NetworkCapabilities
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.util.Log
class MainActivity: FlutterActivity() {
private val CHANNEL = "channelname/wifiworks"
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
call, result ->
if (call.method == "getTest") {
val temp = getTest(call.argument<String>("SSID").toString())
result.success(temp)
// Note: this method is invoked on the main thread.
// TODO
}
if(call.method == "dc") {
val temp = disconnect()
result.success(temp)
}
}
}
private fun disconnect(): Int {
if(android.os.Build.VERSION.SDK_INT >= 29)
{
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
connectivityManager.unregisterNetworkCallback(mNetworkCallback)
}
return 3434;
}
private val mNetworkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
//phone is connected to wifi network
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
connectivityManager.bindProcessToNetwork(network)
}
}
private fun getTest(ssid: String): Int {
if(android.os.Build.VERSION.SDK_INT >= 29)
{
val specifier = WifiNetworkSpecifier.Builder()
// .setSsidPattern(PatternMatcher("SSID", PatternMatcher.PATTERN_PREFIX))
.setSsid(ssid)
.setWpa2Passphrase("Your_Password")
.build()
val request = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setNetworkSpecifier(specifier)
.build()
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
connectivityManager.requestNetwork(request, mNetworkCallback)
// Release the request when done.
//
return 123
}
else {
var networkSSID = ssid;
var networkPass = "Your_Password";
var conf = WifiConfiguration()
conf.SSID = "\"" + networkSSID + "\""
conf.preSharedKey = "\""+ networkPass +"\""
var wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
var netid = wifiManager.addNetwork(conf)
wifiManager.disconnect()
wifiManager.enableNetwork(netid, true)
wifiManager.reconnect()
return ssid.length
}
}
}
Hope this is helpful ;)

java.lang.RuntimeException: org.json.JSONException: JSONObject["_source"] not found. #Kuzzle

I am trying to update the document using java sdk, when I run my code kuzzle status is connected, it update the fields but neither comes in onSuccess() nor onError(). when I again request to updateDocument(), kuzzle state change from connected to error. I debug the issue and found this exception.
java.lang.RuntimeException: org.json.JSONException: JSONObject["_source"] not found.
as I am not getting "_source" in my code
kuzzle.collection("collection","testindex").updateDocument(obj.getDocumentId(), jsonObj, new ResponseListener<Document>() {
#Override
public void onSuccess(Document response) {
System.out.println("success"+response);
}
#Override
public void onError(JSONObject error) {
System.out.println("error"+error);
}
});
A fix has recently been released for this probleme. You should try with version 3.0.10 of the SDK: https://bintray.com/kuzzle/maven/kuzzle-sdk-android/3.0.10

After building release in Flutter Web, kReleaseMode is still false

I build my deployment release via Docker.
command: ["flutter", "pub", "global", "run", "webdev", "serve", "--hostname", "0.0.0.0", "web:5001", "--release"]
I have defined part of the code which retrieve different value based on the environment. Either it's production or development.
import 'package:flutter_web/foundation.dart';
....
static getEnvironment() {
if (kReleaseMode) {
return "production";
} else {
return "development";
}
}
However, even after building a release build, after calling getValue method I receive development value.
What I do wrong here?

Angular2 e2e test case with protractor throwing error

I have created my app with angular2-webpack-starter and i have used socket.io with it. I have created one common service to create socket connection and listen its method. this service is used and initialized after user is logged in. When app is running and i execute test case for login, i am checking url with below code :
browser.getCurrentUrl().then((url) => {
expect(url).toEqual('/dashboard');
});
The issue is when socket is connected its throwing error 'Timed out waiting for Protractor to synchronize with the page after 15 seconds' and if socket is not connected same test case is running without any error.
I'm not sure if connecting to the socket is actually make things take longer or not but if the 15 seconds isn't enough time, you can change the
allScriptsTimeout:timeout_in_millis in your protractor configuration file
protractor timeouts
So the solution I have found is:
(This is copied from here for your convenience. All credit goes to https://github.com/cpa-level-it
https://github.com/angular/angular/issues/11853#issuecomment-277185526)
What I did to fix the problem was using ngZone everywhere I have an observable that relies on socket.io.
So let's say you have this method in your service that gives you an observable on a socket.io.
private socket: SocketIOClient.Socket;
public getSocketIOEvents(): Observable<SocketIOEvent> {
if (this.socket == null) {
this.socket = io.connect(this._socketPath);
}
return Observable.create((observer: any) => {
this.socket.on('eventA', (item: any) => observer.next(new SocketIOEvent(item)));
this.socket.on('eventB', (item: any) => observer.next(new SocketIOEvent(item)));
return () => this.socket.close();
});
}
Then you need to use the ngZone service to tell Angular to create the socket outside the Angular 2 zone and then execute the callback of the Observable inside the Angular 2 zone.
import { NgZone } from '#angular/core';
constructor(
private socketService: SocketIOService, ,
private ngZone: NgZone) { }
ngOnInit() {
// Subscribe to the Observable outside Angular zone...
this.ngZone.runOutsideAngular(() => {
this.socketService
.getSocketIOEvents()
.subscribe(event => {
// Come back into Angular zone when there is a callback from the Observable
this.ngZone.run(() => {
this.handleEvent(event);
});
});
});
}
This way protractor doesn't hang waiting on the socket.