IdlingResource not waiting in AndroidTests - android

I am trying to test MyActivity shows an alert dialog when incorrect intent extras were passed. It is a url, so I pass the url to a internal webView to load the url and show an alert if any error happened. The alert should be dismissed when the positive button is clicked.
This is how the alertDialog is created when the error happens
// Method in `MyActivity.java` called when the url couldn't be loaded
private void showAlertDialog(final String title, final String message) {
final MyActivity self = this;
runOnUiThread(new Runnable() {
#Override
public void run() {
if (!isFinishing()) {
alertDialog = new AlertDialog.Builder(MyActivity.this)
.setTitle(title)
.setMessage(message)
.setCancelable(false)
.setPositiveButton(BUTTON_OK_TITLE, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
self.alertDialog = null;
//self.finishWithMessage(messageRaw, true);
}
}).create();
alertDialog.show();
}
}
});
}
In the test, I am using ElapsedTimeIdlingResource taken from chiuki's answer to wait 10 seconds after launching the activity and assert the alertDialog was created and showing.
Then I am pressing the alert button and wait again 10 seconds to try to assert it is gone.
This is the test code MyActivityTest.java:
#RunWith(AndroidJUnit4.class)
public class MyActivityTest {
#Rule
public ActivityTestRule<MyActivityTest> mActivityRule = new ActivityTestRule<>(MyActivityTest.class, true, false);
#Test
public void testErrorDialog() {
Intent intent = createIntentWithWrongExtras();
mActivityRule.launchActivity(intent);
// Wait
IdlingResource idlingResource1 = new ElapsedTimeIdlingResource(10000);
Espresso.registerIdlingResources(idlingResource1);
assertNotNull("Activity should have been created", mActivityRule.getActivity());
assertNotNull("AlertDialog should have been created", mActivityRule.getActivity().alertDialog);
assertTrue("AlertDialog should be showing", mActivityRule.getActivity().alertDialog.isShowing());
// Test clicking the button dismisses the alert
mActivityRule.getActivity().runOnUiThread(() ->
mActivityRule.getActivity().alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick()
);
IdlingResource idlingResource2 = new ElapsedTimeIdlingResource(10000);
Espresso.registerIdlingResources(idlingResource2);
assertTrue("AlertDialog should NOT be showing", mActivityRule.getActivity().alertDialog == null || !mActivityRule.getActivity().alertDialog.isShowing());
Espresso.unregisterIdlingResources(idlingResource2);
}
}
However the test always fails:
"AlertDialog should NOT be showing"
I don't think I am understanding well what is really going on. I wrote some logs and I can see that idlingResource1 never waits for 10 seconds. Also I know alertDialog becomes null when dismissed but that happens after the last assert, so idlingResource2 is not working either? Why? Is this the right way to test this?

IdlingResources make Espresso wait. But you do not use Espresso to test (besides registering IdlingResources that have no effect), so the test runs straight though without waiting and your test fails.
If you replace your IdlingResources with simple Thread.sleep() your test should work. At least it would wait.
Read a little bit about Espresso, it's easy and would really improve your test: https://developer.android.com/training/testing/ui-testing/espresso-testing.html

I don't think you are using Espresso in the right way.
Try remove the idlingResources, and replace the first three assertion with something like:
onView(use_matcher_to_match_the_dialog).check(matches(isDisplayed()));
Espresso will wait until UI thread to become idle.
Then, do the click in Espresso way:
onView(use_matcher_to_match_the_button).perform(click());
and the final assertion:
onView(use_matcher_to_match_the_dialog).check(matches(not(isDisplayed())));

Related

Android creating and returning value from dialogs called from non-Activity classes

I've built an Android application which calls a TCP socket related thread from six different activities. It works just and fine, but as I try to add a simple dialog witch asks the user's id/pw just before starting the thread(the retrieved data will be required in the thread), I'm having trouble. There is an adapter class which is actually called to start the threads, but it is also not an "Activity" which can implement a dialog.
Is there any way to solve this in some smarter method? Adding six same codes to create the dialog, and implementing additional six same handlers for each dialog will solve this, but I don't think that's not the right thing to do.
I tried to make the dialog an Activity(with a dialog theme), but it can't return any datas since the class which starts this dialog like activity is not an Activity(thus, startActivityForResult is invalid).
How can I solve this? Reforming the whole source is impossible, since it's over more than 20,000 line. Please help!
Threads which are not the UI/main thread cannot control UI elements like a dialog box. But there is a way to make a part of your code run on the main thread, and there you then can do such things.
You want to post something to the main handler like this:
new Handler().post(new Runnable{
public void run(){
//Be sure to pass your Activity class, not the Thread
AlertDialog.Builder builder = new AlertDialog.Builder(MyActivity.this);
//... setup dialog and show
}
});
Well, I finally figured out this easy issue.
To call a Dialog.show() on a non-UI Thread, I needed a Handler object created with the Looper.getMainLooper(). Then, just as #peedee explained, implement the things to do(UI related works) on the run() block.
Receiving the datas retrieved by the dialog wasn't difficult either. Adding some getter methods on my Dialog class, and adding an OnDismissListener before showing the dialog was all I required. (the OnDismissListener will react when the dialog's dismiss() function is called.)
Here's the code I wrote. Hope it might give help.
Handler mHandler = new Handler(Looper.getMainLooper());
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
Log.e("TESTRUNNABLE", "RUNNABLE LOADED");
cDialog = new TestDialog(mContext);
cDialog.setTitle("GROUP USER LOGIN");
cDialog.setOnDismissListener(new OnDismissListener(){
#Override
public void onDismiss(DialogInterface dialog) {
// TODO Auto-generated method stub
nameStr = cDialog.getNameStr();
pwStr = cDialog.getPwStr();
Toast.makeText(mContext, nameStr + ", " + pwStr, 3000).show();
Log.e("DISMISSLISTENER", nameStr + ", " + pwStr);
}
});
cDialog.show();
}
}, 0);

Auto-Close Alert Dialog

I have need where I have to auto close the Alert Dialog in 2 different situations
Auto-close the dialog after I get a return value which I am waiting for
Auto-close the dialog after 10 sec of no input from User. I know I should use timer of some sort, but not sure how to attach it with dialog.
I know and understand that its not right way to handle UI, but my requirement needs me to do this.
Please share your thoughts,
Thanks,
SKU
1- For the first case :
AlertDialog alertDialog = new AlertDialog.Builder(Main.this).create();
...
alertDialog.show();
int valueIamWaitingFor = 5;
if (aValue == valueIamWaitingFor){
alertDialog.hide();
}
2- For the second case :
private static final ScheduledExecutorService executor =
Executors.newSingleThreadScheduledExecutor();
public AlertDialog alertDialog = new AlertDialog.Builder(Main.this).create();
...
alertDialog.show();
Runnable hideDialog= new Runnable() {
public void run() {
this.alertDialog.hide();
}
};
executor.schedule(hideDialog, 10, TimeUnit.SECONDS);
Inherit a custom Dialog from AlertDialog, where you handle your timer in onStart. Using the AsyncTask would be good to do the countdown.
Auto-closing dialog ain't something bad... at least we always see this in changing screen resolution (win xp) which is good for such scenario. Maybe you can also include the countdown timer on the button (like "closing in 5 seconds").

Need to show loading screen while app queries server

in this app, the user logs in and their credentials are checked against a server.
The user could be waiting a few seconds, depending on how fast the phone can open a data connection if at all. I need dialog box saying "please wait" or "verifying credentials" or something a long those lines after the user clicks log in.
Desired visual order: press log in -> "please wait" dialog is show in this same activity -> when result comes in from server a new activity is loaded (or error is thrown)
Current visual order: press log in -> user waits as if the app is frozen -> new activity is loaded
I'm trying to do this threading thing with AsyncTask but I'm just not getting it right now!
class Progressor extends AsyncTask<Void, Void, Void> {
ProgressDialog dialog;
protected void onPreExecute(){
dialog = ProgressDialog.show(Login.this, "Logging In",
"Verifying Credentials, Please wait...", true);
}
Then in my oncreate method I had all of the other logic like user clicking the button and stuff, but I've since moved that into the AsyncTask method's doInBackGround function
/* When the Login Button is clicked: */
Button loginButton = (Button) findViewById(R.id.loginButton);
loginButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Progressor showMe = new Progressor();
showMe.onPreExecute();
showMe.doInBackground(null);
showMe.onPostExecute();
and onPostExecute simply dismisses the dialog box
Why doesn't this work and how should it be re-arranged. What variable should I be passing into the showMe.doInBackGround() function, it is void. In debugging it never goes in here
#Override
protected Void doInBackground(Void... arg0) {
Don't call the onPreExecute/doInBackground methods of an AsyncTask manually; just call execute() on it and it will call all your methods in the proper places from the correct threads. It defeats the entire purpose of an asynchronous task to call all of its methods synchronously from the UI thread (which is what your sample code does).
That isn't how you use an AsyncTask, have a look at the documentation. Once you have created a new instance of your task, just call execute(), not the individual methods:
Progressor showMe = new Progressor();
showMe.execute();
I have a similar code at the start of my application i load the current settings from the server, it works for me with:
public static ProgressDialog verlauf;
public static String vmessage = "";
static Handler handler = new Handler();;
public static void initialize_system(final Context ctx)
{
verlauf = ProgressDialog.show(ctx, "Starte FISforAndroid..", "synchronisiere Einstellungen",true,false);
new Thread(){
#Override
public void run(){
Looper.prepare();
GlobalVars.table_def.initialize();
vmessage = "erstelle Tabellen";
handler.post(verlauf_message);
builded = sqldriver.create_tables();
vmessage = "setze Systemkonstanten";
handler.post(verlauf_message);
builded = setsystemvars(ctx);
vmessage = "synchronisiere Einstellungen";
handler.post(verlauf_message);
builded = settings.sync_ini();
builded = settings.set_ini();
GlobalVars.system_initialized = builded;
switch(GlobalVars.init_flags.FLAG){
case 0:
break;
case GlobalVars.init_flags.UPDATE:
//load the update
break;
}
verlauf.dismiss();
}
}.start();
}
You need to call showMe.execute(), rather than directly calling doInBackground or onPreExecute etc.

Android: wait on user input from dialog?

I would like to implement a method that displays a dialog, waits until the dialog is dismissed, and then returns a result depending on the dialog contents. Is this possible?
public String getUserInput()
{
//do something to show dialog
String input = //get input from dialog
return input;
}
I am actually trying to implement an interface which has method "public String getUserInput()", where the returned String must be retrieved via dialog. This is easily done in java, seems impossible in android?
EDIT: Posting some sample code as requested in comment
getInput() must be called from a background thread (I call it from an AsynchTask). getInput() displays a dialog and calls wait. When the ok button is pressed on the dialog, the dialog sets the user input in a member variable and calls notify. When notify is called, getInput() continues and returns the member variable.
String m_Input;
public synchronized String getInput()
{
runOnUiThread(new Runnable()
{
#Override
public void run()
{
AlertDialog.Builder alert = new AlertDialog.Builder(context);
//customize alert dialog to allow desired input
alert.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton)
{
m_Input = alert.getCustomInput();
notify();
}
});
alert.show();
}
});
try
{
wait();
}
catch (InterruptedException e)
{
}
return m_Input;
}
Is this possible?
No. There is no blocking UI model in Android. Everything is asynchronous.
UPDATE
In response to some of your comments on the question itself, you cannot display a UI from a background thread. As I wrote in this answer, there is no blocking UI model in Android. Just put your code in the button handler for your dialog that you want to have executed when the dialog is accepted, such as in this sample project.
The right way to do this is an event driven program model, ie, "don't call us, we'll call you".
In simple console mode programming, your code tends to call blocking input functions, which don't return until you've gotten a value.
Many gui programming environments work differently - your code is not normally running, but instead it's called by the operating system / window manager when something of potential interest happens. You do something in response to this and promptly return - if you do not, you can't be notified of anything else since the OS has no way to contact you until you return. (In comparison to win32, it's as if the message loop is implemented by Android, and you only get to write the rest of the code that the message loop calls with events - if you don't return promptly, the message loop hangs)
As a result, you need to rethink your concept of program flow. Instead of writing out a to-do list as a simple series of statements, think about it as a sequence of actions which depend on each other and on input. Remember what action you are currently on in a state variable. When you get called with an event such as user input, see if that event means it's now possible to move on to the next step, and if so update your state variable before promptly returning to the OS in order to be able to receive the next event. If the event wasn't what you needed, then just return without updating your state.
If this model won't work for you, what you can do is write a background thread of program logic which runs like a console-mode application using blocking input. But your input functions will really just wait on a flag or something to be notified that input is available. Then on your UI thread where Android delivers events, you update the flag and promptly return. The background thread sees the flag has changed to indicate that data has been provided, and continues execution. (Something like an android terminal emulator takes this to an extreme, where the background component is actually another process - a console mode linux one, and it gets its input using potentially blocking I/O from pipes. The java component accepts android UI events and stuffs characters into the stdin pipe and pulls them out of the stdout pipe to display on the screen.)
Thanks for all the feedback, I was able to solve this using a background thread along with a wait() and notify(). I recognize this isn't the greatest idea for the given paradigm, but it was necessary to conform to a library that I am working with.
I had a hard time understanding all the solutions offered above so far so I found my own one.
I wrap the code thats supposed to be performed after the user input is OK'ed in a runnable, like so:
Runnable rOpenFile = new Runnable() {
#Override
public void run() {
.... code to perform
}
}
Then right below that I pass the name of the runnable function to the user dialog method.
userInput("Open File", rOpenFile);
The userInput method is based on the alertDialog builder like described above. When the user input is Ok'ed it starts the intended runnable.
private void userInput(String sTitle, final Runnable func) {
AlertDialog.Builder aBuilder = new AlertDialog.Builder(this);
aBuilder.setTitle(sTitle);
final EditText input = new EditText(this);
input.setInputType(InputType.TYPE_CLASS_TEXT);
aBuilder.setView(input);
bDialogDone = false;
aBuilder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
final String sText = input.getText().toString();
sEingabe = sText;
func.run();
}
});
aBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
sEingabe = "";
}
});
aBuilder.show();
}
Something like this would do
/**
*
*/
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.WindowManager;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
/**
* #author
*/
public class TextEntryActivity extends Activity {
private EditText et;
/*
* (non-Javadoc)
* #see android.app.Activity#onCreate(android.os.Bundle)
*/
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_text_entry);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
// title
try {
String s = getIntent().getExtras().getString("title");
if (s.length() > 0) {
this.setTitle(s);
}
} catch (Exception e) {
}
// value
try {
et = ((EditText) findViewById(R.id.txtValue));
et.setText(getIntent().getExtras().getString("value"));
} catch (Exception e) {
}
// button
((Button) findViewById(R.id.btnDone)).setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
executeDone();
}
});
}
/* (non-Javadoc)
* #see android.app.Activity#onBackPressed()
*/
#Override
public void onBackPressed() {
executeDone();
super.onBackPressed();
}
/**
*
*/
private void executeDone() {
Intent resultIntent = new Intent();
resultIntent.putExtra("value", TextEntryActivity.this.et.getText().toString());
setResult(Activity.RESULT_OK, resultIntent);
finish();
}
}
The launch is:
public void launchPreferedNameEdit() {
Intent foo = new Intent(this, TextEntryActivity.class);
foo.putExtra("value", objItem.getPreferedNickname());
this.startActivityForResult(foo, EDIT_PREFERED_NAME);
}
You get the result by using
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case EDIT_PREFERED_NAME:
try {
String value = data.getStringExtra("value");
if (value != null && value.length() > 0) {
}
} catch (Exception e) {
}
break;
default:
break;
}
}
CASE: My data was ready to be processes after a preference change listener event and I needed to add a String queried from user. It doesn't appear possible to pop an alert dialog while the options menu is open...so I had to wait. I threw the half complete object into the next activity in the workflow and set its onResume() to check if its placeholder was !null in which case I popped the dialog and finished up the object *"in the button handler of the dialog"*.
Since this is my first post I can't vote for the correct answer given above but want to save anyone else running into this the time and in-elegance of less correct solutions. The dialog is the place.
You can think in terms of a state machine where if you initially require first-time user input you can have a flag set to mark "user input needed" or whatever. Then upon processing an event you check that flag and if set you fire up a dialog as the only action for the event and unset the flag. Then from the dialog event handler after handling user input you can call the code normally intended for the case when a dialog is not needed.
For your reference, I just made a dialog.
It would show and wait then dismiss.
And I deploy Java wait and notify to make it, this function can be copied and run directly.
private final Object lock = new Lock();
private static final class Lock {}
private void showWaitDialog(final String message, final int time_to_wait) { //ms
if(this.isFinishing()) return;
final String TTAG = "[showWaitDialog]";
Log.d(TTAG, "dialog going to show");
final ProgressDialog waitProgress = ProgressDialog.show(this, "WARNING", message, true);
waitProgress.setCancelable(false);
waitProgress.setOnShowListener(new DialogInterface.OnShowListener() { //callback got the asynchronous
#Override
public void onShow(DialogInterface dialog) {
Log.d(TTAG, "dialog showed");
synchronized (lock) {
try {
Log.d(TTAG, "main thread going to wait");
lock.wait();
} catch (InterruptedException e) {
Log.e(TTAG, e.toString());
Log.e(TTAG, "main thread going ahead");
}
}
}
});
new Thread(new Runnable() {
#Override
public void run() {
synchronized (lock) {
try {
Thread.sleep(time_to_wait);
} catch (Exception e) {
Log.d(TTAG, e.toString());
}
lock.notifyAll();
Log.d(TTAG, "dialog notified");
waitProgress.dismiss();
}
}
}).start();
}

Check if AsyncTask is taking too long

I have an AsyncTask that get info from the web. Sometimes the connection fails and the AsyncTask processdialog is running forever.
In the doInBackground I have a check in the end if my catched info are empty and if this is the case, it should appear Positive button/Negative button, but this is not happening. The dialog is just running.
How can I check if an AsyncTask is taking too long time (Maybe 5 seconds) and dismiss the dialog?
Code snippet (doInBackground):
//orders is my ArrayList<Order> object, from my own Order class.
if(orders==null) {
pdia.dismiss();
AlertDialog.Builder alt_bld = new AlertDialog.Builder(ctx);
alt_bld.setMessage("Try agin?")
.setCancelable(false)
.setPositiveButton("Try again", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
button_refresh.setVisibility(View.GONE);
new ListTask().execute(null, null , null);
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = alt_bld.create();
alert.setTitle("Connection failed!");
alert.show();
}
else {
return orders;
}
Thanks in advance and tell me if you need more info!
Instead of checking your result in doInBackground() you can get the value from the process and check it in onPostExecute(), like this:
protected void onPostExecute(ArrayList<Order> localOrders){
super.onPostExecute(localOrders);
if (localOrders==null) {
// Paste the positive and negative DialogListeners here
// and dismiss the dialog.
}
}
The result from your doInBackground() process passes into onPostExecute's parameter, thence you can check if your ArrayList object is empty or not.
But then writing the above code snippet in the onPostExecute will defeat the purpose right? We want the user to not wait past 5 seconds for an answer from the webservice and therefore we must handle the timeout within doInBackground itself.

Categories

Resources