Android AlertDialog crashes with BadTokenException? - java

this is my first post so please bear with me.
I'm a high school developer and recently released an Android app on the play store. I'm using Crashlytics to capture exceptions, and for this some reason it throws this error.
Fatal Exception: android.view.WindowManager$BadTokenException: Unable to add window -- token android.os.BinderProxy#1989547c is not valid; is your activity running?
It was reported specifically on the LG D855, Nexus 5, and the Huawei PLK AL10 occurring on versions 5.0, 5.0.2, 5.1.1 and 6.0.1. I've looked online, and have found that this occurs when an activity does not exist. This error occurs on the initial startup of the app.
The following is the code I use for an Alert Dialog which simply asks if the user wants to see a tutorial (y/n)
public void showTutorialDialog() {
AlertDialog tutorialDialog = new AlertDialog.Builder(this)
.setTitle(R.string.tutorial_question_title)
.setCancelable(false)
.setMessage(R.string.tutorial_question)
.setPositiveButton(getResources().getString(R.string.tutorial_question_pos), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// Take to tutorial
// Assume isn't backer for now..
finish();
Intent i = new Intent(StartupActivity.this, TutorialActivity.class);
i.putExtra("from", "StartupActivity");
startActivity(i);
}
})
.setNegativeButton(getResources().getString(R.string.tutorial_question_neg), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// No tutorial, ask if they are a backer
showBackerDialog();
}
}).show();
Upon the initial start of the app, I load the users purchase details using IabHelper in a separate class. This class, called PurchaseRetriever, retrieves the content of users purchases asynchronously and stores it in an ArrayList. This is how my code works.
if (mManager.isUserFirstTime()) {
// Initialize purchase retriever.
// The rest will be done when the observer reports that purchase data has been retrieved.
mPurchases = PurchaseRetriever.getInstance(StartupActivity.this);
mPurchases.addObserver(new FirstStartupObserver(this));
StartupManager.FIRST = true;
loadImageContent();
It runs using the Observer pattern, so when the purchase details are queried it calls the update() method in FirstStartupObserver, which then by a reference to StartupActivity, calls startupActivity.showTutorialDialog();where the error occurs.
I've tested it on multiple devices I and my friends own personally (Nexus 6, Nexus 5, Nexus 7 tablet, Samsung Galaxy Tab, various devices on Samsung Remote Lab) yet it works fine on my end...
Any advice appreciated, thanks.
Edit: Here is StartupActivity.
/**
* Main startup activity. Determines which activity to launch.
* Puts the user in one place or another depending on if they are a backer.
*/
public class StartupActivity extends AppCompatActivity {
private StartupManager mManager;
private ProgressBar bar;
// --- Used if first time app loading to query purchase info
private PurchaseRetriever mPurchases;
#Override
protected void onCreate(Bundle savedInstanceState) {
// Used in either cases
// If first time, displayed, if not, hidden//
//hideNavBar();
User.init(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_startup);
bar = (ProgressBar)this.findViewById(R.id.progressBar);
mManager = new StartupManager(this);
// Returns true if data was corrupt before
if (mManager.isDataCorrupt()) {
bar.setVisibility(View.VISIBLE);
loadImageContent();
// Reset watch to default black
// Internally starts NewMainActivity
ErrorManager.fixCorruptData(bar, this);
} else {
// Stays true until user selects watch
if (mManager.isUserFirstTime()) {
// Initialize purchase retriever.
// The rest will be done when the observer reports that purchase data has been retrieved.
mPurchases = PurchaseRetriever.getInstance(StartupActivity.this);
mPurchases.addObserver(new FirstStartupObserver(this));
StartupManager.FIRST = true;
loadImageContent();
} else {
// NOT first time starting app.
mPurchases = PurchaseRetriever.getInstance(StartupActivity.this);
mPurchases.addObserver(new AfterFirstStartupObserver(this));
loadImageContent();
}
}
}
// Two main dialogs used
public void showTutorialDialog() {
AlertDialog tutorialDialog = new AlertDialog.Builder(this)
.setTitle(R.string.tutorial_question_title)
.setCancelable(false)
.setMessage(R.string.tutorial_question)
.setPositiveButton(getResources().getString(R.string.tutorial_question_pos), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// Take to tutorial
// Assume isn't backer for now..
finish();
Intent i = new Intent(StartupActivity.this, TutorialActivity.class);
i.putExtra("from", "StartupActivity");
startActivity(i);
}
})
.setNegativeButton(getResources().getString(R.string.tutorial_question_neg), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// No tutorial, ask if they are a backer
showBackerDialog();
}
}).show();
tutorialDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(Color.RED);
tutorialDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(Color.RED);
}
private void showBackerDialog() {
// Show AlertDialog ask if they are kickstarter backer
AlertDialog askDialog = new AlertDialog.Builder(this)
.setTitle(getResources().getString(R.string.startup_dialog_title))
.setCancelable(false)
.setMessage(getResources().getString(R.string.startup_dialog_message))
.setPositiveButton(getResources().getString(R.string.startup_dialog_pos), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// User is a backer, take to watch chooser screen, then it takes to login screen
// Also look at Timer with TimerTask
new Thread(new Runnable() {
#Override
public void run() {
try {
Intent i = new Intent(StartupActivity.this, WatchChooserActivity.class);
i.putExtra("from", "StartupActivityBacker");
startActivity(i);
} finally {
finish();
}
}
}).start();
}
})
.setNegativeButton(getResources().getString(R.string.startup_dialog_neg), new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// User is not a backer, take to MainActivity
new Thread(new Runnable() {
#Override
public void run() {
try {
Intent i = new Intent(StartupActivity.this, WatchChooserActivity.class);
i.putExtra("from", "StartupActivityNonBacker");
startActivity(i);
} finally {
finish();
}
}
}).start();
}
}).show();
askDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(Color.RED);
askDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(Color.RED);
}
Here is the code for FirstStartupObserver.'
public class FirstStartupObserver implements Observer {
private StartupActivity startupActivity;
public FirstStartupObserver(StartupActivity startupActivity) {
this.startupActivity = startupActivity;
}
// Called when the observable is done loading purchase detail
// (Only called when user runs app first time)
#Override
public void update(Observable observable, Object data) {
// Set default first-time watch
// Query product data (the Watchfaces purchased in the form of a WatchFace object)
PurchaseRetriever mPurchases = PurchaseRetriever.getInstance(startupActivity);
if (mPurchases.hasSuccess()) {
ArrayList<DynamicLoader.WatchFace> facesOwned = mPurchases.getPurchasedFaces();
for (DynamicLoader.WatchFace f : facesOwned) {
f.setPurchased(true);
}
// Check if coming from v1.4
if (UpgradeManager.isUpgrading(startupActivity)) {
// Then it calls the code below, but after the async task.
String accessCode = UpgradeManager.getOldAccessCode(startupActivity);
String accessToken = UpgradeManager.getOldAccessToken(startupActivity);
UpgradeManager.migrateBacker(startupActivity, accessCode, accessToken);
} else {
// Ask if they want to see tutorial.
// This is when the exception occurs!!!
startupActivity.showTutorialDialog();
}
return;
} else {
Log.d("TAG", "Showing fail dialog");
DialogUtils.showIabFailDialog(startupActivity, this);
}
}
}

token android.os.BinderProxy#1989547c is not valid; is your activity
running?
This means that you're trying to show your popup while your activity is being destroyed or after it's destroyed.
You can check if your activity isDestroyed like below:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && !isDestroyed()) {
showTutorialDialog();
}
If you're supporting below api 17 devices you can try to use isFinishing in else case. I did not test if it's working as expected. (If i'm wrong please correct me.)
else {
if (!isFinishing()) {
showTutorialDialog();
}
}
Or for a quick fix you can surround with try catch

This is usually caused by doing something in an AsyncTask or other background task which holds a reference to the Activity and tries to display the dialog when the work is done. In this case, it sounds like your FirstStartupObserver is holding a reference to the activity and trying to show a dialog, but the activity may have been destroyed by the time PurchaseRetriever completes its work.
Don't try to test the activity state, and don't catch the BadTokenException. That just masks the problem. The simplest solution would be to cancel the PurchaseRetriever when the activity is paused. If you want the background work to survive configuration changes like rotations but still be restricted to the user-perceived lifetime of the activity, do the work in a retained fragment. Finally, if the background work should continue when the user navigates between activities or puts the app in the background, do the work in a Service and save the result where the activity can retrieve it.

token android.os.BinderProxy#1989547c is not valid; is your activity
running?
You are trying to load the AlertDialog too early, when the Activity doesn't exist! In my apps I load a little tutorial when the activity lifecycle is completed:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash_screen);
...
...
...
showTutorialDialog();
}

Related

Crash in ViewRootImpl.java line XXX on Android 7 despite catching all Toast related errors [duplicate]

From my main activity, I need to call an inner class and in a method within the class, I need to show AlertDialog. After dismissing it, when the OK button is pressed, forward to Google Play for purchase.
Things work perfectly for most of the times, but for few users it is crashing on builder.show() and I can see "android.view.WindowManager$BadTokenException: Unable to add window" from crash log. Please suggest.
My code is pretty much like this:
public class classname1 extends Activity{
public void onCreate(Bundle savedInstanceState) {
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.<view>);
//call the <className1> class to execute
}
private class classNamename2 extends AsyncTask<String, Void, String>{
protected String doInBackground(String... params) {}
protected void onPostExecute(String result){
if(page.contains("error"))
{
AlertDialog.Builder builder = new AlertDialog.Builder(classname1.this);
builder.setCancelable(true);
builder.setMessage("");
builder.setInverseBackgroundForced(true);
builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton){
dialog.dismiss();
if(!<condition>)
{
try
{
String pl = "";
mHelper.<flow>(<class>.this, SKU, RC_REQUEST,
<listener>, pl);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
});
builder.show();
}
}
}
}
I have also seen the error in another alert where I am not forwarding to any other activity. It's simple like this:
AlertDialog.Builder builder = new AlertDialog.Builder(classname1.this);
builder.setCancelable(true);
//if successful
builder.setMessage(" ");
builder.setInverseBackgroundForced(true);
builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton){
// dialog.dismiss();
}
});
builder.show();
}
android.view.WindowManager$BadTokenException: Unable to add window"
Problem :
This exception occurs when the app is trying to notify the user from
the background thread (AsyncTask) by opening a Dialog.
If you are trying to modify the UI from background thread (usually
from onPostExecute() of AsyncTask) and if the activity enters
finishing stage i.e.) explicitly calling finish(), user pressing home
or back button or activity clean up made by Android then you get this
error.
Reason :
The reason for this exception is that, as the exception message says,
the activity has finished but you are trying to display a dialog with
a context of the finished activity. Since there is no window for the
dialog to display the android runtime throws this exception.
Solution:
Use isFinishing() method which is called by Android to check whether
this activity is in the process of finishing: be it explicit finish()
call or activity clean up made by Android. By using this method it is
very easy to avoid opening dialog from background thread when activity
is finishing.
Also maintain a weak reference for the activity (and not a strong
reference so that activity can be destroyed once not needed) and check
if the activity is not finishing before performing any UI using this
activity reference (i.e. showing a dialog).
eg.
private class chkSubscription extends AsyncTask<String, Void, String>{
private final WeakReference<login> loginActivityWeakRef;
public chkSubscription (login loginActivity) {
super();
this.loginActivityWeakRef= new WeakReference<login >(loginActivity)
}
protected String doInBackground(String... params) {
//web service call
}
protected void onPostExecute(String result) {
if(page.contains("error")) //when not subscribed
{
if (loginActivityWeakRef.get() != null && !loginActivityWeakRef.get().isFinishing()) {
AlertDialog.Builder builder = new AlertDialog.Builder(login.this);
builder.setCancelable(true);
builder.setMessage(sucObject);
builder.setInverseBackgroundForced(true);
builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton){
dialog.dismiss();
}
});
builder.show();
}
}
}
}
Update :
Window Tokens:
As its name implies, a window token is a special type of Binder token
that the window manager uses to uniquely identify a window in the
system. Window tokens are important for security because they make it
impossible for malicious applications to draw on top of the windows of
other applications. The window manager protects against this by
requiring applications to pass their application's window token as
part of each request to add or remove a window. If the tokens don't
match, the window manager rejects the request and throws a
BadTokenException. Without window tokens, this necessary
identification step wouldn't be possible and the window manager
wouldn't be able to protect itself from malicious applications.
 A real-world scenario:
When an application starts up for the first time,
the ActivityManagerService creates a special kind of window token
called an application window token, which uniquely identifies the
application's top-level container window. The activity manager gives
this token to both the application and the window manager, and the
application sends the token to the window manager each time it wants
to add a new window to the screen. This ensures secure interaction
between the application and the window manager (by making it
impossible to add windows on top of other applications), and also
makes it easy for the activity manager to make direct requests to the
window manager.
I had dialog showing function:
void showDialog(){
new AlertDialog.Builder(MyActivity.this)
...
.show();
}
I was getting this error and i just had to check isFinishing() before calling this dialog showing function.
if(!isFinishing())
showDialog();
The possible reason is the context of the alert dialog. You may be finished that activity so its trying to open in that context but which is already closed.
Try changing the context of that dialog to you first activity beacause it won't be finished till the end.
e.g
rather than this.
AlertDialog alertDialog = new AlertDialog.Builder(this).create();
try to use
AlertDialog alertDialog = new AlertDialog.Builder(FirstActivity.getInstance()).create();
first you cannot extend AsyncTask without override doInBackground
second try to create AlterDailog from the builder then call show().
private boolean visible = false;
class chkSubscription extends AsyncTask<String, Void, String>
{
protected void onPostExecute(String result)
{
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setCancelable(true);
builder.setMessage(sucObject);
builder.setInverseBackgroundForced(true);
builder.setNeutralButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton)
{
dialog.dismiss();
}
});
AlertDialog myAlertDialog = builder.create();
if(visible) myAlertDialog.show();
}
#Override
protected String doInBackground(String... arg0)
{
// TODO Auto-generated method stub
return null;
}
}
#Override
protected void onResume()
{
// TODO Auto-generated method stub
super.onResume();
visible = true;
}
#Override
protected void onStop()
{
visible = false;
super.onStop();
}
I am creating Dialog in onCreate and using it with show and hide. For me the root cause was not dismissing onBackPressed, which was finishing the Home activity.
#Override
public void onBackPressed() {
new AlertDialog.Builder(this)
.setTitle("Really Exit?")
.setMessage("Are you sure you want to exit?")
.setNegativeButton(android.R.string.no, null)
.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog,
int which) {
Home.this.finish();
return;
}
}).create().show();
I was finishing the Home Activity onBackPressed without closing / dismissing my dialogs.
When I dismissed my dialogs the crash disappeared.
new AlertDialog.Builder(this)
.setTitle("Really Exit?")
.setMessage("Are you sure you want to exit?")
.setNegativeButton(android.R.string.no, null)
.setPositiveButton(android.R.string.yes,
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog,
int which) {
networkErrorDialog.dismiss() ;
homeLocationErrorDialog.dismiss() ;
currentLocationErrorDialog.dismiss() ;
Home.this.finish();
return;
}
}).create().show();
I try this it solved.
AlertDialog.Builder builder = new AlertDialog.Builder(
this);
builder.setCancelable(true);
builder.setTitle("Opss!!");
builder.setMessage("You Don't have anough coins to withdraw. ");
builder.setMessage("Please read the Withdraw rules.");
builder.setInverseBackgroundForced(true);
builder.setPositiveButton("OK",
(dialog, which) -> dialog.dismiss());
builder.create().show();
In my case I refactored code and put the creation of the Dialog in a separate class. I only handed over the clicked View because a View contains a context object already. This led to the same error message although all ran on the MainThread.
I then switched to handing over the Activity as well and used its context in the dialog creation
-> Everything works now.
fun showDialogToDeletePhoto(baseActivity: BaseActivity, clickedParent: View, deletePhotoClickedListener: DeletePhotoClickedListener) {
val dialog = AlertDialog.Builder(baseActivity) // <-- here
.setTitle(baseActivity.getString(R.string.alert_delete_picture_dialog_title))
...
}
I , can't format the code snippet properly, sorry :(
I got this error, but mine was coming from the Toasts, not a Dialog.
I have Activity and Fragments in my layout. Code for the Toast was in the Activity class. Fragments gets loaded before the Activity.
I think the Toast code was hit before the Context/Activity finished initializing. I think it was the getApplicationContext() in the command Toast.makeText(getApplicationContext(), "onMenutItemActionCollapse called", Toast.LENGTH_SHORT).show();
Try this :
public class <class> extends Activity{
private AlertDialog.Builder builder;
public void onCreate(Bundle savedInstanceState) {
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.<view>);
builder = new AlertDialog.Builder(<class>.this);
builder.setCancelable(true);
builder.setMessage(<message>);
builder.setInverseBackgroundForced(true);
//call the <className> class to execute
}
private class <className> extends AsyncTask<String, Void, String>{
protected String doInBackground(String... params) {
}
protected void onPostExecute(String result){
if(page.contains("error")) //when not subscribed
{
if(builder!=null){
builder.setNeutralButton("Ok",new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton){
dialog.dismiss();
if(!<condition>)
{
try
{
String pl = "";
mHelper.<flow>(<class>.this, SKU, RC_REQUEST,
<listener>, pl);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
});
builder.show();
}
}
}
}
with this globals variables idea,
I saved MainActivity instance in onCreate();
Android global variable
public class ApplicationController extends Application {
public static MainActivity this_MainActivity;
}
and Open dialog like this. it worked.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Global Var
globals = (ApplicationController) this.getApplication();
globals.this_MainActivity = this;
}
and in a thread, I open dialog like this.
AlertDialog.Builder alert = new AlertDialog.Builder(globals.this_MainActivity);
Open MainActivity
Start a thread.
Open dialog from thread -> work.
Click "Back button" ( onCreate will be called and remove first MainActivity)
New MainActivity will start. ( and save it's instance to globals )
Open dialog from first thread --> it will open and work.
: )

How Should I check for internet connectivty and display a "retry" and "cancel" dialog?

I want to check for internet connection and if it's available call getData() but if it's not available show a dialog with RETRY and CANCEL options.
If RETRY is clicked check for internet connection; if available call getData but if it isn't available, show the dialog again (something like looping).
Alternatively, if CANCEL is clicked exit the app altogether.
I'm using this class to check for network availability and internet connection:
public class NetworkCheck {
public static boolean isAvailableAndConnected(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
boolean isNetworkAvailable = cm.getActiveNetworkInfo() != null;
boolean isNetWorkConnected = isNetworkAvailable && cm.getActiveNetworkInfo().isConnected();
return isNetWorkConnected;
}
}
And in MainActivity I do this:
if (NetworkCheck.isAvailableAndConnected(this)) {
//Caling method to get data
getData();
} else {
final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
alertDialogBuilder.setTitle("No Internet Connection");
alertDialogBuilder.setMessage("Failed to load. Please ensure ypu're connected to the internet and try again");
alertDialogBuilder.setPositiveButton("Retry", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if (!NetworkCheck.isAvailableAndConnected(context)) {
alertDialogBuilder.show();
} else {
getData();
}
}
});
alertDialogBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
alertDialogBuilder.show();
}
From the above codes, I have three questions:
In line if (!NetworkCheck.isAvailableAndConnected(context)) { context is being highlighted in red and when I hover it, I see Cannot resolve symbol 'context'. If I leave the method empty or type this or getActivity; Android Studio complains. Which parameter show I pass the method?
Calling finish() will only kill the activity. Shoudn't the whole app be killed and how?
What else am I doing wrong?
1) If you're not using fragments, on the place of context you could do a workaround like this (first you declare):
final Context mContext;
mContext = this;
Then you substitute context to mContext
2) Killing the activity is not a recommendation (you probably already read about Activity Lifecycle), but you could try android.os.Process.killProcess(android.os.Process.myPid());

How do you prevent the back button from closing an application from the main activity?

I am having a problem with my activity closing when i am trying to display a fragment. I am trying to have my application ask the user to rate my application when they press the back button to exit my application from the main screen. This is supposed to display a fragment asking to rate it now or later. The fragment appears for about 1 second then the application closes before the user can make a choice. How do I fix this?
This is the solution I came up with:
I am going to show you what I did to implement the fragment and how I prevented it from closing the activity bypassing the fragment.
In my main activity I added:
#Override
public void onBackPressed() {
final DialogFragment rate = new RateMe();
rate.show(getSupportFragmentManager(), "Rate");
}
Be sure to remove the super statement from the Override to stop the button push from CLOSING OUT YOUR APPLICATION completely when the back button is pushed.
I created a blank fragment activity with an xml called "RateMe". I left the xml blank after removing the "Hllo World" but be sure to double check your tools:context= . This was a mistake I made and it did not match my package. Simple fix. Just change it to your package name.
Then in the fragment i put:
public class RateMe extends DialogFragment {
public static RateMe newInstance(int title) {
RateMe frag = new RateMe();
Bundle args = new Bundle();
// args.putInt("title", title);
frag.setArguments(args);
return frag;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setCancelable(false);
builder.setTitle("Rate Me");
builder.setMessage(R.string.Rate);
//Create a string in your res values strings.xml file. This will be your //R.string that appears on your fragment^^^^ Then continue by adding......
builder.setPositiveButton("Yes, I Will", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Uri uri = Uri.pa(RateMe.this);rse("market://details?id=****PUT YOUR GOOGLE PLAY URL INFO HERE");
Intent myAppLinkToMarket = new Intent(Intent.ACTION_VIEW, uri);
myAppLinkToMarket.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY |
Intent.FLAG_ACTIVITY_NEW_DOCUMENT |
Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
try {
startActivity(myAppLinkToMarket);
} catch (ActivityNotFoundException e) {
Toast.makeText(getContext(), " unable to find market app", Toast.LENGTH_LONG).show();
}
}
});
builder.setNegativeButton("Not Now", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
System.exit(0); //this will close the application
}
});
return builder.create();
}
}
This worked perfectly for me. Hope this can help someone.

Android - How to keep prompting a dialog

Introduction
What I want to accomplish [sounds] simple. I want to prompt a user with a login dialog until the user successfully authenticates.
What I planned to do is use an AsyncTask for the data handling and web requests, but this has turned into a nightmare quickly; most likely due to my lack of experience in Android.
However, I know this can be done, as I've seen it before in other apps.
What I want to accomplish
The question is how? I know what I want to do:
1. Initially prompt a user to login.
2. Send authentication data.
3. If successful, continue the application.
4. If unsuccessful, reprompt the user until success.
What I have so far
What I have so far is my AsyncTask (LoginTask) which will handle the web requests and login data:
public class LoginTask extends AsyncTask<String, Void, App.STATUS>
{
private boolean m_proceed = false;
private String m_username, m_key;
#Override
protected void onPreExecute()
{
// Check if there is a dialog on screen. //
m_proceed = !App.DIALOG_ONSCREEN;
}
#Override
protected App.STATUS doInBackground(String ... p_args)
{
// Do not do this if a dialog is on screen. //
if(!m_proceed)
return App.STATUS.DENIED;
// Make a web request. //
try
{
URL t_url = new URL("https://mysite.com/api/login");
HttpsURLConnection t_con = (HttpsURLConnection)t_url.openConnection();
t_con.setRequestMethod("POST");
t_con.setRequestProperty("User-Agent", "Mozilla/5.0");
t_con.setDoOutput(true);
DataOutputStream t_wr = new DataOutputStream(t_con.getOutputStream());
t_wr.writeBytes("username="+p_args[0]+"&password="+p_args[1]);
t_wr.flush();
t_wr.close();
t_con.connect();
BufferedReader t_in = new BufferedReader(new InputStreamReader(t_con.getInputStream()));
String t_input_line;
StringBuffer t_response = new StringBuffer();
while((t_input_line = t_in.readLine()) != null)
{
t_response.append(t_input_line);
}
t_in.close();
// If denied, return failed. If accepted, set the username and key. //
if(t_response.toString().equals("DENIED"))
return App.STATUS.FAILED;
else
{
m_key = t_response.toString();
m_username = p_args[0];
}
return App.STATUS.ACCEPTED;
}
catch(Exception err)
{
System.err.println(err.getMessage());
}
return App.STATUS.FAILED;
}
#Override
protected void onPostExecute(App.STATUS p_status)
{
// Authenticate the user if the username and key are valid. //
if(p_status == App.STATUS.ACCEPTED)
App.acceptCredentials(m_username, m_key);
// The dialog is no longer on the screen. //
App.DIALOG_ONSCREEN = false;
}
}
And the main activity (HomeActivity) which will prompt the user if they are not authenticated, and will show content if they are:
public class HomeActivity extends Activity
{
#Override
public void onCreate(Bundle p_data)
{
// Basic crap... //
super.onCreate(p_data);
setContentView(R.layout.activity_home);
// Are we authenticated? //
if(!App.isAuthenticated())
{
// Create the dialog. //
LayoutInflater t_infl = LayoutInflater.from(this);
View t_login_view = t_infl.inflate(R.layout.login_dialog, null);
AlertDialog.Builder t_builder = new AlertDialog.Builder(this);
t_builder.setTitle("Login").setView(t_login_view).setPositiveButton("Login", new DialogInterface.OnClickListener()
{
#Override
public void onClick(DialogInterface dialog, int which)
{
// What should go here? //
}
});
t_builder.create();
}
// How do I keep checking if the user is not authenticated, and keep showing the dialog as such? //
}
}
What I need help with
My main question is how do I design my program in such a way that I can easily keep displaying the login dialog until the user has successfully authenticated? I thought of using a while loop, but then it would keep displaying dialogs and hamper performance. It's pretty tricky when I have asynchronous and synchronous tasks working in tandem.
I'm not looking for straight code, but general insight would be much appreciated.
Thank you for taking your time to read this and thank you for helping!
The solution
HomeActivity.java
private void promptLogin()
{
final Context t_main_context = this;
// Create the dialog. //
LayoutInflater t_infl = LayoutInflater.from(this);
final View t_login_view = t_infl.inflate(R.layout.login_dialog, null);
final AlertDialog t_dialog = new AlertDialog.Builder(this)
.setTitle("Login")
.setCancelable(false)
.setView(t_login_view)
.setPositiveButton("Login", null)
.create();
t_dialog.show();
t_dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View t_view)
{
String t_username = ((EditText)t_login_view.findViewById(R.id.in_username)).getText().toString(),
t_password = ((EditText)t_login_view.findViewById(R.id.in_password)).getText().toString();
try
{
new LoginTask(t_main_context, t_dialog).execute(t_username, t_password);
}
catch(Exception err)
{
err.printStackTrace();
}
}
});
}
#Override
public void onCreate(Bundle p_data)
{
// Basic crap... //
super.onCreate(p_data);
setContentView(R.layout.activity_home);
// Are we authenticated? //
if(!App.isAuthenticated())
promptLogin();
}
LoginTask.java
private String m_username, m_key;
private Context m_context;
private AlertDialog m_dialog;
private ProgressDialog m_loading;
public LoginTask(Context p_context, AlertDialog p_dialog)
{
m_context = p_context;
m_dialog = p_dialog;
m_loading = ProgressDialog.show(m_context, "", "Logging in...", true);
}
#Override
protected App.STATUS doInBackground(String ... p_args)
{
// Make a web request. //
try
{
URL t_url = new URL("https://mysite.com/api/login");
HttpsURLConnection t_con = (HttpsURLConnection)t_url.openConnection();
t_con.setRequestMethod("POST");
t_con.setRequestProperty("User-Agent", "Mozilla/5.0");
t_con.setDoOutput(true);
DataOutputStream t_wr = new DataOutputStream(t_con.getOutputStream());
t_wr.writeBytes("username="+p_args[0]+"&password="+p_args[1]);
t_wr.flush();
t_wr.close();
t_con.connect();
BufferedReader t_in = new BufferedReader(new InputStreamReader(t_con.getInputStream()));
String t_input_line;
StringBuffer t_response = new StringBuffer();
while((t_input_line = t_in.readLine()) != null)
{
t_response.append(t_input_line);
}
t_in.close();
// If denied, return failed. If accepted, set the username and key. //
if(t_response.toString().equals("DENIED"))
return App.STATUS.FAILED;
else
{
m_key = t_response.toString();
m_username = p_args[0];
}
return App.STATUS.ACCEPTED;
}
catch(Exception err)
{
System.err.println(err.getMessage());
}
return App.STATUS.FAILED;
}
#Override
protected void onPostExecute(App.STATUS p_status)
{
m_loading.dismiss();
// Authenticate the user if the username and key are valid. //
if(p_status == App.STATUS.ACCEPTED)
{
App.acceptCredentials(m_username, m_key);
m_dialog.dismiss();
}
else
Toast.makeText(m_context, "Login failed", Toast.LENGTH_SHORT).show();
}
So what I did in promptLogin() in HomeActivity.java was that I overrode the button onClickListener, so that the dialog would not close unless closed by t_dialog.dismiss(). I then sent the web request to LoginTask and passed the dialog as a parameter, so that the dialog would only close until I dismissed the dialog.
I only dismiss the dialog when the credentials are accepted, as you can see in onPostExecute().
In this way, the dialog stays on screen until the user successfully logs in, which is the behavior I was looking for.
Thanks everyone for helping!
1. Initially prompt a user to login.
keep prompting the dialog here, with setCancelable(false), so the user will not cancel the login process. Then create a View.OnclickListner on the button that the user have to click on in order to send data to your server. Let's say Login button.
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("Delete entry")
.setMessage("Are you sure you want to delete this entry?")
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// send data here with AsyncTask
}
})
.setIcon(R.drawable.ic_my_icon);
builder.setCancelable(false);
builder.show();
2. Send authentication data.
Use your AsyncTask here, do the sending task in doInBackgroud() method, and return something onPostExecute() to know if authentication succeeded or not. if success, dismiss the dialog, if not, you keep the dialog and wait for the user to click again the Login button.
protected void onPostExecute(Boolean result) {
if(result) {
// he is now authenticated, dismiss dialog and continue in your app
dialog.dismiss();
} else {
// nothing to do, until he succeed
}
}
3. If successful, continue the application.
Dismiss the dialog here buy using the dismiss() method.
4. If unsuccessful, reprompt the user until success.
Don't do anything, let the dialog shown, until the authentication process succeeds. You can also show something to the user (a toast, an image etc) to tell him that he hasn't logged in yet.
Hope it's clear for you.
You have to create one activity which handles your login, and another which is your main activity. If the login fails, nothing happens, if it succeeds you start the second activity. No need for your complicated setup.
You can also have a look at the Volley library, makes http connections pretty easy.

Get And Display List Of Installed Programs on Android

I'm currently developing an app that will allow the user to choose an app and launch it at a later time (there is more functionality but this is the main thing I'm having an issue with.)
I'm looking for a way to get a list of the applications (user installed or updateable ex. Gmail, GMaps, etc...) And throw it into an AlertDialog similar to how you add a shortcut to the Homescreen (Long press -> Applications).
This is the thread I'm using that has the code to get the list of applications that I need. However how would I turn this into an AlertDialog?
Here is the code from the thread.
public void getApps()
{
PackageManager pm = getPackageManager();
List<ApplicationInfo> apps = pm.getInstalledApplications(0);
List<ApplicationInfo> installedApps = new ArrayList<ApplicationInfo>();
for(ApplicationInfo app : apps) {
//checks for flags; if flagged, check if updated system app
if((app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 1) {
installedApps.add(app);
//it's a system app, not interested
} else if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 1) {
//Discard this one
//in this case, it should be a user-installed app
} else {
installedApps.add(app);
}
}
}//end getApps()
And here is the code I use for displaying an AlertDialog similar to what I want to use.
//PseudoCode does not compile
public void displayAppList(View v)
{
final CharSequence[] items = {getApps()};
AlertDialog.Builder builder = new AlertDialog.Builder(SchedulerActivity.this);
builder.setTitle("Choose an App To Launch");
builder.setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
appChoiceString[count] = items[item];
Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();
}
});
builder.setPositiveButton("Yes",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Toast.makeText(SchedulerActivity.this, "Success", Toast.LENGTH_SHORT).show();
}
});
builder.setNegativeButton("No",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Toast.makeText(SchedulerActivity.this, "Fail", Toast.LENGTH_SHORT).show();
}
});
AlertDialog alert = builder.create();
alert.show();
}
Any help as to getting this to display properly would be awesome.
Why not just use the standard intent chooser? (See this). Otherwise, you probably want to explain what is not displaying the way you want, and how you really want it to look in detail.

Categories