MotionEvent's ACTION_UP could not work after ACTION_DOWN evet - kotlin

I tried to users while to pressed button, run handler, then release button, finish it. But MotionEvent.ACTION_UP could not work like this below code, then my runnable infinite loop continue. How can I triggered ?
myButton.setOnTouchListener{view, event->
when(event.action){
MotionEvent.ACTION_DOWN -> {myhandler.post{myrunnableaction} true}
MotionEvent.ACTION_UP -> { myhandler.removeCallbacks(myrunnableaction) true}
}
view.onTouchEvent(event) ?: true
}
``

removeCallbacks method removes you Runnable from message queue, when your Runnable already launched you can't remove that with removeCallbacks method because already launched.

Related

Kotlin, How do I dismiss an App by pressing the Back button Activity which is not the launcher activity in android?

I have a login activity with navigation fragments , one is the splash screen and then navigate to Login Fragment this is the Launcher Activity and it checks if the user is already logged in and then start de Initial Activity if is already logged in.
In the Initial Activity i have a navigation fragments but when the user press the back button the Activity Launch start instead of close the activity. I want the user to close the app like the same behavior that occurs when back button is pressed on the launcher activity. How do i achieve this?
This is the splash fragment in the Launcher Activity:
fun initListeners() {
val DURATION = 2500
user_app.toString()
val handler = Handler()
handler.postDelayed({
if (user_app.isEmpty() && pwd_app.isEmpty()) {
navigationToLogin()
} else {
Toast.makeText(safeActivity, "Sesión iniciada ${OPERATOR_APP.getPreferenceToString().toString()}", Toast.LENGTH_SHORT).show()
startActivity(Intent(safeActivity,XTInitActivity::class.java))
}
}, DURATION.toLong())
}
fun navigationToLogin() {
val navigate = SplashFragmentDirections.actionSplashFragmentToXTLoginFragment()
findNavController().navigate(navigate)
}
}
I try when the methon onBackPressed whit its callbacks but its now deprecated for most recent API
I think you should make a separate activity for splash and keep the splash activity as the launcher instead of Login activity and and then if the user is not signed in then only go to Loginactivity else start the MainActivity (XTInitActivity in your case)
However if you need to make LoginActivity as the launcher activity then use the following:
override fun onBackPressed(){
super.onBackPressed()
exitProcess(0)
}
override onBackPressed in Initial Activity
override fun onBackPressed() {
super.onBackPressed()
exitProcess(0)
}

How to retrigger focusRequester.requestFocus() when coming back to a composable?

I have a MyBasicTextField in a composable to request user input:
#Composable
fun MyBasicTextField() {
val keyboardController = LocalSoftwareKeyboardController.current
val focusRequester = remember{ FocusRequester() }
BasicTextField(
modifier = Modifier
.focusRequester(focusRequester),
keyboardActions = keyboardActions ?: KeyboardActions(onAny = { keyboardController?.hide() }),
)
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
}
The keyboard automatically slides in when showing this composable, always.
But wherever MyBasicTextField is used:
I tap on a LinkifiedText to leave and open a browser to show link
I tap BACK
and come back to previous MyBasicTextField screen, the keyboard is not shown
also the focusRequester.requestFocus() is not triggered again when coming back
How can I solve my issue?
Create a top-level variable in your activity, then modify it from within the onStart overridden method. Use that variable as the key for LaunchedEffect in place of Unit. That variable basically keeps track of when the user enters the app.
var userIn by mutableStateOf (true)
In your Composable,
Launched effect(userIn){
if(userIn && isKeyboardShown){
...
}
}
Boring,
You can use my answer here as well, but instead of incrmenting the launchKey every time it's called, only increment the launchKey once user clicks on the browser link, that way it will not pop up during other re-compositions.

How to make widget test wait until Bloc has updated the state?

I have a EmailScreen (stateful widget) that has a text input, and a button. The button is only enabled when a valid email is input.
I'm using Bloc, and my screen has InitialEmailState and ValidEmailInputState, and it works fine when I run the app.
In my widget test, the second expectation is failing before bloc has a chance to update the state:
testWidgets('when valid email is input, button is enabled', (tester) async {
const validEmail = 'email#provider.com';
emailBloc.listen((event) {
print('NEW EVENT: ' + event.toString());
});
await bootUpWidget(tester, emailScreen);
final BottomButton button = tester.widget(
find.widgetWithText(BottomButton, 'CONTINUE'));
expect(button.enabled, isFalse);
await tester.enterText(find.byType(TextInputScreen), validEmail);
await tester.pumpAndSettle();
expect(button.enabled, isTrue);
});
And here's the output I'm getting:
NEW EVENT: InitialEmailState
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure object was thrown running a test:
Expected: true
Actual: <false>
...
The test description was:
when valid email is input, button is enabled
════════════════════════════════════════════════════════════════════════════════════════════════════
Test failed. See exception logs above.
The test description was: when valid email is input, button is enabled
NEW EVENT: InputValidEmailState
✖ when valid email is input, button is enabled
Exited (1)
As you can see, it prints the initial state, fails the second expectation, and then prints the expected state.
Thanks in advance :)
== UPDATE ==
We managed to get this to work by adding LiveTestWidgetsFlutterBinding(); to the start of our main. But it doesn't feel like a good solution.
Encountered the same issue with BLoC driven UI widgets, I found the solution is pretty simple: use expectLater() rather than expect() to wait for BLoC state update:
testWidgets('test widgets driven by BLoC', (tester) async {
await tester.pumpWidget(yourWidget);
await tester.tap(loginButton); // Do something like tapping a button, entering text
await tester.pumpAndSettle();
await expectLater(findSomething, findsOneWidget); // IMPRTANT: use `expectLater` to wait for BLoC state update
expect(findSomethingElse, findsOneWidget); // Subsequently you can use normal version `expect` until next `tester.pumpAndSettle()`
}
I put breakpoints into the BLoC to figure out what's the problem, it turns out that without using expectLater, the normal expect is being evaluated before the BLoC stream emits a new state. That is to say BLoC does emit a new state, but at that time the test case already runs to the end.
By using expectLater, it is being evaluated after the new state is emitted.

How to start `mockjax` back after `stop`

In my application I have a shuffle button to start and stop the mockjax response. as a first time, I am initiating the mockjax it works. later the user click on off button to stop mockjax service. ( I am showing server data )
for stop I use:
stopSimulation : function( ) {
if( !this.retriveSimulationStatus() ) {
console.log("i switched off!!");
$.mockjax.clear(); //removes the simulation;
}
}
But user clicks back to start button, But mockjax not intercepting again to provide the data.
so what is the correct way to stop and start the mockjax service? any one help me?
Clearing the Mockjax handlers does NOT "stop" them, it erases them from Mockjax. There is no "start"/"stop" functionality. You will need to clear the handlers, then re-add the handlers when you want to start. Here's an example:
var handler = { url: '/foo', responseText: { foo: 'bar' } };
function start() {
$.mockjax(handler);
}
function stop() {
$.mockjax.clear();
}

How to wait for Location Alert ( which is system alert) to show?

I figured out how to dismiss System alert, but I am not able to wait for it to show , since app doesn't see System Alerts. I tried to debug with app.debugDescription and app.alerts.count but no luck.
You should use addUIInterruptionMonitor as #Oletha wrote.
The tricky part here is that system alert buttons don't use Accessibility Identifiers so you have to search for text to tap them. This text is translated to the language you're running your simulator/device, which can be hard if you want to run the test for several languages beside English.
You could use AutoMate framework to simplify this. Here you have an example how to deal with system alerts using AutoMate:
func locationWhenInUse() {
let token = addUIInterruptionMonitor(withDescription: "Location") { (alert) -> Bool in
guard let locationAlert = LocationWhenInUseAlert(element: alert) else {
XCTFail("Cannot create LocationWhenInUseAlert object")
return false
}
locationAlert.allowElement.tap()
return true
}
// Interruption won't happen without some kind of action.
app.tap()
// Wait for given element to appear
wait(forVisibilityOf: locationPage.requestLabel)
removeUIInterruptionMonitor(token)
}
In the example above the locationAlert.allowElement.tap() is possible because AutoMate can handle any language supported by iOS Simulator.
For more examples on how to deal with system alerts using AutoMate please look into: PermissionsTests.swift
Use addUIInterruptionMonitor:withDescription:handler: to register an interruption monitor. To 'wait' for the system alert to appear, use the handler to set a variable when it has been dealt with, and execute a benign interaction with the app when you want to wait for the alert.
You must continue to interact with the app while you are waiting, as interactions are what trigger interruption monitors.
class MyTests: XCTestCase {
let app = XCUIApplication()
func testLocationAlertAppears() {
var hasDismissedLocationAlert = false
let monitor = addUIInterruptionMonitor(withDescription: "LocationPermissions") { (alert) in
// Check this alert is the location alert
let location = NSPredicate(format: "label CONTAINS 'Location'")
if alert.staticTexts.element(matching: location).exists {
// Dismiss the alert
alert.buttons["Allow"].tap()
hasDismissedLocationAlert = true
return true
}
return false
}
// Wait for location alert to be dismissed
var i = 0
while !hasDismissedLocationAlert && i < 20 {
// Do some benign interaction
app.tap()
i += 1
}
// Clean up
removeUIInterruptionMonitor(monitor)
// Check location alert was dismissed
XCTAssertTrue(hasDismissedLocationAlert)
}
}