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());
}
Related
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.
I'm building an application for web/Android with Vue+Capacitor. What's the correct way to manipulate the Android part?
For example, I'd like the screen to not turn off through idle. Apparently, the way to do this is to put
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
in the MainActivity. But since the Android part gets built again all the time it doesn't really make sense to edit it directly (or maybe I'm mistaken here somehow?)
So how do I achieve something like this?
Edit for clarification:
This is the MainActivity:
import com.getcapacitor.BridgeActivity;
public class MainActivity extends BridgeActivity {}
As you can see it does absolutely nothing besides extending BridgeActivity
This is the (generated!) onCreate of BridgeActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bridgeBuilder.setInstanceState(savedInstanceState);
getApplication().setTheme(getResources().getIdentifier("AppTheme_NoActionBar", "style", getPackageName()));
setTheme(getResources().getIdentifier("AppTheme_NoActionBar", "style", getPackageName()));
setTheme(R.style.AppTheme_NoActionBar);
setContentView(R.layout.bridge_layout_main);
/* The initally mentioned line here works but gets overwritten*/
}
You can just use this community plugin: https://github.com/capacitor-community/keep-awake.
This way you don't have to change the native code.
import { KeepAwake } from '#capacitor-community/keep-awake';
const keepAwake = async () => {
await KeepAwake.keepAwake();
};
const allowSleep = async () => {
await KeepAwake.allowSleep();
};
Once you run npx cap add android, the native project gets created and it’s never modified by capacitor, so you can make any changes you want in the MainActivity.java file or any other files (except the ones that start with “automatically generated, do not edit” or similar messaging).
For tests I use Espresso and Barista
I have a test in which I need to open another screen by pressing a button. How can I check if this screen opens? Did the screen I need open?
Can I somehow check the chain of screens? To understand that the screens open in the order I need?
If someone throws links to good tutorials on UI tests in Android, I will be very grateful.
An easy solution would be to just check for an element of the new screen to be shown like this:
onView(withId(R.id.id_of_element_in_your_new_screen)).check(matches(isDisplayed()))
If you really want to check out for the current activity that is shown, you could try something like this:
Gather the current activity via InstrumentationRegistry and check for the activity in stage RESUMED.
fun getTopActivity(): Activity? {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
val resumedActivities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED)
if (resumedActivities.iterator().hasNext()) {
resumedActivities.iterator().next()?.let {
activity = it
}
}
}
return activity
}
You could then check this in a test like this:
#Test
fun checkForActivity() {
val currentActivity = getTopActivity()
assertTrue(currentActivity?.javaClass == YourActivityToCheckAgainst::class.java)
}
I personally use intended(hasComponent(YourActivityToCheckAgainst::class.java.name)), which checks if the last intent was done with a desired activity, set as its component.
I also wrote an extensive Android UI testing tutorial using Espresso + Barista libraries.
Is there any library which has ability like moveTaskToBack in React Native?
Previously I use https://github.com/jaysoo/react-native-activity-android and it has moveTaskToBack. But unfortunately this repo is not active anymore, and since React Native 0.29, it has some internal change which make that library didn't work.
I'm not sure it's what you need, but if you put this in your MainActivity.java class:
#Override
public void invokeDefaultOnBackPressed() {
// do not call super.invokeDefaultOnBackPressed() as it will close the app. Instead lets just put it in the background.
moveTaskToBack(true);
}
Then when user press the android back button on the "root" page, the app will go in background instead of being closed.
Source: background a react-native android app using back button
Use react-native-navigation, then navigate to your projects node modules folder and locate the react-native-navigation folder and inside it, follow the path to the NavigationActivity.java:
/android/app/src/main/java/com/reactnativenavigation/controllers/NavigationActivity.java.
In the NavigationActivity.java file, you will see the following method at around line 196:
#Override
public void invokeDefaultOnBackPressed() {
if (layout != null && !layout.onBackPressed()) {
super.onBackPressed();
}
}
Comment out the line: super.onBackPressed() and add the line: this.moveTaskToBack(true);
and thats all! Happy coding
I have created a Hybrid App using Xamarin.Forms. It uses multiple WebViews (XLabs HybridWebViews). I use the same app on iOS and it works fine, while on Android it gradually slows down. When I do a chrome://inspect on the device it shows multiple WebViews for the same page, seems more like the WebViews which were hidden are still in the memory and are slowing down the application.
How can I destroy the hidden WebView instances on Android?
Following is a screenshot of the chrome://inspect on the App:
Generally you should:
set your WebViews instances to null.
(optionaly) force garbage collection with GC.Collect();
If it's not enough make a custom renderer of your WebView and add this:
protected override void Dispose(bool disposing)
{
if (disposing && Element != null)
{
if (Control != null)
{
Control.StopLoading();
Control.ClearHistory();
Control.ClearCache(false);
Control.LoadUrl("about:blank");
Control.FreeMemory();
Control.PauseTimers();
}
}
base.Dispose(disposing);
}
It should solve your issues.