mchs.neverforget.NeverForgetActivity.java Source code

Java tutorial

Introduction

Here is the source code for mchs.neverforget.NeverForgetActivity.java

Source

/*      Copyright 2016 Marcello de Paula Ferreira Costa
    
    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at
    
    http://www.apache.org/licenses/LICENSE-2.0
    
    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License. */
package mchs.neverforget;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;

import mchs.neverforget.model.Book;
import mchs.neverforget.receivers.NeverForgetReceiver;
import mchs.neverforget.services.NotificationIntentService;
import mchs.neverforget.services.UpdateIntentService;
import mchs.neverforget.util.Crawler;
import mchs.neverforget.util.NeverForgetDatabaseAdapter;

public class NeverForgetActivity extends AppCompatActivity {
    private static final String GREETING = "GREETING";
    private static final String TAG = "NeverForgetActivity";
    private static final String SELECTED_BOOKS = "SELECTED_BOOKS";
    private static final int NO_BOOKS_RENEWED = 0;
    private static final int AT_LEAST_ONE_BOOK_RENEWED = 1;
    private static final int ALL_BOOKS_RENEWED = 2;
    private ArrayList<Book> bookArrayList = new ArrayList<>();
    private ListView booksListView;
    private TextView nameTextView;
    private Crawler crawler;
    private boolean[] isBookSelected;
    private NeverForgetDatabaseAdapter databaseAdapter;
    private ArrayAdapter<Book> bookListAdapter;
    private String greeting;

    private boolean isDatasetChanged = false;

    /* First function called when a activity is created */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_never_forget);
        nameTextView = (TextView) findViewById(R.id.nameTextView);

        Toolbar toolbar = (Toolbar) findViewById(R.id.app_bar);
        setSupportActionBar(toolbar);
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.setHomeButtonEnabled(true);
            actionBar.setDisplayHomeAsUpEnabled(true);
        }

        setupBookListViewListener();

        databaseAdapter = new NeverForgetDatabaseAdapter(this);
        crawler = Crawler.getInstance();

        if (savedInstanceState == null) {
            if (isFirstLogin(this)) {
                populateBooksList(true);
                isDatasetChanged = true;
                NeverForgetReceiver.broadcastFirstLogin(this);
                PreferenceManager.getDefaultSharedPreferences(this).edit()
                        .putBoolean(LoginScreenActivity.IS_FIRST_LOGIN_PREFS, false).apply();
            } else {
                populateBooksList(false);
            }
        }
    }

    protected static boolean isFirstLogin(Context context) {
        SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
        return sharedPreferences.getBoolean(LoginScreenActivity.IS_FIRST_LOGIN_PREFS, true);
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (isDatasetChanged) {
            updateDatabase();
            isDatasetChanged = false;
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBooleanArray(SELECTED_BOOKS, isBookSelected);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        isBookSelected = savedInstanceState.getBooleanArray(SELECTED_BOOKS);
        populateBooksList(false); /* False tells that it isn't first login */
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater menuInflater = getMenuInflater();
        menuInflater.inflate(R.menu.never_forget_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        switch (id) {
        case android.R.id.home:
            onBackPressed();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onBackPressed() {
        AlertDialog.Builder alertBuilder = new AlertDialog.Builder(NeverForgetActivity.this);
        alertBuilder.setTitle(R.string.nfa_logout_dialog_title).setMessage(R.string.nfa_logout_dialog_message)
                .setCancelable(false)
                .setPositiveButton(R.string.nfa_logout_dialog_yes_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        databaseAdapter.clearDatabase();
                        UpdateIntentService.startActionCancelRecurrentUpdateAlarm(NeverForgetActivity.this);
                        NotificationIntentService.cancelCustomRecurrentNotificationAlarm(getApplicationContext(),
                                1);
                        NotificationIntentService.cancelCustomRecurrentNotificationAlarm(getApplicationContext(),
                                2);
                        NotificationIntentService.cancelCustomRecurrentNotificationAlarm(getApplicationContext(),
                                3);
                        PreferenceManager.getDefaultSharedPreferences(NeverForgetActivity.this).edit()
                                .putBoolean(LoginScreenActivity.IS_LOGGED_IN, false).apply();
                        dialog.dismiss();
                        NeverForgetActivity.super.onBackPressed();
                    }
                }).setNegativeButton(R.string.nfa_logout_dialog_no_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
        alertBuilder.create().show();
    }

    private void updateDatabase() {
        databaseAdapter.clearDatabase();
        for (Book book : bookArrayList) {
            if (databaseAdapter.insertBook(book) > 0) {
                Log.v(TAG, "Book added to database");
            } else {
                Log.e(TAG, "Failed to add book to database");
            }
        }
    }

    private void setupInformativeColors() {
        @SuppressLint("SimpleDateFormat")
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yy");
        for (Book b : bookArrayList) {
            Calendar returnDate = new GregorianCalendar();
            Calendar currentDate = new GregorianCalendar();
            try {
                returnDate.setTime(sdf.parse(b.getReturnDate()));
                currentDate.setTime(sdf.parse(sdf.format(System.currentTimeMillis())));
                if (returnDate.before(currentDate)) {
                    b.setDrawableId(R.drawable.item_book_expired);
                } else {
                    int days = daysBetween(returnDate, currentDate);
                    if (days < 2) {
                        b.setDrawableId(R.drawable.item_book_red);
                    } else
                        b.setDrawableId(R.drawable.item_book);
                }
            } catch (ParseException e) {
                e.printStackTrace();

            }
        }
    }

    public static int daysBetween(Calendar day1, Calendar day2) {
        Calendar dayOne = (Calendar) day1.clone(), dayTwo = (Calendar) day2.clone();

        if (dayOne.get(Calendar.YEAR) == dayTwo.get(Calendar.YEAR)) {
            return Math.abs(dayOne.get(Calendar.DAY_OF_YEAR) - dayTwo.get(Calendar.DAY_OF_YEAR));
        } else {
            if (dayTwo.get(Calendar.YEAR) > dayOne.get(Calendar.YEAR)) {
                //swap them
                Calendar temp = dayOne;
                dayOne = dayTwo;
                dayTwo = temp;
            }
            int extraDays = 0;

            int dayOneOriginalYearDays = dayOne.get(Calendar.DAY_OF_YEAR);

            while (dayOne.get(Calendar.YEAR) > dayTwo.get(Calendar.YEAR)) {
                dayOne.add(Calendar.YEAR, -1);
                // getActualMaximum() important for leap years
                extraDays += dayOne.getActualMaximum(Calendar.DAY_OF_YEAR);
            }

            return extraDays - dayTwo.get(Calendar.DAY_OF_YEAR) + dayOneOriginalYearDays;
        }
    }

    private void setupBookListViewListener() {
        booksListView = (ListView) findViewById(R.id.booksListView);
        booksListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(final AdapterView<?> parent, final View view, final int position, long id) {
                booksListView.post(new Runnable() {
                    @Override
                    public void run() {
                        if (isBookSelected[position]) {
                            view.setBackgroundColor(
                                    ContextCompat.getColor(getApplicationContext(), R.color.colorListView));
                            isBookSelected[position] = false;
                            Toast.makeText(getApplicationContext(), R.string.nfa_book_removed, Toast.LENGTH_SHORT)
                                    .show();
                        } else {
                            isBookSelected[position] = true;
                            view.setBackgroundColor(
                                    ContextCompat.getColor(getApplicationContext(), R.color.colorSelected));
                            Toast.makeText(getApplicationContext(), R.string.nfa_book_added, Toast.LENGTH_SHORT)
                                    .show();
                        }
                    }
                });

            }
        });
    }

    private void populateBooksListView() {
        bookListAdapter = new BookListAdapter();
        booksListView.setAdapter(bookListAdapter);
    }

    private void populateBooksList(boolean isFirstLogin) {
        if (isFirstLogin) {
            Thread getBooksThread = new Thread() {
                public void run() {
                    bookArrayList.addAll(crawler.getBooksListPostLogin());
                    isBookSelected = new boolean[bookArrayList.size()];
                    setupInformativeColors();
                    greeting = "Ol, " + crawler.getName();
                    PreferenceManager.getDefaultSharedPreferences(NeverForgetActivity.this).edit()
                            .putString(GREETING, greeting).apply();

                    booksListView.post(new Runnable() {
                        @Override
                        public void run() {
                            nameTextView.setText(greeting);
                            populateBooksListView();
                        }
                    });
                }
            };
            getBooksThread.start();
        } else {
            bookArrayList.addAll(databaseAdapter.getBooks());

            setupInformativeColors();
            nameTextView.setText(PreferenceManager.getDefaultSharedPreferences(this).getString(GREETING, "Ol!"));
            populateBooksListView();
        }
    }

    /* Callback function that is called from portrait mode by clicking a button*/
    public void renewSelectedBooks(View view) {
        AlertDialog.Builder alertBuilder = new AlertDialog.Builder(NeverForgetActivity.this);
        alertBuilder.setTitle(R.string.action_renew_selected)
                .setMessage(getString(R.string.nfa_renew_selected_question)).setCancelable(false)
                .setPositiveButton(R.string.nfa_logout_dialog_yes_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        renewSelectedBooks();
                    }
                }).setNegativeButton(R.string.nfa_logout_dialog_no_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
        alertBuilder.create().show();
    }

    /* Callback function that is called from landscape mode by clicking the action bar */
    public void renewSelectedBooks(MenuItem menuItem) {
        AlertDialog.Builder alertBuilder = new AlertDialog.Builder(NeverForgetActivity.this);
        alertBuilder.setTitle(R.string.action_renew_selected)
                .setMessage(getString(R.string.nfa_renew_selected_question)).setCancelable(false)
                .setPositiveButton(R.string.nfa_logout_dialog_yes_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        renewSelectedBooks();
                    }
                }).setNegativeButton(R.string.nfa_logout_dialog_no_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
        alertBuilder.create().show();
    }

    /* Function that implements "Renew Selected" feature */
    public void renewSelectedBooks() {
        Thread renewSelectedThread = new Thread() {
            @Override
            public void run() {
                ArrayList<Book> temp = crawler.renewSelected(isBookSelected, bookArrayList);
                if (temp == null) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(getApplicationContext(), R.string.nfa_renew_failed, Toast.LENGTH_SHORT)
                                    .show();
                        }
                    });
                } else {
                    int returnFlag = replaceBooks(temp);
                    if (returnFlag == NO_BOOKS_RENEWED) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(getApplicationContext(), R.string.nfa_no_books_renewed,
                                        Toast.LENGTH_SHORT).show();
                            }
                        });
                    } else if (returnFlag == ALL_BOOKS_RENEWED) {
                        isDatasetChanged = true;
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                bookListAdapter.notifyDataSetChanged();
                                Toast.makeText(getApplicationContext(), R.string.nfa_renew_at_least_one_selected,
                                        Toast.LENGTH_SHORT).show();
                            }
                        });
                    } else {
                        isDatasetChanged = true;
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                bookListAdapter.notifyDataSetChanged();
                                Toast.makeText(getApplicationContext(), R.string.nfa_renewed, Toast.LENGTH_SHORT)
                                        .show();
                            }
                        });
                    }
                }
            }
        };
        renewSelectedThread.start();
    }

    private int replaceBooks(ArrayList<Book> renewedBookList) {
        int size = bookArrayList.size();
        int returnValue = NO_BOOKS_RENEWED;
        int count = 0;
        boolean[] shouldNotPersist = new boolean[size];
        for (int i = 0; i < size; i++) {
            Book book = bookArrayList.get(i);
            for (Book renewedBook : renewedBookList)
                if (book.getTitle().equals(renewedBook.getTitle())
                        && !book.getReturnDate().equals(renewedBook.getReturnDate())) {
                    returnValue++;
                    shouldNotPersist[i] = true;
                }
        }

        for (int i = 0; i < size; i++) {
            if (!shouldNotPersist[i])
                renewedBookList.add(bookArrayList.get(i));
        }

        for (boolean b : isBookSelected) {
            if (b)
                count++;
        }

        bookArrayList.clear();
        bookArrayList.addAll(renewedBookList);

        return (count == returnValue) ? ALL_BOOKS_RENEWED : AT_LEAST_ONE_BOOK_RENEWED;
    }

    /* Callback function that is called from portrait mode by clicking a button*/
    public void renewAllBooks(View view) {
        AlertDialog.Builder alertBuilder = new AlertDialog.Builder(NeverForgetActivity.this);
        alertBuilder.setTitle(R.string.action_renew_all).setMessage(getString(R.string.nfa_renew_all_question))
                .setCancelable(false)
                .setPositiveButton(R.string.nfa_logout_dialog_yes_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        renewAllBooks();
                    }
                }).setNegativeButton(R.string.nfa_logout_dialog_no_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
        alertBuilder.create().show();
    }

    /* Callback function that is called from landscape mode by clicking the action bar */
    public void renewAllBooks(MenuItem menuItem) {
        AlertDialog.Builder alertBuilder = new AlertDialog.Builder(NeverForgetActivity.this);
        alertBuilder.setTitle(R.string.action_renew_all).setMessage(getString(R.string.nfa_renew_all_question))
                .setCancelable(false)
                .setPositiveButton(R.string.nfa_logout_dialog_yes_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        renewAllBooks();
                    }
                }).setNegativeButton(R.string.nfa_logout_dialog_no_button, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                    }
                });
        alertBuilder.create().show();
    }

    /* Function that implements "Renew All" feature */
    public void renewAllBooks() {
        Thread renewAllThread = new Thread() {
            @Override
            public void run() {
                ArrayList<Book> temp = crawler.renewAll();
                if (temp == null) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(NeverForgetActivity.this, R.string.nfa_renew_failed, Toast.LENGTH_LONG)
                                    .show();
                        }
                    });
                } else {
                    int returnFlag = replaceBooks(temp);
                    if (returnFlag == NO_BOOKS_RENEWED) {
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                Toast.makeText(getApplicationContext(), R.string.nfa_no_books_renewed,
                                        Toast.LENGTH_SHORT).show();
                            }
                        });
                    } else if (returnFlag == ALL_BOOKS_RENEWED) {
                        isDatasetChanged = true;
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                bookListAdapter.notifyDataSetChanged();
                                Toast.makeText(getApplicationContext(), R.string.nfa_renew_at_least_one,
                                        Toast.LENGTH_SHORT).show();
                            }
                        });
                    } else {
                        isDatasetChanged = true;
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                bookListAdapter.notifyDataSetChanged();
                                Toast.makeText(getApplicationContext(), R.string.nfa_renewed, Toast.LENGTH_SHORT)
                                        .show();
                            }
                        });
                    }
                }
            }
        };
        renewAllThread.start();
    }

    /* Callback function that can be called from both portrait and landscape mode and implements
     * a intentional update of the book list */
    public void updateBooks(MenuItem item) {
        Thread updateBooksListThread = new Thread() {
            public void run() {
                ArrayList<Book> temp = crawler.updateBooksList();
                if (temp == null) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(getApplicationContext(), R.string.nfa_update_failed, Toast.LENGTH_LONG)
                                    .show();
                        }
                    });
                } else {
                    bookArrayList.clear();
                    bookArrayList.addAll(temp);
                    isDatasetChanged = true;
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            setupInformativeColors();
                            bookListAdapter.notifyDataSetChanged();
                            Toast.makeText(getApplicationContext(), R.string.nfa_updated, Toast.LENGTH_LONG).show();
                        }
                    });
                }
            }
        };
        updateBooksListThread.start();
    }

    /* Callback function that can be called from both portrait and landscape mode.
     * startSettingsActivity is responsible of starting the settings activity as
     * its name says */
    public void startSettingsActivity(MenuItem item) {
        Intent neverForgetIntent = new Intent(NeverForgetActivity.this, SettingsActivity.class);
        startActivity(neverForgetIntent);
    }

    public void startAboutActivity(MenuItem item) {
        Intent intent = new Intent(this, AboutActivity.class);
        startActivity(intent);
    }

    public void startHelpActivity(MenuItem item) {
        Intent intent = new Intent(this, HelpActivity.class);
        startActivity(intent);
    }

    /* Custom array adapter implementation became required when a custom layout
     * item was chose to be used. It fills all fields correctly and is also
     * responsible to check if a item was previously selected in a scenario
     * where the activity was re-built due to a orientation change for example */
    private class BookListAdapter extends ArrayAdapter<Book> {

        public BookListAdapter() {
            super(NeverForgetActivity.this, R.layout.item_book, bookArrayList);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View bookView = convertView;
            if (bookView == null) {
                bookView = getLayoutInflater().inflate(R.layout.item_book, parent, false);
            }
            /* Find current book */
            final Book currentBook = bookArrayList.get(position);

            /* Fill view's fields with proper gathered information */
            TextView textViewAuthor = (TextView) bookView.findViewById(R.id.item_book_author);
            textViewAuthor.setText(currentBook.getAuthor());
            TextView textViewTitle = (TextView) bookView.findViewById(R.id.item_book_title);
            textViewTitle.setText(currentBook.getTitle());
            TextView textViewReturnDate = (TextView) bookView.findViewById(R.id.item_book_return_date);
            textViewReturnDate.setText(currentBook.getReturnDate());
            ImageView imageView = (ImageView) bookView.findViewById(R.id.item_book_image_view);
            imageView.setImageDrawable(ContextCompat.getDrawable(getContext(), currentBook.getDrawableId()));
            if (isBookSelected != null) {
                if (isBookSelected[position])
                    bookView.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.colorSelected));
            }
            return bookView;
        }
    }
}