com.amsterdam.marktbureau.makkelijkemarkt.LoginFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.amsterdam.marktbureau.makkelijkemarkt.LoginFragment.java

Source

/**
 * Copyright (C) 2016 X Gemeente
 *                    X Amsterdam
 *                    X Onderzoek, Informatie en Statistiek
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/
 */
package com.amsterdam.marktbureau.makkelijkemarkt;

import android.app.ProgressDialog;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.SimpleCursorAdapter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import com.amsterdam.marktbureau.makkelijkemarkt.api.ApiGetAccounts;
import com.amsterdam.marktbureau.makkelijkemarkt.api.ApiPostLoginBasicId;
import com.amsterdam.marktbureau.makkelijkemarkt.api.MakkelijkeMarktApiService;
import com.amsterdam.marktbureau.makkelijkemarkt.data.MakkelijkeMarktProvider;
import com.google.gson.JsonObject;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;

import java.util.Date;
import java.util.concurrent.TimeUnit;

import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnItemSelected;
import retrofit2.Callback;
import retrofit2.Response;

/**
 *
 * @author marcolangebeeke
 */
public class LoginFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor>, Callback<JsonObject> {

    // use classname when logging
    private static final String LOG_TAG = LoginFragment.class.getSimpleName();

    // create unique update dialog fragment instance tag
    private static final String UPDATE_FRAGMENT_TAG = LOG_TAG + UpdateAvailableDialogFragment.class.getSimpleName()
            + "_TAG";

    // bind layout elements
    @Bind(R.id.account)
    Spinner mAccount;
    @Bind(R.id.password)
    TextView mPassword;
    @Bind(R.id.login_button)
    Button mLoginButton;
    @Bind(R.id.progressbar_accounts)
    ProgressBar mAccountsProgressBar;

    // unique id for the accounts loader
    private static final int ACCOUNTS_LOADER = 1;

    // cursoradapter for populating the account spinner with accounts from the database
    private SimpleCursorAdapter mAccountsAdapter;

    // keep the selected account id
    private int mSelectedAccountId;

    // keep a reference to toast messages
    private Toast mToast;

    // progress dialog for during authentication
    private ProgressDialog mLoginProcessDialog;

    /**
     * Constructor
     */
    public LoginFragment() {
    }

    /**
     * Set the login fragment layout and initialize the login logic
     * @param inflater inflater object to inflate the layout
     * @param container the parent view container
     * @param savedInstanceState fragment state bundle
     * @return the inflated view
     */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        // inflate the login fragment layout
        View mainView = inflater.inflate(R.layout.login_fragment, container, false);

        // bind the elements to the view
        ButterKnife.bind(this, mainView);

        if (savedInstanceState == null) {

            // check if there is a newer build of the app available
            Utility.checkForUpdate(getActivity(), UPDATE_FRAGMENT_TAG, true);

            // TODO: if there is no internet connection and accounts were never loaded: keep checking for an internet connection and try again

            // check time in hours since last fetched the accounts
            SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getContext());
            long diffInHours = getResources()
                    .getInteger(R.integer.makkelijkemarkt_api_accounts_fetch_interval_hours);
            if (settings.contains(getContext().getString(R.string.sharedpreferences_key_accounts_last_fetched))) {
                long lastFetchTimestamp = settings
                        .getLong(getContext().getString(R.string.sharedpreferences_key_accounts_last_fetched), 0);
                long differenceMs = new Date().getTime() - lastFetchTimestamp;
                diffInHours = TimeUnit.MILLISECONDS.toHours(differenceMs);
            }

            // update the local accounts by reloading them from the api
            if (diffInHours >= getResources()
                    .getInteger(R.integer.makkelijkemarkt_api_accounts_fetch_interval_hours)) {

                // show the progressbar
                mAccountsProgressBar.setVisibility(View.VISIBLE);

                // call the api
                ApiGetAccounts getAccounts = new ApiGetAccounts(getContext());
                if (!getAccounts.enqueue()) {
                    mAccountsProgressBar.setVisibility(View.GONE);
                }
            }
        }

        // create an adapter for the account spinner
        mAccountsAdapter = new SimpleCursorAdapter(getContext(), android.R.layout.simple_list_item_activated_1,
                null, new String[] { MakkelijkeMarktProvider.Account.COL_NAAM }, new int[] { android.R.id.text1 },
                0);

        // attach the adapter to the account spinner
        mAccount.setAdapter(mAccountsAdapter);

        // initiate loading the accounts from the database
        getLoaderManager().initLoader(ACCOUNTS_LOADER, null, this);

        // disable all caps for the button title
        mLoginButton.setTransformationMethod(null);

        // create the login progress dialog
        mLoginProcessDialog = new ProgressDialog(getContext());
        mLoginProcessDialog.setIndeterminate(true);
        mLoginProcessDialog
                .setIndeterminateDrawable(ContextCompat.getDrawable(getContext(), R.drawable.progressbar_circle));
        mLoginProcessDialog.setMessage(getString(R.string.login) + "...");
        mLoginProcessDialog.setCancelable(false);

        return mainView;
    }

    /**
     * When selecting an account in the dropdown update the selected account id and naam
     * @param position position in the list
     */
    @OnItemSelected(R.id.account)
    public void onItemSelected(int position) {

        // get the account id and naam from the selected item
        Cursor selectedAccount = (Cursor) mAccountsAdapter.getItem(position);
        mSelectedAccountId = selectedAccount
                .getInt(selectedAccount.getColumnIndex(MakkelijkeMarktProvider.Account.COL_ID));
        String naam = selectedAccount
                .getString(selectedAccount.getColumnIndex(MakkelijkeMarktProvider.Account.COL_NAAM));

        // add account id and naam to the shared preferences
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getContext());
        SharedPreferences.Editor editor = settings.edit();
        editor.putInt(getString(R.string.sharedpreferences_key_account_id), mSelectedAccountId);
        editor.putString(getString(R.string.sharedpreferences_key_account_naam), naam);
        editor.apply();
    }

    /**
     * OnClick on the login button authenticate the user with the api
     */
    @OnClick(R.id.login_button)
    public void authenticateAccount() {

        // if user entered a password try to authenticate with the api
        if (mPassword.getText().toString().trim().equals("")) {
            mToast = Utility.showToast(getContext(), mToast, getString(R.string.notice_login_enter_password));
        } else {

            // prepare the json payload for the post request from the entered login details
            JsonObject auth = new JsonObject();
            auth.addProperty(getString(R.string.makkelijkemarkt_api_login_payload_accound_id_name),
                    String.valueOf(mSelectedAccountId));
            auth.addProperty(getString(R.string.makkelijkemarkt_api_login_payload_password_name),
                    mPassword.getText().toString());

            // show progress dialog
            mLoginProcessDialog.show();

            // create a login post request and add the account details as json
            ApiPostLoginBasicId postLogin = new ApiPostLoginBasicId(getContext());
            postLogin.setPayload(auth);
            if (!postLogin.enqueue(this)) {
                mLoginProcessDialog.dismiss();
            }
        }
    }

    /**
     * Create the cursorloader that will load the accounts from the db
     * @param id the unique id given in the initloader call
     * @param args the arguments given in the initloader call
     * @return the cursor containing the accounts when loaded from the db
     */
    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {

        // create the loader
        CursorLoader loader = new CursorLoader(getActivity());
        loader.setUri(MakkelijkeMarktProvider.mUriAccount);
        loader.setProjection(
                new String[] { MakkelijkeMarktProvider.Account.COL_ID, MakkelijkeMarktProvider.Account.COL_NAAM });
        loader.setSortOrder(MakkelijkeMarktProvider.Account.COL_NAAM + " ASC");

        return loader;
    }

    /**
     * Add the loaded accounts into the accounts adapter when done loading from the db
     * @param loader the accounts loader attached to the spinner
     * @param data the loaded data
     */
    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {

        // populate the adapter with the loaded accounts
        mAccountsAdapter.swapCursor(data);

        // get the id of previously selected account from the shared preferences
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getContext());
        int accountId = settings.getInt(getContext().getString(R.string.sharedpreferences_key_account_id), -1);
        if (accountId != -1) {

            // update selected account id member var
            mSelectedAccountId = accountId;

            // select the account in the spinner dropdown
            if (data.moveToFirst()) {
                while (!data.isAfterLast()) {
                    if (data.getLong(
                            data.getColumnIndex(MakkelijkeMarktProvider.Account.COL_ID)) == mSelectedAccountId) {
                        mAccount.setSelection(data.getPosition());
                        break;
                    }
                    data.moveToNext();
                }
            }
        }
    }

    /**
     * Clear the accounts from the loader
     * @param loader adapter containing the accounts
     */
    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        mAccountsAdapter.swapCursor(null);
    }

    /**
     * Handle the response of the Api login post request
     * @param response the response of the post request, containing a gson object
     */
    @Override
    public void onResponse(Response<JsonObject> response) {

        // hide progress dialog
        mLoginProcessDialog.dismiss();

        if (response.isSuccess() && response.body() != null) {

            // get the api key from the response gson
            String uuid = response.body().get(getString(R.string.makkelijkemarkt_api_uuid_name)).getAsString();

            // store the uuid in the shared preferences
            SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getContext());
            SharedPreferences.Editor editor = settings.edit();
            editor.putString(getString(R.string.sharedpreferences_key_uuid), uuid);
            editor.apply();

            // start the api service
            Intent apiServiceIntent = new Intent(getContext(), MakkelijkeMarktApiService.class);
            getContext().startService(apiServiceIntent);

            // open the markten activity
            Intent intent = new Intent(getActivity(), MarktenActivity.class);
            startActivity(intent);

        } else if (response.code() == 401) {

            // 401 Unauthorised (wrong account / password)
            mToast = Utility.showToast(getContext(), mToast, getString(R.string.notice_login_password_invalid));

        } else if (response.code() == 423) {

            // 423 Locked (account locked)
            mToast = Utility.showToast(getContext(), mToast, getString(R.string.notice_login_account_locked));
        }
    }

    /**
     * On failure of the Api login pos request
     * @param t error
     */
    @Override
    public void onFailure(Throwable t) {

        // hide progress dialog
        mLoginProcessDialog.dismiss();
        mToast = Utility.showToast(getContext(), mToast, getString(R.string.notice_login_failed_connect));
    }

    /**
     * Handle response event from api get accounts request onresponse method to update our ui
     * @param event the received event
     */
    @Subscribe
    public void onGetAccountsResponseEvent(ApiGetAccounts.OnResponseEvent event) {

        // hide progressbar or show an error
        mAccountsProgressBar.setVisibility(View.GONE);
        if (event.mAccountCount == -1) {
            mToast = Utility.showToast(getContext(), mToast,
                    getString(R.string.error_accounts_fetch_failed) + ": " + event.mMessage);
        }
    }

    /**
     * Register eventbus handlers
     */
    @Override
    public void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    }

    /**
     * Unregister eventbus handlers
     */
    @Override
    public void onStop() {
        EventBus.getDefault().unregister(this);
        super.onStop();
    }
}