Domanda

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!

È stato utile?

Soluzione

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.

Altri suggerimenti

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top