My app have a background service (Android) which look periodicly for new data. If there are new data a notification is started. By tapping the notification a little native code piece is running and this will bring back to front the Nativescript app.
Snippet from the Kotlin class to bring back the Nativescript app:
val launchIntent: Intent? = packageManager.getLaunchIntentForPackage(applicationContext.packageName)
launchIntent?.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or Intent.FLAG_ACTIVITY_SINGLE_TOP)
launchIntent?.setAction(ACTION_NOTIFICATION_AVAILABLE)
startActivity(launchIntent)
In the AppComponent class my listener for a AndroidApplication.activityResumedEvent will listen:
export class AppComponent implements OnInit {
constructor(
private router: Router,
private ngZone: NgZone,
) {}
ngOnInit(): void {
android.on(AndroidApplication.activityResumedEvent, (args) => this.ngZone.run(() => {
let intent = args.activity.getIntent();
if (intent.getAction() === com.sample.app.NotificationTapReceiver.ACTION_NOTIFICATION_AVAILABLE) {
this.router.navigate(['/notificationList']);
}
}));
}
}
Most of the times this code works as aspected. But in my opinion we have three diffrent situations:
App is in foreground and running when a notification is incomming and the use tap on it. => all works fine
The app is not running only the background service. If the user tap the notification the app will fully start and move to the notification list. => all works fine
Problem: The App is running but not in the foreground. By tapping the notification the app resume (without restart) to the front and navigate to the notification list BUT the user interface are freezed. By tapping the elements on the screen, the inside code are running (consoloe.log(...)) but no action on the UI. Maybee the UI are not freezed only the routing/navigation don't work.
I find the question Nativescript - How to bring app to foreground when notification is pressed but in my case there are no difference between android emulator and (not connected) real android device.
Tests with the flags to start the activity will change the behavior but not in the right way
# FLAG_ACTIVITY_SINGLE_TOP might be the best solution but the described error above is very annoying
launchIntent?.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or Intent.FLAG_ACTIVITY_SINGLE_TOP)
# even the app is in foreground after a tap on the notification the app is fully restarts
launchIntent?.addFlags(FLAG_ACTIVITY_NEW_TASK)
Have anyone an idea where the problem is or which flags i have to set to prevent the current state and the fully working UI (with router/navigation)? Or do i have to modify the nativescript/angular code the better handle the resume event?
Not the best solution but when i was able to trim the flags to get the in the foreground running app still running and in the other two states (app dead or app in the background) a fully restart is initiated, at the moment this will be fine too.
Update: It look like, that the UI not freezed. I added a RadListView and i can scroll to the list, still the routing/navigate don't work. Even i use the this.routerExtensions.back() nor i use the this.router.navigate(['notificationDetail']).
Related
I'm triggering android activity FullScreenActivity whenever video notification comes into the app. This screen display on top of react-native. But I don't want to trigger this FullScreenActivity when user is on a specific screen of react-native.
we have same feature coded on RN and android. I want to achieve
when app is foreground and not in RN specific screen, FullScreenActivity should trigger.
when app is foreground and in RN specific screen, FullScreenActivity should not trigger and call RN scene instead.
when app is background or killed, FullScreenActivity should trigger.
Scenario:
user is in screen 1 -> got notification -> now trigger FullScreenActivity.
How to handle if user moves to screen 2 (doesn't trigger if in screen 2) while the above step in progress?
How android know user in the specific screen of RN and how RN should know FullScreenActivity is currently active? I want to make one / two-way communication between both platforms.
You should make two-way communication between platforms. I assume you know how to create and register your native modules in Android.
React-Native -> Android communication.
Native code:
#ReactMethod
public void setCurrentScreen(String screen) {
}
In JS code, you can attach navigation listener to your Router via setting onStateChange prop and calling YourNativeModule.setCurrentScreen() method.
Android -> React-Native communication:
Just send an event from FullScreenActivity to JS with activity state information and subscribe to events in JS.
override fun onStart() {
super.onStart()
sendActivityStartedEvent()
}
override fun onStop() {
sendActivityStoppedEvent()
super.onStop()
}
private fun sendActivityStartedEvent() {
val params = Arguments.createMap()
params.putBoolean("active", true)
sendEvent("activityState", params)
}
private fun sendActivityStoppedEvent() {
val params = Arguments.createMap()
params.putBoolean("active", false)
sendEvent("activityState", params)
}
private fun sendEvent(eventName: String, params: WritableMap?) {
reactNativeHost.reactInstanceManager.currentReactContext
?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
?.emit(eventName, params)
}
In order to access reactNativeHost your FullScreenActivity must be instance of ReactActivity
JS code:
this.subscription = DeviceEventEmitter.addListener('activityState', event => {
console.log(event.active)
});
You need to create a native module with two-way binding with:
A function that RN calls on android, invoked every time the user change a react native screen
When you receive a video notification, you check the current screen and decide if the FullScreenActivity should be shown or not
An event emitted by the native module every time the FullScreenActivity shows/hides, and react-native can subscribe to it
when app is background or killed, FullScreenActivity should trigger.
I don't understand the question, You can't present/hide/do stuff when your app is killed
If I understand correctly, you can use Actions.
import {Actions} from 'react-native-router-flux';
Actions.currentScene;
Actions.currentScene gives you the active scene.
I'm creating a notification reminder application in react native, I tried opening a specific screen in the application when user taps on the notification, I'm using react-navigation and tried deep linking with the screen paths. so my problem is, the deep-linked screen is opening fine when the app is foreground already, but if I open the notification when the app is closed(even not in recent), it goes to the deep-linked screen and immediately going back to the previous screen.
I'm doing some async operations in the useEffect. not sure if this is the issue, help can be greatly appreciated.
useEffect(() => {
const paramObject = navigation.getParam('someObject', {});
if (Object.keys(paramObject).length === 0) {
const resourceID = navigation.getParam('resource_id', '');
const target = navigation.getParam('target', '');
axios.get().then(() => {
// some state setting
}).catch(); #Some api call
}
}, []);
Note: Problem occurs when the deeplink page opens when the app is closed, I'm also getting some warnings with the problem like Can't Perform react state update on a unmounted component ...
I think your problem is the app navigate to initial route.
Forced the deep linking to wait 1000ms before doing anything, so that sure that the app finished loading properly before anything.
Another approach would be to modify your dynamic initialRoute in order to recognize that the app has been opened by deep linking and to not do anything in that scenario.
I am writing a test that requires launching application directly from launcher. Because I can't emulate it correctly by launching through intent.
The problem is that when I am running the test on a fresh emulator (I am using Travis CI, but it can be easily reproduced on my home PC) the emulator starts with the "first run" greeter overlay. Which blocks my uiautomator code from correctly launching the application.
I have tried to add some code to close that greeter but unfortunately it can appear with some delay, when my "greeter detecting and closing" code has already stopped working thinking that the coast is clear.
Is there any guaranteed way to disable that greeter? Some preference maybe or just an example of code that will reliably kill the greeter.
Have you tried using PackageManager.getLaunchIntentForPackage(..)? This will allow you to send the same Intent that the launcher uses to start your app. It should be equivalent to clicking on your application's launcher icon.
If you do need to go through the launcher, you can use a UiWatcher to dismiss the first-run overlay. Whenever UiAutomator can't find an element, it will call the checkForCondition(..) method for each registered UiWatcher and give you a chance to dismiss any overlays or dialogs that are getting in the way.
Apparently the greeter is called "cling". Searching though (rather old) code I found the following:
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android-apps/4.0.2_r1/com/android/launcher2/Launcher.java#Launcher.isClingsEnabled%28%29
private boolean isClingsEnabled() {
// TEMPORARY: DISABLE CLINGS ON LARGE UI
if (LauncherApplication.isScreenLarge()) return false;
// disable clings when running in a test harness
if(ActivityManager.isRunningInTestHarness()) return false;
return true;
}
And next stop is isRunningInTestHarness() at http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.2_r1/android/app/ActivityManager.java#ActivityManager.isRunningInTestHarness%28%29
public static boolean isRunningInTestHarness() {
return SystemProperties.getBoolean("ro.test_harness", false);
}
Which in turn leads to adb shell setprop ro.test_harness true. Which just works.
I have a situation which I have been working on for close to a week now but can not come up with a working solution.
I have an app, which will launch a terminal window and run a command, the terminal used is Jack Palevich's Android-Terminal-Emulator (source code here) and the code used to launch a window is:
public boolean runCommand(String command) {
Intent intent = new Intent("jackpal.androidterm.RUN_SCRIPT");
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.putExtra("jackpal.androidterm.iInitialCommand", command);
if (mHandle != null) {
// Identify the targeted window by its handle
intent.putExtra("jackpal.androidterm.window_handle", mHandle);
}
startActivityForResult(intent, REQUEST_WINDOW_HANDLE);
return true;
}
Now this works fine, however the issue is that there are a number of times in the apps life that it will need to pass a command to this terminal window, and no user will be interacting with the device (i.e it will need to be automated).
When you launch a terminal window that window becomes the activity in view, therefore while my app still runs in the background any future calls to runCommand() will not happen until my app is brought back to view.
So I need to find a way to have my app become the app in view again once it has called runCommand() and started the terminal. I have tried a number of routes but with no really success, I guess the only really way forward will be to make changes to the terminal app itself, which I am happy to do (I have the source download and tested) but am just a little stuck as to where to look and what to change.
If you need any more info about my app or anything else, let me know!
Thank you
I am looking for a way to launch another app from within my app but so that the focus is not changed from my app to the app launched.
I.e currently I have the new app launched via a intent, however when this is carried out the new app is launched and becomes the app in view, I need it to be kept in the background with my app still in view.
The reason for this?
I am developing an application for internal use that will act like a lock-screen to the device so although things must happen in the background the 'lock-screen' must always be on top.
I have done some research into intents and launching other apps but can not find anything about what I need.
Hope you can help thank you!
Currently the terminal is called like this:
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setComponent(new ComponentName("jackpal.androidterm", "jackpal.androidterm.RemoteInterface"));
intent.setAction("jackpal.androidterm.RUN_SCRIPT");
intent.putExtra("jackpal.androidterm.iInitialCommand", cmdString);
The reason it needs to be running in the background is so that the app can run commands in terminal without the user having access, but then they 'unlock' the screen they need to then be able to view the terminal and what commands are being run etc
You can not startActivity in Background.Instead start the activity and minimise the activity(in your case this activity is of different application) using moveTaskToBack(true);
In your case, put a condition based on your intent and its params and use moveTaskToBack(true); so that activity will be minimised only when your application launches.
This won't be possible, you will have to start a background Service that does the actual work and only launch the Activity you want to navigate to once your foreground Activity is finished. Depending on your architecture, you can store the Activity to call when your foreground Activity is finished and change it from the service. That way you will have your desire behaviour without having to actually call the Activity.
In addition to the answer from #Meher, in the intent for your current starting activity, you can add the flag FLAG_FROM_BACKGROUND.
With this you get rid of the "blinking" effect (the one where your activity shows for one fraction of second while it discovers wether to go to background)