my app is fairly simple and always works IF my IOT device is up.
i need to load a popup and show the ReScan button on the toolbar if the device cannot be found.
the app preloads IPaddress="-" and loads 2 asyncTask(s)
one uses NsdManager.DiscoveryListener to find the mDNS name and loads the IP into IPaddress
this task watches to see IPaddress change and gets the presets from the device by JSON and sets up the UI or pops up the error dialog with instructions if not found.
MY PROBLEM:
when counter >= 15 , i show the "Rescan" Button on the toolbar with setMenuVisible() then popup the error dialog but when the button in the dialog is pressed to close the dialog, the "Rescan" Button disappears again.
Also times out in about 5 seconds.
how do i get the "Rescan" Button to stay?
.
private class getSettingsFromClock extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
String mlooper = IPaddress;
Log.i(TAG, "LOG getSettingsFromClock doInBackground started ");
int counter = 0;
while ( mlooper.equals("-") ) {
mlooper = IPaddress;
try {
Thread.sleep(600);
} catch (InterruptedException e) {
e.printStackTrace();
}
counter++;
if (counter >= 15) // in normal operation counter never goes above 3
{
Log.i(TAG, "LOG getSettingsFromClock - NO IP Found, count= " + counter );
runOnUiThread(new Runnable() {
#Override
public void run() {
setMenuVisible( true, R.id.action_rescan); // show rescan button on toolbar
try { // delay is debugging only
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//scanning failed Popup Dialog
final Dialog dialog = new Dialog(context );
dialog.setContentView(R.layout.popup);
dialog.setTitle("Scan Error");
Button button = dialog.findViewById(R.id.Button01);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
dialog.dismiss();
}
});
dialog.show();
Toast.makeText(getApplicationContext(),
"Could Not get presets from clock. \n check Clock is on and on WiFi\n and reload app.",
Toast.LENGTH_LONG).show();
}
});
break;
}
}
if( IPaddress != "-" )
{
// gets JSON here
} else
{
// add popup - IOT Not found
}
// JSON starts here
if (JSON_return != null) {
try {
// loads presets from JSON to UI here
} catch (final JSONException e) {
Log.e(TAG, "LOG, JSON parsing error: " + e.getMessage());
}
} else
{
Log.e(TAG, "LOG, Could Not get JSON from Clock.");
}
return null;
}
} // end asyncTask class
// remember to run on main thread
// NOTE; private Menu option_Menu; declared in MainActivity
// ie; setMenuVisible( true, R.id.action_rescan);
public void setMenuVisible(boolean visible, int id) {
if (option_Menu != null) {
option_Menu.findItem(id).setVisible(visible);
}
}
Mike M. had it, Thank You Mike
added onPrepareOptionsMenu()
added showRescan = visible; and invalidateOptionsMenu(); to setMenuVisible()
all work perfectly now.
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
try {
if( showRescan )
{
option_Menu.findItem(R.id.action_rescan).setVisible( true );
} else
{
option_Menu.findItem(R.id.action_rescan).setVisible( false );
}
}
catch(Exception e) {
Log.e(TAG, "onPrepareOptionsMenu error");
}
return true;
}
// when task is completed you can show your menu just by calling this method
// remember to run on main thread
// ie; setMenuVisible( true, R.id.action_rescan);
public void setMenuVisible(boolean visible, int id) {
if (option_Menu != null) {
option_Menu.findItem(id).setVisible(visible);
showRescan = visible;
invalidateOptionsMenu();
}
}
Here is my code:
automaticCountryButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
progressBar.setVisibility(View.VISIBLE);
if (ContextCompat.checkSelfPermission(HomeActivity.this,
Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED){
setUpLocationPermission();
return;
}
Log.d(TAG, String.valueOf(gps.canGetLocation()));
Log.d(TAG, String.valueOf(gps.getLocation()));
Log.d(TAG, String.valueOf(gps.getLatitude()));
Log.d(TAG, String.valueOf(gps.getLongitude()));
Geocoder myLocation = new Geocoder(HomeActivity.this);
try
{
myList = myLocation.getFromLocation(gps.getLatitude(), gps.getLongitude(), 1);
}
catch (Exception e)
{
Log.d(TAG, "unable");
e.printStackTrace();
}
if(myList != null) {
try {
String country = myList.get(0).getCountryName();
Log.d(TAG, country);
findCountryInArrayList(country);
}
catch (Exception e)
{
Toast.makeText(HomeActivity.this, "Didn't manage to automatically detect location.", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
}
});
}
I want that immediately after the view is clicked the progressbar will become visible. However, it donesn't become visable until all the code is finished, which is against the whole point.
WHy is this not happening right at the beginning of the click? I have put progressBar.setVisibility(View.VISIBLE) at the top, why is it only executed after all the code is done, which sometimes takes a few seconds.
Thanks very much.
This is because you are trying to do your work on the UI thread - the UI will not actually be updated at all until this method finishes.
Try changing up your call to this:
public void onClick(View view) {
progressBar.setVisibility(View.VISIBLE);
progressBar.post( new Runnable() {
public void run() {
// long running code that has UI interactions
}
});
}
This will show the view immediately, and submit the runnable - long running task - to the message queue; this task will be run on a background thread that can still manipulate the UI, but will not cause it to hang.
I am new to android. I want to show progress dialog when user click on login button. I tried this but the dialog is not showing
btn_logIn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
getUserCredentials();
}
}); //end of anonymous class
private void showProgressDialog() {
if (dialog == null) {
dialog = new ProgressDialog(this);
}
dialog.setMessage("Please Wait. Your authentication is in progress");
dialog.setButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which)
dialog.dismiss();
}
}); //end of anonymous class
dialog.show();
} //end of showProgressDialog()
private void getUserCredentials() {
EditText txt_userName = (EditText) findViewById(R.id.txt_userName);
String userName = txt_userName.getText().toString();
EditText txt_password = (EditText) findViewById(R.id.txt_password);
String password = txt_password.getText().toString();
if (userName != null && !userName.trim().equals("") && password != null && !password.trim().equals("")) {
showProgressDialog();
callWebService(userName, password);
}
} //end of getUserCredentials()
private void callWebService(String userName, String password) {
try {
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
request.addProperty("userName", userName);
....
Object result = envelope.getResponse();
if (result.equals("true")) {
dialog.dismiss();
Toast.makeText(this, result.toString(), Toast.LENGTH_SHORT).show();
} else {
dialog.dismiss();
Toast.makeText(this, result.toString(), Toast.LENGTH_SHORT).show();
}
} catch (SocketTimeoutException e) {
dialog.dismiss();
Toast.makeText(this, "Service is not connected, Please make sure your server is running", Toast.LENGTH_LONG).show();
} catch(Exception e) {
e.printStackTrace();
dialog.dismiss();
Toast.makeText(this, "Unable to connect, please try again later. Thank you", Toast.LENGTH_LONG).show();
}
} //end of callWebServide()
Am i doing anything wrong. When i click on login button and service is not running then it shows message that Service is not connected, Please make sure your server is running", but the dialog isn't showing...Why? My logic is when user click on login button and fields have values then start showing progress dialog and if anything happens like when result come or server is not running or if any exception happen , then i remove the dialog and show the appropriate message, but dialog isn't showing...Why? What i am doing wrong? Please help..
Thanks
Try this,
Change your getUserCredentials() like this,
private void getUserCredentials() {
EditText txt_userName = (EditText) findViewById(R.id.txt_userName);
String userName = txt_userName.getText().toString();
EditText txt_password = (EditText) findViewById(R.id.txt_password);
String password = txt_password.getText().toString();
if (userName != null && !userName.trim().equals("") && password != null && !password.trim().equals("")) {
showProgressDialog();
Thread t=new Thread(new Runnable() {
public void run() {
callWebService(userName, password);
}
}); t.start();
}
}
And your callWebService method like this,
private void callWebService(String userName, String password) {
try {
SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
request.addProperty("userName", userName);
....
Object result = envelope.getResponse();
if (result.equals("true")) {
dialog.dismiss();
Toast.makeText(this, result.toString(), Toast.LENGTH_SHORT).show();
} else {
ActivityName.this.runOnUiThread(new Runnable() {
public void run() {
dialog.dismiss();
Toast.makeText(this, result.toString(), Toast.LENGTH_SHORT).show();
}
});
}
} catch (SocketTimeoutException e) {
ActivityName.this.runOnUiThread(new Runnable() {
public void run() {
dialog.dismiss();
Toast.makeText(this, "Service is not connected, Please make sure your server is running", Toast.LENGTH_LONG).show();
}
});
} catch(Exception e) {
e.printStackTrace();
ActivityName.this.runOnUiThread(new Runnable() {
public void run() {
dialog.dismiss();
Toast.makeText(this, "Unable to connect, please try again later. Thank you", Toast.LENGTH_LONG).show();
}
});
}
}
Update 1
To answer your questions from your comments,
1)Yes Async Task is more efficient. It has its own methods to do the same task what I have described here.
AsyncTask has the following methods,
i)onPreExecute()-which can be used to start your Dialog
ii)doInBackground()-which acts as the background thread.
iii)onPostExecute()-which gets called at the end where you can dismiss the dialog.
The reason why I didn't mention is that, there are possibilities that you might have to change your working code's structure to adapt to Async task.
2)runonUiThread- as the name indicates, anything inside this will be considered as it is running in the main UI thread. So basically to update the screen you have to use this. There are also other methods available, like Handlers which can also do the same task.
Use AsyncTask for it when ever task started at that time initialise your widget and then call your webservice from run method and close your progress bar on stop method.
I have several SeekBar and onSeekBarProgressStop(), I want to show a Toast message.
But if on SeekBar I perform the action rapidly then UI thread somehow blocks and Toast message waits till UI thread is free.
Now my concern is to avoid the new Toast message if the Toast message is already displaying. Or is their any condition by which we check that UI thread is currently free then I'll show the Toast message.
I tried it in both way, by using runOnUIThread() and also creating new Handler.
I've tried a variety of things to do this. At first I tried using the cancel(), which had no effect for me (see also this answer).
With setDuration(n) I wasn't coming to anywhere either. It turned out by logging getDuration() that it carries a value of 0 (if makeText()'s parameter was Toast.LENGTH_SHORT) or 1 (if makeText()'s parameter was Toast.LENGTH_LONG).
Finally I tried to check if the toast's view isShown(). Of course it isn't if no toast is shown, but even more, it returns a fatal error in this case. So I needed to try and catch the error.
Now, isShown() returns true if a toast is displayed.
Utilizing isShown() I came up with the method:
/**
* <strong>public void showAToast (String st)</strong></br>
* this little method displays a toast on the screen.</br>
* it checks if a toast is currently visible</br>
* if so </br>
* ... it "sets" the new text</br>
* else</br>
* ... it "makes" the new text</br>
* and "shows" either or
* #param st the string to be toasted
*/
public void showAToast (String st){ //"Toast toast" is declared in the class
try{ toast.getView().isShown(); // true if visible
toast.setText(st);
} catch (Exception e) { // invisible if exception
toast = Toast.makeText(theContext, st, toastDuration);
}
toast.show(); //finally display it
}
The following is an alternative solution to the most popular answer, without the try/catch.
public void showAToast (String message){
if (mToast != null) {
mToast.cancel();
}
mToast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
mToast.show();
}
A clean solution that works out of the box. Define this on your Activity:
private Toast toast;
/**
* Use this to prevent multiple Toasts from spamming the UI for a long time.
*/
public void showToast(CharSequence text, int duration)
{
if (toast == null)
toast = Toast.makeText(this, text, duration);
else
toast.setText(text);
toast.show();
}
public void showToast(int resId, int duration)
{
showToast(getResources().getText(resId), duration);
}
My solution is:
public class Utils {
public static Toast showToast(Context context, Toast toast, String str) {
if (toast != null)
toast.cancel();
Toast t = Toast.makeText(context, str, Toast.LENGTH_SHORT);
t.show();
return t;
}
}
and the caller should have a Toast member for this method's parameter, or
class EasyToast {
Toast toast;
Context context;
public EasyToast(Context context) {
this.context = context;
}
public Toast show(String str) {
if (toast != null)
toast.cancel();
Toast t = Toast.makeText(context, str, Toast.LENGTH_SHORT);
t.show();
return t;
}
}
have a helper class like this.
keep track of the last time you showed the toast, and make re-showing it a no-op if it falls within some interval.
public class RepeatSafeToast {
private static final int DURATION = 4000;
private static final Map<Object, Long> lastShown = new HashMap<Object, Long>();
private static boolean isRecent(Object obj) {
Long last = lastShown.get(obj);
if (last == null) {
return false;
}
long now = System.currentTimeMillis();
if (last + DURATION < now) {
return false;
}
return true;
}
public static synchronized void show(Context context, int resId) {
if (isRecent(resId)) {
return;
}
Toast.makeText(context, resId, Toast.LENGTH_LONG).show();
lastShown.put(resId, System.currentTimeMillis());
}
public static synchronized void show(Context context, String msg) {
if (isRecent(msg)) {
return;
}
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
lastShown.put(msg, System.currentTimeMillis());
}
}
and then,
RepeatSafeToast.show(this, "Hello, toast.");
RepeatSafeToast.show(this, "Hello, toast."); // won't be shown
RepeatSafeToast.show(this, "Hello, toast."); // won't be shown
RepeatSafeToast.show(this, "Hello, toast."); // won't be shown
this isn't perfect, since the length of LENGTH_SHORT and LENGTH_LONG are undefined, but it works well in practice. it has the advantage over other solutions that you don't need to hold on to the Toast object and the call syntax remains terse.
The enhanced function from above thread, which will show toast only if not visible with same text message:
public void showSingleToast(){
try{
if(!toast.getView().isShown()) {
toast.show();
}
} catch (Exception exception) {
exception.printStackTrace();
Log.d(TAG,"Toast Exception is "+exception.getLocalizedMessage());
toast = Toast.makeText(this.getActivity(), getContext().getString(R.string.no_search_result_fou`enter code here`nd), Toast.LENGTH_SHORT);
toast.show();
}
}
A combined solution
For my case, I needed to cancel the current toast if it shown and display another one.
This was to solve the scenario when the user asks for a service while it is still loading or not available I need to show a toast (might me different if the requested service is different). Otherwise, the toasts will keep showing in order and it will take a very long time to hide them automatically.
So basically I save the instance of the toast am creating and the following code is how to cancel it safly
synchronized public void cancel() {
if(toast == null) {
Log.d(TAG, "cancel: toast is null (occurs first time only)" );
return;
}
final View view = toast.getView();
if(view == null){
Log.d(TAG, "cancel: view is null");
return;
}
if (view.isShown()) {
toast.cancel();
}else{
Log.d(TAG, "cancel: view is already dismissed");
}
}
And to use it I can now not worry about cancelling as in:
if (toastSingleton != null ) {
toastSingleton.cancel();
toastSingleton.showToast(messageText);
}else{
Log.e(TAG, "setMessageText: toastSingleton is null");
}
The showToast is up to you how to implement it as I needed a custom look for my toast.
Good for stopping stacking e.g. click driven toast. Based off #Addi's answer.
public Toast toast = null;
//....
public void favsDisplay(MenuItem item)
{
if(toast == null) // first time around
{
Context context = getApplicationContext();
CharSequence text = "Some text...";
int duration = Toast.LENGTH_SHORT;
toast = Toast.makeText(context, text, duration);
}
try
{
if(toast.getView().isShown() == false) // if false not showing anymore, then show it
toast.show();
}
catch (Exception e)
{}
}
Check for showing toast message on screen either it is displayed or not.
For Showing a toast message Make a separate class. And use the method of this class which display the toast message after checking the visibility of the toast message. Use This Snippet of code:
public class AppToast {
private static Toast toast;
public static void showToast(Context context, String message) {
try {
if (!toast.getView().isShown()) {
toast=Toast.makeText(context, message, Toast.LENGTH_SHORT);
toast.show();
}
} catch (Exception ex) {
toast=Toast.makeText(context,message,Toast.LENGTH_SHORT);
toast.show();
}
}
}
I hope this solution will help you.
Thanks
added timer to remove the toast after 2 seconds.
private Toast toast;
public void showToast(String text){
try {
toast.getView().isShown();
toast.setText(text);
}catch (Exception e){
toast = Toast.makeText(mContext, text, Toast.LENGTH_SHORT);
}
if(toast.getView().isShown()){
new Timer().schedule(new TimerTask() {
#Override
public void run() {
toast.cancel();
}
}, 2000);
}else{
toast.show();
}
}
showToast("Please wait");
Solution for Android 11+ (API level 30 and above)
Toast.getView() is deprecated since API level 30:
This method was deprecated in API level 30. Custom toast views are
deprecated. Apps can create a standard text toast with the
makeText(android.content.Context, java.lang.CharSequence, int) method,
or use a Snackbar when in the foreground. Starting from Android
Build.VERSION_CODES#R, apps targeting API level Build.VERSION_CODES#R
or higher that are in the background will not have custom toast views
displayed.
If you want to avoid Toast overlapping, you could save the time the last Toast was shown using System.currentTimeMillis().
Here's a an example of use case where Toast is instantly overlapped only if the text of the new one is different from the last one, otherwise, it waits a certain amount of time before overlapping it (i.e SAME_TOAST_DURATION_BEFORE_OVERLAP):
public class SingleToast {
private static Toast _toast;
private static String _text;
private static long _lastToast;
private static final int SAME_TOAST_DURATION_BEFORE_OVERLAP = 2000; // in ms
public static void show(Context context, String text, int duration) {
if (_toast == null) {
_toast = Toast.makeText(context.getApplicationContext(), text, duration);
_text = text;
} else {
if (_text.equals(text)) {
if (System.currentTimeMillis() - _lastToast > SAME_TOAST_DURATION_BEFORE_OVERLAP) {
_toast.cancel();
} else {
return;
}
} else {
_text = text;
_toast.cancel();
_toast.setText(_text);
}
}
_lastToast = System.currentTimeMillis();
_toast.show();
}
}
i am calling this function from the menu and calls the upload(item) function to pass the index of the selected priority.
public void showPriorityDialog()
{
final CharSequence[] priority = {"1 Hour", "12 Hours", "24 Hours", "Cancel"};
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Select Priority");
builder.setItems(priority, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
if(item != 3)
upload(item);
}
});
AlertDialog alert = builder.create();
alert.show();
}
however, whenever i call the upload function, the thread doesn't run in background, and the OS thinks that the app is not responding due to executing timeout.
public void upload(int priority)
{
final int _priority = priority;
uploadThread = new Thread()
{
#Override
public void run()
{
try
{
super.run();
mHandler.post(new Runnable() {
#Override
public void run() {
try
{
//ftp commands...
}
catch (Exception e)
{
Toast.makeText(getApplicationContext(), e.toString() , Toast.LENGTH_SHORT).show();
}
}
});
}
catch (Exception e)
{
Toast.makeText(getApplicationContext(), e.toString() , Toast.LENGTH_SHORT).show();
}
}
};
uploadThread.start();
}
am i doing something wrong? TIA
When you do mHandler.post(), your entire Runnable executes on UI thread and your background thread just exits. To fix, do FTP before posting to handler. Then do mHandler.post() to have Toast appear. Note that you catch also need to display Toast via post.