Thursday, 23 June 2016

Android login: using volley, php, mysql, pdo

Today you gonna learn how to create a login activity that uses volley for request. Click here If you want more information about volley library.
To begin with, will first create a back-end using PHP and my SQL. I'm using wamp server to host the php scripts.

1. Backend - PHP, MYSQL, PDO

Project Structure:




i) fire up the server (wampserver for my case), open browser and go to localhost/phpmyadmin

ii) create a database androidmastermind,

iii) create a table login,

CREATE TABLE IF NOT EXISTS `login` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(30) NOT NULL,
  `password` varchar(50) NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
)

INSERT INTO `login` (`id`, `username`, `password`, `created_at`)
 VALUES(1, 'admin@gmail.com', 'admin', '2016-06-22 09:34:13');

iv) Create constants.php file, this will hold all the variables that we will need to reuse.

<?php

define("USERNAME","username");
define("PASSWORD","password");

v) Create DbConnection.php file, this will handle the database connection and disconnection when needed.
* Remember to change the username and password*

<?php

    private static $dbName = 'androidmastermind';
    private static $dbHost = 'localhost';
    private static $dbUsername = 'kevynashinski';
    private static $dbUserPassword = 'elegant';
    private static $cont = null;
    public function __construct(){
    }

    public static function connect(){
        // One connection through whole application
        if (null == self::$cont) {
            try {
                self::$cont = new PDO("mysql:host=" . self::$dbHost . ";"
                 . "dbname=" . self::$dbName, self::$dbUsername, self::$dbUserPassword);
                self::$cont->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            } catch (PDOException $e) {
                die($e->getMessage());            }
        }
        return self::$cont;    }

    public static function disconnect(){
        self::$cont = null;    }
}

vi) Finally create login.php file, this will authenticate the user, by receiving thus username and password through POST.

<?php
include 'DbConnection.php';
include 'constants.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {

    $username = $_REQUEST[USERNAME];
    $password = $_REQUEST[PASSWORD];
//    open connection
    $conn = DatabaseConnection::connect();
    $sql = "select * from login WHERE username='$username' AND password='$password'";
//    check if a row exists with the credentials
    if ($conn->query($sql)->rowCount() > 0) {
//        shows there's a record
        echo 1;
    } else {
//        shows there's no such record
        echo 0;
    }
} else{
//    shows that nothing was posted
    echo 2;
}

* The index.php serves the purpose of testing our database connection.

<?php
include 'DbConnection.php';
$conn=DatabaseConnection::connect();
if($conn){
    echo "Ready to Roll!!!";
}else{
    echo "connection Error".$conn->errorInfo();
}

2. Frontend - Android

Project Structure:



i) Create a new android project, name it AndroidLogin

ii) Import volley library to your build.gradle (app:module)

compile group: 'com.mcxiaoke.volley', name: 'library', version: '1.0.19'

ii) Create App.java in the package, this class extends Application, it holds the initialization of the app configurations, in our case volley initialization.

package com.androidmatermind.androidlogin;

import android.app.Application;
import android.text.TextUtils;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;

/** * Created by Kevynashinski on 6/23/2016. */
public class App extends Application{
    public static final String TAG = "androidlogin";

    private ImageLoader mImageLoader;

    private RequestQueue mRequestQueue;
    private static App mInstance;

    @Override    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }


    public static synchronized App getInstance() {
        return mInstance;
    }

    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(getBaseContext());
        }

        return mRequestQueue;
    }

    public <T> void addToRequestQueue(Request<T> req, String tag) {
        // set the default tag if tag is empty
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
        getRequestQueue().add(req);
    }

    public <T> void addToRequestQueue(Request<T> req) {
        req.setTag(TAG);
        getRequestQueue().add(req);
    }

    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }
}

iii) Create another file, AppConfig.java, this holds the server link

package com.androidmatermind.androidlogin;

/** * Created by muus on 6/23/2016. */
public final class AppConfig {

    public static final String SERVER_URL="http://192.168.56.1/androidlogin/";
    public static final String URL_LOGIN=SERVER_URL+"login.php";
}

iv) InternetConnection.java class, will help us check if internet is available before login begins

package com.androidmatermind.androidlogin;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

/** * Created by kevynashinski on 11/9/2015. */
public class InternetConnection {

    Context context;

    public InternetConnection(Context context) {
        this.context = context;
    }

    public boolean isInternetAvailable() {
        ConnectivityManager connectivityManager = (ConnectivityManager)
 context.getSystemService(Context.CONNECTIVITY_SERVICE);

//        boolean isWiFi = activeNetwork.getType() == ConnectivityManager.TYPE_WIFI;
        NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
        return activeNetwork != null && activeNetwork.isConnectedOrConnecting();
    }
}

v) Under the MainActivity.java, will when a user click sign in button, will first check if internet is available, then carry out the login operation. The email and password will also be validated

package com.androidmatermind.androidlogin;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.TargetApi;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.CursorLoader;
import android.content.Loader;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.util.Patterns;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static android.Manifest.permission.READ_CONTACTS;

/** * A login screen that offers login via email/password. */
public class MainActivity extends AppCompatActivity implements LoaderCallbacks<Cursor> {

    private static final int REQUEST_READ_CONTACTS = 0;

    // UI references.    private AutoCompleteTextView mEmailView;
    private EditText mPasswordView;
    private View mProgressView;
    private View mLoginFormView;

    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Set up the login form.
        mEmailView = (AutoCompleteTextView) findViewById(R.id.email);
        populateAutoComplete();

        mPasswordView = (EditText) findViewById(R.id.password);

        Button signInButton = (Button) findViewById(R.id.sign_in_button);
        assert signInButton != null;
        signInButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                if(new InternetConnection(getBaseContext()).isInternetAvailable())
                attemptLogin();
                else {
                    Toast.makeText(getBaseContext(),"Check your internet connection",
Toast.LENGTH_LONG).show();
                }
            }
        });

        mLoginFormView = findViewById(R.id.login_form);
        mProgressView = findViewById(R.id.login_progress);
    }

    private void populateAutoComplete() {
        if (!mayRequestContacts()) {
            return;
        }

        getLoaderManager().initLoader(0, null, this);
    }

    private boolean mayRequestContacts() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            return true;
        }
        if (checkSelfPermission(READ_CONTACTS) == PackageManager.PERMISSION_GRANTED) {
            return true;
        }
        if (shouldShowRequestPermissionRationale(READ_CONTACTS)) {
            Snackbar.make(mEmailView, R.string.permission_rationale, Snackbar.LENGTH_INDEFINITE)
                    .setAction(android.R.string.ok, new View.OnClickListener() {
                        @Override
                        @TargetApi(Build.VERSION_CODES.M)
                        public void onClick(View v) {
                            requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS);
                        }
                    });
        } else {
            requestPermissions(new String[]{READ_CONTACTS}, REQUEST_READ_CONTACTS);
        }
        return false;
    }

    /**     * Callback received when a permissions request has been completed.     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
        if (requestCode == REQUEST_READ_CONTACTS) {
            if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                populateAutoComplete();
            }
        }
    }


    /**     * Attempts to sign in or register the account specified by the login form.
     * If there are form errors (invalid email, missing fields, etc.), the     
     * errors are presented and no actual login attempt is made.
     */
    private void attemptLogin() {
        // Reset errors.
        mEmailView.setError(null);
        mPasswordView.setError(null);

        // Store values at the time of the login attempt.
        String email = mEmailView.getText().toString();
        String password = mPasswordView.getText().toString();

        boolean cancel = false;
        View focusView = null;

        // Check for a valid password, if the user entered one.
        if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) {
            mPasswordView.setError(getString(R.string.error_invalid_password));
            focusView = mPasswordView;
            cancel = true;
        }

        // Check for a valid email address.
        if (TextUtils.isEmpty(email)) {
            mEmailView.setError(getString(R.string.error_field_required));
            focusView = mEmailView;
            cancel = true;
        } else if (!isEmailValid(email)) {
            mEmailView.setError(getString(R.string.error_invalid_email));
            focusView = mEmailView;
            cancel = true;
        }

        if (cancel) {
            // There was an error; don't attempt login and focus the first
            // form field with an error.
            focusView.requestFocus();
        } else {
            // Show a progress spinner, and kick off a background task to
            // perform the user login attempt.
            showProgress(true);
            login(email,password);
        }
    }

    private void login(final String email, final String password) {
        StringRequest strReq = new StringRequest(Request.Method.POST,
                AppConfig.URL_LOGIN, new Response.Listener<String>() {

            @Override
            public void onResponse(String response) {
                Log.d(App.TAG, "Login Response: " + response);

                showProgress(false);

                if(response.equalsIgnoreCase("1")){
                    Toast.makeText(getBaseContext(),"Login Success!!!",Toast.LENGTH_LONG).show();
                }else if(response.equalsIgnoreCase("0")){
                    Toast.makeText(getBaseContext(),"Invalid username or password",
Toast.LENGTH_LONG).show();
                }else if(response.equalsIgnoreCase("2")){
                    Toast.makeText(getBaseContext(),"Server Error!",Toast.LENGTH_LONG).show();
                }
            }
        }, new Response.ErrorListener() {

            @Override
            public void onErrorResponse(VolleyError error) {

                showProgress(false);

                Log.e(App.TAG, "Login Error: " + error.getMessage());
            }
        }) {

            @Override
            protected Map<String, String> getParams() {
                // Posting parameters to login url
                Map<String, String> params = new HashMap<>();
                params.put("username", email);
                params.put("password", password);

                return params;
            }
        };

        // Adding request to request queue
        App.getInstance().addToRequestQueue(strReq);
    }

    private boolean isEmailValid(String email) {
        //TODO: Replace this with your own logic
        return Patterns.EMAIL_ADDRESS.matcher(email).matches();
    }

    private boolean isPasswordValid(String password) {
        //TODO: Replace this with your own logic
        return password.length() > 4;
    }

    /**     * Shows the progress UI and hides the login form.
     */    
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2)
    private void showProgress(final boolean show) {
        // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow
        // for very easy animations. If available, use these APIs to fade-in
        // the progress spinner.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
            int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime);

            mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
            mLoginFormView.animate().setDuration(shortAnimTime).alpha(
                    show ? 0 : 1).setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
                }
            });

            mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
            mProgressView.animate().setDuration(shortAnimTime).alpha(
                    show ? 1 : 0).setListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
                }
            });
        } else {
            // The ViewPropertyAnimator APIs are not available, so simply show
            // and hide the relevant UI components.
            mProgressView.setVisibility(show ? View.VISIBLE : View.GONE);
            mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE);
        }
    }

    @Override
    public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
        return new CursorLoader(this,
                // Retrieve data rows for the device user's 'profile' contact.
                Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI,
                        ContactsContract.Contacts.Data.CONTENT_DIRECTORY), ProfileQuery.PROJECTION,

                // Select only email addresses.
                ContactsContract.Contacts.Data.MIMETYPE +
                        " = ?", new String[]{ContactsContract.CommonDataKinds.Email
                .CONTENT_ITEM_TYPE},

                // Show primary email addresses first. Note that there won't be
                // a primary email address if the user hasn't specified one.
                ContactsContract.Contacts.Data.IS_PRIMARY + " DESC");
    }

    @Override
    public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
        List<String> emails = new ArrayList<>();
        cursor.moveToFirst();
        while (!cursor.isAfterLast()) {
            emails.add(cursor.getString(ProfileQuery.ADDRESS));
            cursor.moveToNext();
        }

        addEmailsToAutoComplete(emails);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> cursorLoader) {

    }

    private void addEmailsToAutoComplete(List<String> emailAddressCollection) {
        //Create adapter to tell the AutoCompleteTextView what to show in its dropdown list.
        ArrayAdapter<String> adapter =
                new ArrayAdapter<>(MainActivity.this,
                        android.R.layout.simple_dropdown_item_1line, emailAddressCollection);

        mEmailView.setAdapter(adapter);
    }


    private interface ProfileQuery {
        String[] PROJECTION = {
                ContactsContract.CommonDataKinds.Email.ADDRESS,
                ContactsContract.CommonDataKinds.Email.IS_PRIMARY,
        };

        int ADDRESS = 0;
        int IS_PRIMARY = 1;
    }

}

vi) Add the following codes to the activity_main.xml

<LinearLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <!-- Login progress -->
    <ProgressBar
        android:id="@+id/login_progress"
        style="?android:attr/progressBarStyleLarge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:visibility="gone" />

    <ScrollView
        android:id="@+id/login_form"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:id="@+id/email_login_form"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <android.support.design.widget.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

                <AutoCompleteTextView
                    android:id="@+id/email"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="Email Address"
                    android:inputType="textEmailAddress"
                    android:maxLines="1"
                    android:singleLine="true" />

            </android.support.design.widget.TextInputLayout>

            <android.support.design.widget.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

                <EditText
                    android:id="@+id/password"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="password"
                    android:inputType="textPassword"
                    android:maxLines="1"
                    android:singleLine="true" />

            </android.support.design.widget.TextInputLayout>

            <Button
                android:id="@+id/sign_in_button"
                style="?android:textAppearanceSmall"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:text="Sign in"
                android:textStyle="bold" />

        </LinearLayout>
    </ScrollView>
</LinearLayout>

vii) Run the project,
a) When login success.



b) When login failure.



Thats all, your good to go!!!
Reactions:

12 comments:

  1. Given so much info in it, These type of articles keeps the users interest in the website, and keep on sharing more ... good luck.
    Android Training in chennai | Best Android Training in chennai|Android Training in chennai with placement | Android Training in velachery

    ReplyDelete
  2. You got an extremely helpful website I actually have been here reading for regarding an hour. I’m an initiate and your success is incredibly a lot of a concept on behalf of me.
    Click here:
    angularjs training in bangalore
    Click here:
    angularjs training in chennai

    ReplyDelete
  3. Thanks for posting this info. I just want to let you know that I just check out your site and I find it very interesting and informative. I can't wait to read lots of your posts
    Microsoft azure training in chennai
    Click here:
    Microsoft azure training in online
    Click here:
    Microsoft azure training in bangalore
    Click here:
    Microsoft azure training in pune

    ReplyDelete
  4. Nice post. By reading your blog, i get inspired and this provides some useful information. Thank you for posting this exclusive post for our vision. 
    Blueprism training in velachery

    Blueprism training in marathahalli


    AWS Training in chennai

    ReplyDelete
  5. Thanks you for sharing this unique useful information content with us. Really awesome work. keep on blogging
    Devops Training in Chennai

    Devops Training in Bangalore

    ReplyDelete
  6. Thanks you for sharing this unique useful information content with us. Really awesome work. keep on blogging
    Devops Training in Chennai

    Devops Training in Bangalore

    ReplyDelete
  7. Great Article… I love to read your articles because your writing style is too good, its is very very helpful for all of us and I never get bored while reading your article because, they are becomes a more and more interesting from the starting lines until the end.

    best rpa training in chennai |
    rpa training in chennai |
    rpa training in bangalore
    rpa training in pune | rpa online training

    ReplyDelete
  8. Thank you for benefiting from time to focus on this kind of, I feel firmly about it and also really like comprehending far more with this particular subject matter. In case doable, when you get know-how, is it possible to thoughts modernizing your site together with far more details? It’s extremely useful to me.


    java training in chennai | java training in USA

    java training in indira nagar

    ReplyDelete
  9. Thank you for allowing me to read it, welcome to the next in a recent article. And thanks for sharing the nice article, keep posting or updating news article.

    angularjs Training in bangalore

    angularjs Training in bangalore

    angularjs Training in chennai

    python training in pune

    python training institute in chennai

    python training in Bangalore

    ReplyDelete

  10. Whoa! I’m enjoying the template/theme of this website. It’s simple, yet effective. A lot of times it’s very hard to get that “perfect balance” between superb usability and visual appeal. I must say you’ve done a very good job with this.


    AWS Training in BTM Layout |Best AWS Training in BTM Layout

    AWS Training in Marathahalli | Best AWS Training in Marathahalli


    ReplyDelete