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!!!

31 comments:

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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

  7. 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
  8. I found a lot of interesting information here. A really good post man, very thankful and hopeful that you will write many more posts like this one.
    kajal agarwal hot

    ReplyDelete
  9. This is an awesome post.Really very informative and creative contents. These concept is a good way to enhance the knowledge.I like it and help me to development very well.Thank you for this brief explanation and very nice information.Well, got a good knowledge.
    angularjs online training

    apache spark online training

    informatica mdm online training

    devops online training

    aws online training

    ReplyDelete
  10. Hey Nice Blog!! Thanks For Sharing!!!Wonderful blog & good post.Its really helpful for me, waiting for a more new post. Keep Blogging!
    SEO company in coimbatore
    SEO Service in Coimbatore
    web design company in coimbatore

    ReplyDelete
  11. This is quite educational arrange. It has famous breeding about what I rarity to vouch. Colossal proverb. This trumpet is a famous tone to nab to troths. Congratulations on a career well achieved. This arrange is synchronous s informative impolites festivity to pity. I appreciated what you ok extremely here 
    Microsoft Azure online training
    Selenium online training
    Java online training
    Python online training
    uipath online training

    ReplyDelete
  12. This post is so useful and informative. Keep updating with more information.....
    Android Developer Requirement
    Android Applications

    ReplyDelete
  13. Nice article.Thanks for posting. I really appreciate your efforts for writing this blog.
    SQL Classes in Pune

    ReplyDelete