I am working on SDK for android and later for ios. And this SDK should be added to the native android project as AAR and probably for ios as a pod.
I was following RN article integration with existing apps
and it is working completely fine but my problem is I don't want to run the whole activity up to the native project... I just need to open modal dialog with RN components and some logic
The third part of application write in Kotlin on android implementing my SDK
class MainActivity : AppCompatActivity() {
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val MySDK = MySdk(this, this, 'some other data')
...
btnOpenWalleePaymentModalReactNative.setOnClickListener {
MySDK.launchDialog()
}
and now my SDK opening RN activity (inside MySDK):
class MySDK(private val context: Context, val activity: Activity) {
fun launchDialog() {
val intent = Intent(context, MyReactActivity::class.java)
context.startActivity(intent)
}
and this part open standard activity where all RN exist but i don't want to have an activity i want to have dialogue
so I created a class with dialogue what I want to use for that (BottomSheetDialogFragment)
class TestDialog(context: Context, var activity: Activity): BottomSheetDialogFragment() {
private lateinit var reactRootView: ReactRootView
private lateinit var reactInstanceManager: ReactInstanceManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
SoLoader.init(context, false)
reactRootView = ReactRootView(context)
val packages: List<ReactPackage> = PackageList(activity.application).packages
reactInstanceManager = ReactInstanceManager.builder()
.setApplication(activity.application)
.setCurrentActivity(activity)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackages(packages)
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build()
reactRootView?.startReactApplication(reactInstanceManager, "PaymentSDK", null)
activity.setContentView(reactRootView)
}
and when I init this class and open dialogue with react native it will change the activity from third party library and put there my react activity and blurred overlay from dialogue...
TestDialog(context, activity ).show(activity.supportFragmentManager, "tag")
like on the picture
any idea how to squeeze RN view/UI into fragment or dialogue view or any other idea how to do it? Or an article with something similar like writing an RN module for native android?
I've developed a RN native module as a bridge for a native SDK once. I've followed this old tutorial page https://archive.reactnative.dev/docs/native-modules-android.
From what I've understood from your situation, you will have a RN application trying to call a showDialog function from this native module, passing the RN activity to make sure it is opened from it, right? The snippets that you've posted confused me a little bit but I'll try to help with what I remember.
You'll have to have a JS/RN project for this native module, you can follow this https://reactnative.dev/docs/native-modules-setup and the following articles from the official docs to create it.
First, in the Android part of this created project you will need to have the .aar downloaded through gradle or locally in the project.
The Android part will have two important files: MySdkPackage and MySdkModule
MySdkPackage will just list your module for React Native to see it, something like:
package com.reactnativemysdk
import java.util.Arrays
import java.util.Collections
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager
import com.facebook.react.bridge.JavaScriptModule
class MySdkPackage : ReactPackage {
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
return Arrays.asList<NativeModule>(MySdkModule(reactContext))
}
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList<ViewManager<*, *>>()
}
}
and MySdkModule will indeed make the connection with your Android native .aar implementation, having a reference from the React Native context and activity. Something like:
package com.reactnativemysdk
class MySdkModule(private var reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
#ReactMethod
fun showMyDialog(promise: Promise) {
if (reactContext.hasCurrentActivity()) {
reactContext.currentActivity?.runOnUiThread {
MySdk.getInstance().showDialog(reactContext.currentActivity) //this activity and context are the references that
// you need to properly show the dialog as it was native
}
} else {
promise.reject(ERROR_STRING, "No activity found.")
}
}
}
this method annotated with #ReactMethod will need a JS counterpart, where things will be tied together and you will be able to call this JS 'bridge' code from your RN app JS code
the JS bridge will be something like this (the example I did was in TypeScript so I'm sorry):
import { Platform, NativeModules } from 'react-native';
type MySdkInterface = {
showMyDialog(): Promise<void>;
const { MySdk } = NativeModules;
export default MySdk as MySdkInterface;
Creating this native module and organizing things like this you will have a better integration between and RN and the native parts and you will be able to properly show the dialog and any rendering/layout issues would be solvable in your original Android SDK code
So you will have something like
RN App JS showDialog <-> RN Native Module JS showDialog <-> RN Native Module Android (or iOS) native code showDialog <-> MySdk (the .aar one) showDialog implementation
I'm pretty sure these links and references will be able to help you as well but I hope I was able to at least clarify the way for you
I'd recommend following the first link, the https://archive.reactnative.dev/docs/native-modules-android, and following and understanding this ToastModule structure, I think the most recent one doesn't help that much.
Related
I am trying to create a wrapper plugin of android plugin for flutter usage.so i have created a flutter plugin and i am using platform view to show the native ui in android side and using AndroidView widget in flutter side. I have properly followed the flutter docs for the implementation.Noraml views like Textview and webview are working fine when returned from native side.
The UI from plugin is supposed to return camera stream inside yellow circle as shown in attached image but the stream alignment is not proper.
The same Plugin and its UI is working fine in any other android project.
class FlutterWebView internal constructor(
context: Context,
messenger: BinaryMessenger,
id: Int,
activity: Activity
) :
PlatformView,KLivenessCallbacks, MethodCallHandler, AppCompatActivity() {
val activity=activity
val context=context
private val kLivenessView: KLivenessView
private var methodChannel: MethodChannel
override fun getView(): View {
return kLivenessView//this is the object of UI class provided by plugin
}
init {
kLivenessView = KLivenessView(context)
}
}
This is the code snippet from UI class extending PlatformView in native android.
I have a project using MAUI Hybrid to produce an Android Application (apk).
A blazorWebView is running in MAUI app, so that the code of pages is used by both Web Site (un Blazor Wasm) and MAUI app (MAUI + Blazor WebView).
A blazorWebView is included in MAUI App (code from MainPage.xaml) :
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:b="clr-namespace:Microsoft.AspNetCore.Components.WebView.Maui;assembly=Microsoft.AspNetCore.Components.WebView.Maui"
xmlns:local="clr-namespace:MyMauiBlazor"
x:Class="MyMauiBlazor.MainPage">
<b:BlazorWebView HostPage="wwwroot/index.html">
<b:BlazorWebView.RootComponents>
<b:RootComponent Selector="app" ComponentType="{x:Type local:Main}" />
</b:BlazorWebView.RootComponents>
</b:BlazorWebView>
</ContentPage>
On Android devices, is it possible to handle the Back Button pressed ?
Currently, the App closes, but I want to handle this event (ex : navigate to previous page).
Is there a way to do this ?
I had the same problem as you, officially microsoft doesn't support back key navigation for MAUI blazor yet, but I found a temporary solution for that:
First you have to create a javascript file, and put it in the wwwroot folder, and include it in the index.html file. Inside the js file put this code:
window.goBack = () => {
history.go(-1);
}
Then you have to create a c# class and make it a service, so for example I created a class called GoBack.cs. To set it as a service just put this code in MauiProgram.cs:
builder.Services.AddTransient<GoBack>();
In the GoBack class you have to create a static variable and method, because otherwise you will not be able to use the IJSRuntime interop. My GoBack file is the following:
public class GoBack
{
private static IJSRuntime JSRuntime { get; set; }
public GoBack(IJSRuntime jSRuntime)
{
GoBack.JSRuntime = jSRuntime;
}
public static async Task GoBackInTime()
{
//Microsoft.Maui.Platform;
if (GoBack.JSRuntime != null)
{
await GoBack.JSRuntime.InvokeVoidAsync("goBack");
}
}
}
The method GoBackInTime is going to call the js method goBack through the jsInteroperability. Now you have to inject in the first blazor page that you load this service.
#inject GoBack goBack
This way the jsRuntime in the cs file will be initialized and not null.
Now all you have to do is override the OnBackButtonPressed method in MainPage.xaml.cs:
protected override bool OnBackButtonPressed()
{
GoBack.GoBackInTime();
return true;
}
This way, when the back button is pressed, it won't exit the application (because we are returning true) and at the same time the js function is called so the blazor page will navigate to the previous page.
There are an update in the end of the question. Now I've got to turn on and turn off the error with one line change in a tiny project with all code transcribed below.
The answer of Alexey Romanov is brilliant. Check in the end of the question. it's an amazing and unknown feature of Android Studio environment.
I've found a very kinky error in Kotlin using updated Android 3.4.2.
First I've run my test code in my computer (not Android) using main modules in any kotlin file in my only module. It always works for me, but it has started to give an error that I comment below and then another error that don't allow to run the main module anymore.
Searching in Stack Overflow, one user as claimed that the last described error ends when one uses test files under java folder (Not Android test files)
However is has continued to give the first error, which I commented above, which I managed to reduce to the following schematic example:
class Cl(
var a:Int=0
)
var vCl = arrayListOf<Cl>()
And the main module is:
fun main(){
println("start")
vCl.clear() // error points to this line
println("ok")
}
I just point to fun main() line in test file and click the green icon.
The error message
Exception in thread "main" java.lang.ExceptionInInitializerError
Suddenly the error stops when I've changed one global statement from one file to another file (in the top part, outside any class or function).
var timings = TimingLogger("MyTag", "Your")
It's crazy. I'm freaking out!
Update:
=======
I've made newerror, a new tiny project with one module to reproduce the error decribed in this question. Below is the complete code:
Gradle: No change after project creation.
AndroidManifest.xml: No change after project creation.
activity_main.xml: It is bare bone because all my views are dinamically created:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/myLayout"
android:textAllCaps="false"
android:layout_width="match_parent"
android:layout_height="match_parent" tools:context=
"br.com.greatsolutions.paulo.myerror.MainActivity">
</RelativeLayout>
The MainActivity.kt code:
package br.com.greatsolutions.paulo.myerror
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
The compiler.kt code
package br.com.greatsolutions.paulo.myerror
import android.util.TimingLogger
var timings = TimingLogger("MyTag", "Your")
class Cl(
var a:Int=0
)
var vCl = arrayListOf<Cl>()
Finally the test.kt code:
package br.com.greatsolutions.paulo.myerror
fun main(){
println("start")
vCl.clear()
println("ok")
}
The complete error message when I run test.kt code in my computer (not in Android):
start
Exception in thread "main" java.lang.ExceptionInInitializerError
at br.com.greatsolutions.paulo.myerror.TestKt.main(test.kt:5)
at br.com.greatsolutions.paulo.myerror.TestKt.main(test.kt)
Caused by: java.lang.RuntimeException: Stub!
at android.util.TimingLogger.<init>(TimingLogger.java:59)
at br.com.greatsolutions.paulo.myerror
.CompilerKt.<clinit>(compiler.kt:5)
... 2 more
And, again, if I put the below declaration inside MainActivity.kt and run the test.kt again in my computer ...
var timings = TimingLogger("MyTag", "Your")
... and the error is gone!
start
ok
For those who want to see to believe:
Conclusion: Now I know how to avoid this crazy error, but I don't understand why it works! Ih theory, the source file that I use to make my declarations should be interchangeable, because all files are in the same module!
Solution of puzzle
Alexey Romanov has hit the nail on the head!
I've researched a little bit more and I've found that Android Studio only executes the declarations in a file if any variable of the scope was used in a computer test.
When
var timings = TimingLogger("MyTag", "Your")
is inside MainActivity.kt, no error is shown.
After I put in this file the following code:
open class Fool(val a:Int=5){
init { println("fool") }
}
class SuperFool(a:Int=8):Fool(a) {
init { println("what a big fool") }
}
var v = SuperFool()
When I put println(v.a) my code in test.kt becomes:
package br.com.greatsolutions.paulo.myerror
fun main(){
println("start")
println(v.a) // new line
vCl.clear()
println("ok")
}
And it gives the same error than before! Take out this line and no error again!
The solution is if one has any declaration of some variable in an Android class in your projet you MUST use lateinit and just initializate in other point of code that will not run in test execution.
In this case one can do
lateinit var timings:TimingLogger
And put the initialization in other place (inside onCreate in MainActivity class, for instance. In my case, immediately before the 1st. call addSplit, one of the methods from TimingLogger class
timings = TimingLogger("MyTag", "Your")
Now my test code gently prints
start
fool
what a big fool
ok
The problem has nothing to do with Kotlin; you can't just use Android classes in code running directly on your computer and not in an emulator, because the versions in the standard jar will crash on first use. They are just there to provide class files with the same names and method signatures as will exist on the device, so that your code can compile (not run!).
Use Robolectric to get a usable-for-JVM-tests version of the Android library.
if I put the below declaration inside MainActivity.kt and run the test.kt again in my computer
Then your test doesn't use any Android classes (see below for details).
It is worth remembering that, within the same module, all declarations, regardless of what file they are in, are executed.
That's wrong. What happens with top-level val/var/fun declarations in Kotlin is that they get wrapped into a single class for each file, so your code works like this:
// compiler.kt
package br.com.greatsolutions.paulo.myerror
object CompilerKt { // you can actually see the name in the stack trace
var timings = TimingLogger("MyTag", "Your")
val vCl = arrayListOf<Cl>()
}
class Cl(
var a:Int=0
)
// test.kt
package br.com.greatsolutions.paulo.myerror
object TestKt {
fun main(){
println("start")
CompilerKt.vCl.clear()
println("ok")
}
}
So calling TestKt.main() forces loading and initialization of CompilerKt because you reference vCl there. This includes calling the constructor TimingLogger("MyTag", "Your") which thrown an exception when the stub library is used.
Calling TestKt.main() does not load MainActivity (which you can confirm by adding some print to that file), so if you move var timings there, nothing in the stub library gets called.
Your missing a an quote on your first line.
I need a way to make a button or a container that can still be visible even when I close the app like so: . But this is just simply impossible in flutter. So what I've been doing is making a platform channel on my app and trying to make the container and button with native components. But I've been using kotlin and do not know much about this programming language. Is there a way for my code to be able to make such widgets?(I would thank you so much if you could edit my full code.)
Full Code:
Flutter:
class FloatingContainer extends StatelessWidget {
static const platform = const MethodChannel('flutter.App.com.channel');
#override
Widget build(BuildContext context) {
return Container ();
}
Future<Null> _showNativeView() async {}
}
Kotlin:
package com.example.swipe
import android.os.Bundle
import io.flutter.app.FlutterActivity
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.plugin.common.MethodChannel
class MainActivity() : FlutterActivity() {
private val CHANNEL = "flutter.App.com.channel"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
MethodChannel(flutterView, CHANNEL).setMethodCallHandler { call, result ->
}
}
}
This is known as a Draw Over Other apps.
This feature is not available in the iOS probably that's the reason flutter doesn't has this feature or official package for it.
But to achieve this feature in your application you can write up some PlatformChannels.
This permission allow you to draw on top of everything.
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
Allows an application to open windows using the type
TYPE_SYSTEM_ALERT, shown on top of all other applications.
Very few applications should use this permission; these windows are intended
for system-level interaction with the user.
Constant Value: "android.permission.SYSTEM_ALERT_WINDOW"
This code can also help you out to achieve this.
You can also check out springy-heads library by Flipkart-Incubator too.
Hello in this case you are searching for PlatformChannels You can review the documentation here: https://flutter.dev/docs/development/platform-integration/platform-channels or if you want i have a little post about this on my website: http://fjbatresv.com/flutter-tambien-habla-nativo/ (Select your language on the top of the post.)
Apart from restriction that it won't work on IOS, there is a plugin in flutter called System Alert Window, which does the same
It shows Truecaller like overlay window, over all other apps along with callback events
I'm working on an Xamarin Android app using MVVMCross and have (only for android) multiple screens/activities I want to start from.
I tried to duplicate the SplashScreen, but then none of the Activities boot anymore.
Any suggestions how to get multiple Activities with MainLauncher=true workable?
You should add a AppStart.cs to your core project and add this function:
public async void Start(object hint = null)
{
if (CheckSomething == true)
ShowViewModel<ViewModels.FirstViewModel>();
else
ShowViewModel<ViewModels.SecondViewModel>();
}
Then in your App.cs do:
public override void Initialize()
{
RegisterAppStart(new AppStart());
}