I have a mobile application for Android with multiple pages and currently try to write simple integration tests for it...
The issue is that my app only uses internal Android back gestures, I have no back button or something like this.
Is it somehow possible to tell the FlutterDriver to go one page back? (Simulate a android internal back button?) Something like this:
driver.goPageBack()
Thank you so much. :)
Try this in your Activity code.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.YOUR_ACTIVITY);
assert getSupportActionBar() != null; //null check
getSupportActionBar().setDisplayHomeAsUpEnabled(true); //show back button
}
#Override
public boolean onSupportNavigateUp() {
finish();
return true;
}
In the Flutter repo there is a test that simulates the hardware back button by sending a message over the platform channel like this:
final ByteData message = const JSONMethodCodec().encodeMethodCall(const MethodCall('popRoute'));
await ServicesBinding.instance!.defaultBinaryMessenger.handlePlatformMessage('flutter/navigation', message, (_) { });
https://github.com/flutter/flutter/blob/6688e63f68ebba0919a5fe3c8f8432bd8a65f81b/packages/flutter/test/widgets/router_test.dart#L725-L726
The closest thing I could find is a package that is a back button interceptor that has a method BackButtonInterceptor.popRoute() for testing that emulates the back button press: https://pub.dev/packages/back_button_interceptor
Related
I decided to experiment with MAUI. I am approaching first an Android App, and using Shell for navigation.
My App has 2 ways of opening:
When it's opened by the user tapping on the icon
Through a deep link, triggered by another app.
The issue I'm having is that when the app is triggered through the Deep link, I need to navigate to a specific page. I am trying to do it on the OnNewIntent accessing the Current instance of Shell, but when doing GoToAsync("my_route") it gives an error when trying to navigate to the new page.
This is what I have on my MainActivity:
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
var action = intent.Action;
var data = intent.DataString;
if (!string.IsNullOrWhiteSpace(data) && data.Contains("/data/")) {
if(Shell.Current != null)
{
Shell.Current.GoToAsync("myroute)";
// Also tried:
// - Shell.Current.GoToAsync("myroute").Wait();
// - App.Current.Dispatcher.Dispatch(async () => await Shell.Current.GoToAsync("//myroute")); (suggested by #toolmakersteve )
}
}
}
And this is the error:
Java.Lang.IllegalArgumentException: 'No view found for id 0x1
(unknown) for fragment ShellItemRenderer{19d353d}
(6c8560ab-dd58-4cbf-9e8b-2b9e12315f45 id=0x1)'
I'm assuming this has something to do with the fact that what I'm doing is not possible, so I need to find the RIGHT way to navigate to a specific page from OnNewIntent on MAUI, using Shell navigation.
UPDATE: It's also important to note that when the Deep Link triggers the app to open, there are two different behaviours:
If the app was already running, it throws the above mentioned exception
If the app was not already running, it opens regularly on the main screen, with no errors, but I would expect it to navigate to the desired Page.
Thanks!
First, make sure that GoToAsync("myroute") works if you use it somewhere more typical, such as a button press.
Assuming that works, then perhaps the intent code isn't running in the Dispatcher context (previously known as MainThread). Try:
Dispatcher.Dispatch(() => {
Shell.Current.GoToAsync("myroute");
});
VERSION 2
Perhaps deep link logic runs BEFORE App's OnResume.
If so, this might work:
In App.xaml.cs:
public partial class App : Application
{
...
public static bool FromDeepLink;
protected override void OnResume()
{
base.OnResume();
if (FromDeepLink)
{
FromDeepLink = false;
MainPage = new MainPage();
Dispatcher.Dispatch(() =>
{
Shell.Current.GoToAsync("myroute");
});
}
}
}
Then in OnNewIntent:
if (!string.IsNullOrWhiteSpace(data) && data.Contains("/data/")) {
App.FromDeepLink = true;
}
Conceptually #ToolmakerSteve answer is correct, but the OnResume event of the Application class seems not to fire when the app is resumed by intent (seems to be a Maui bug), however Android's native OnResume works and fires correctly even when the app is resumed via intent, all you have to do is in the MainActivity class to override Android's native OnResume method:
protected override void OnResume()
{
base.OnResume();
var fromDeepLink = Preferences.Get("FromDeepLink", false);
if (fromDeepLink)
{
Preferences.Set("FromDeepLink", false);
Shell.Current.GoToAsync("myroute");
}
}
protected override void OnNewIntent(Intent intent)
{
base.OnNewIntent(intent);
var action = intent.Action;
var data = intent.DataString;
if (!string.IsNullOrWhiteSpace(data) && data.Contains("/data/"))
{
Preferences.Set("FromDeepLink", true);
}
}
How to release foreground to the previous application in Flutter (Dart) ? I mean, how to force to call the onPause or viewWillDisappear to let my app disappear and let the previous app come back to the foreground.
Is there a method thant I can call ?
Edit : I don't wan't to close my app, juste "minimize" it.
You are struggling with a mismatch between Flutter's architecture and Android's. In your previous question you needed a way to bring your flutter app to the foreground, to which the answer is a full-screen intent notification. The problem is that in native Android, you would probably have used the NEW_TASK flag to start a new task. As Flutter only has one activity, it's necessary to use USE_CURRENT instead.
With NEW_TASK, you would use Activity.finish() to close it, closing just the new activity. If you did that with Flutter, that would probably close the whole app (because of the use of USE_CURRENT).
It might be possible to have a native Android app (allowing you to have more direct control of the launch of activities) and to use Add2App to add the Flutter screen(s). If you get that to work, I'd like to know.
I finally got a solution ! I haven't found yet a solution for the IOS side : I'm working on it.
I used MethodChannel to ask to the native side to minimize itself. For Android use this.moveTaskToBack(true); ! If you got an Objectif-C alternative, it will be perfect !
Dart:
class _MyHomePageState extends State<MyHomePage> {
static const MethodChannel actionChannel = MethodChannel('method.channel');
Future<void> _minimize() async{
try{
await actionChannel.invokeMethod('minimize');
} on PlatformException catch(e) {
print('${e.message}');
}
}
}
Android:
public class MainActivity extends FlutterActivity {
private static final String ACTION_CHANNEL = "method.channel";
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Action-post-alert method
new MethodChannel(getFlutterView(), ACTION_CHANNEL).setMethodCallHandler(
new MethodCallHandler() {
#Override
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("minimize")) {
this.moveTaskToBack(true);
result.success(true);
} else {
result.notImplemented();
}
}
}
);
}
}
Is there any way to know when the 'back' button event on the 'Android phone' is pressed? I'd like to exit the game and add few functionality to it when this button is pressed in Xamarin.Forms.
I googled a bit regarding this, but I got articles regarding Xamarin.Android Back button but not for Xamarin.Forms.
As I am relatively new to Xamarin.Forms, please help me out
public override void OnBackPressed()
{
//base.OnBackPressed();
}
Same thing I want in Xamarin.Forms. Need some assistance, guys.
If you mean Xamarin.Forms by "Xamarin Cross Platform", there is a OnBackButtonPressed event that you can use. As seen on that documentation:
Event that is raised when the hardware back button is pressed. This
event is not raised on iOS.
Simply override this event on your NavigationPage and you're ready to go:
protected override bool OnBackButtonPressed()
{
// Do your magic here
return true;
}
Good luck!
In my xamarin forms app you need to find the NavigationStack of the current Page if you are using master page:
public bool DoBack
{
get
{
MasterDetailPage mainPage = App.Current.MainPage as MasterDetailPage;
if (mainPage != null)
{
bool doBack = mainPage.Detail.Navigation.NavigationStack.Count > 1 || mainPage.IsPresented;
//top level of page and the Master menu isn't showing
if (!doBack)
{
// don't exit the app only show the Master menu page
mainPage.IsPresented = true;
return false;
}
else
{
return true;
}
}
return true;
}
}
I'm writing a mobile application using cordova 3.6, this app just open an external url corresponding to the mobile version of my website
var ref = window.open('http://www.stackoverflow.com', '_self', 'location=no');
If a use _self as the target, the back button behavior is fine forme as it works correctly within browesed pages but the problem is that the last back in the history stack done go back on my index page and then open once again my url ! Also the events on the window doesn't work. How to exit?
var ref = window.open('http://www.stackoverflow.com', '_blank', 'location=no');
If a use _self as the target, the back button behavior is not the same. There is no back possible within browsed pages, just a back on the index page whatever we are. How can I modify the behavior to have the same as the _self?
I am stuck with these 2 solutions :(
Note: I saw this similar question but the suggested code
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
closeDialog();
}
});
no more exists in cordova InAppBrowser.java no more exist
I found a working solution for the "_blank" option target using cordova 3.6 thanks to the answer of Kris Erikson on this post
With those modifications the hardware back button works within pages in an InAppBrowser.
I copy hereafter his working solution
Go to src/com/org/apache/corodova/inappbrowser directory and edit the InAppBrowserDialog.java:
Change
public void onBackPressed () {
if (this.inAppBrowser == null) {
this.dismiss();
} else {
// better to go through the in inAppBrowser
// because it does a clean up
this.inAppBrowser.closeDialog();
}
}
to
public void onBackPressed () {
if (this.inAppBrowser == null) {
this.dismiss();
} else {
if (this.inAppBrowser.canGoBack()) {
this.inAppBrowser.goBack();
} else {
this.inAppBrowser.closeDialog();
}
}
}
Then go to InAppBrowser and find the goBack function, change:
/**
* Checks to see if it is possible to go back one page in history, then does so.
*/
private void goBack() {
if (this.inAppWebView.canGoBack()) {
this.inAppWebView.goBack();
}
}
to
/**
* Checks to see if it is possible to go back one page in history, then does so.
*/
public void goBack() {
if (this.inAppWebView.canGoBack()) {
this.inAppWebView.goBack();
}
}
public boolean canGoBack() {
return this.inAppWebView.canGoBack();
}
Please post if you find a better solution avoiding to modify the java code
I have people complaining my application gets FC when they launch it (meanwhile others never had a single problem). Here is my full activity source. Since it happens on devices I don't own I can not fix it. From what they tell me it doesn't work on: Motorola Blackflip, Motorola Dext, Motorola CLIQ XT. Guess Motorola doesn't like my app after all...
Could it be that I allow a minSdkVersion="3"? I tested 1.5 on the emulator and worked fine...
Thank you in advance for your responses.
public class workit extends Activity implements OnClickListener {
Button yay;
Button yay0;
Button yay1;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.main);
yay = (Button) findViewById(R.id.gostart);
yay.setOnClickListener(this);
yay0 = (Button) findViewById(R.id.dontstart);
yay0.setOnClickListener(this);
yay1 = (Button) findViewById(R.id.exit);
yay1.setVisibility(ImageView.GONE);
ImageView inizio = (ImageView)findViewById(R.id.start);
inizio.setVisibility(ImageView.VISIBLE);
inizio.setBackgroundResource(R.drawable.start);
}
public void onClick(View v) {
// TODO Auto-generated method stub
if (v == yay0) {
finish();
}
if (v == yay) {
ImageView inizio = (ImageView)findViewById(R.id.start);
inizio.setVisibility(ImageView.GONE);
WebView work = new WebView(this);
setContentView(work);
work.loadUrl("file:///android_asset/index1.html");
work.setWebViewClient( new work());
work.setBackgroundColor(0);
work.getSettings().setBuiltInZoomControls(true);
work.getSettings().setDefaultZoom(ZoomDensity.FAR);
}
if (v == yay1) {
finish();
}
}
private class work extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.contains("exit.html")) {
// TODO: do what you have to do
finish();
}
view.loadUrl(url);
return true;
}
}
}
Your best bet is to ask somebody to send you the LogCollector output (in my experience, users are very happy to provide you information to debug problems. There are some really cool people out there). That should give you a callstack, and information on what kind of exception you triggered (NullPointerException, etc).
Next up - what are you building your app against? There should be an "Android x.x" entry in your project structure somewhere. If you're building something that is supposed to run on Android 1.5, then make sure you actually build against 1.5. You CAN build against 2.0 if you want, but if you need to use 2.0-specific functions, you'll have to encapsulate them properly. (This has been explained in detail on stackoverflow several times.)
On an unrelated note - I recommend more informative variable names. "yay0" doesn't mean anything to anyone who hasn't been working intimately with the code for a while.