com.kaliturin.blacklist.fragments.ContactsFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.kaliturin.blacklist.fragments.ContactsFragment.java

Source

/*
 * Copyright (C) 2017 Anton Kaliturin <kaliturin@gmail.com>
 *
 * 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 com.kaliturin.blacklist.fragments;

import android.content.Context;
import android.database.Cursor;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SearchView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.kaliturin.blacklist.R;
import com.kaliturin.blacklist.activities.CustomFragmentActivity;
import com.kaliturin.blacklist.adapters.ContactsCursorAdapter;
import com.kaliturin.blacklist.utils.ButtonsBar;
import com.kaliturin.blacklist.utils.ContactsAccessHelper.ContactSourceType;
import com.kaliturin.blacklist.utils.DatabaseAccessHelper;
import com.kaliturin.blacklist.utils.DatabaseAccessHelper.Contact;
import com.kaliturin.blacklist.utils.DefaultSMSAppHelper;
import com.kaliturin.blacklist.utils.DialogBuilder;
import com.kaliturin.blacklist.utils.IdentifiersContainer;
import com.kaliturin.blacklist.utils.Permissions;
import com.kaliturin.blacklist.utils.Utils;

/**
 * Contacts fragment (black/white list)
 */

public class ContactsFragment extends Fragment implements FragmentArguments {
    private boolean defaultSMSAppPrompt = true;
    private ContactsCursorAdapter cursorAdapter = null;
    private ButtonsBar snackBar = null;
    private int contactType = 0;
    private String itemsFilter = null;
    private ListView listView = null;
    private int listPosition = 0;

    public ContactsFragment() {
        // Required empty public constructor
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        // set activity title
        Bundle arguments = getArguments();
        ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
        if (arguments != null && actionBar != null) {
            actionBar.setTitle(arguments.getString(TITLE));
        }
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Bundle arguments = getArguments();
        if (arguments != null) {
            contactType = arguments.getInt(CONTACT_TYPE, 0);
        }

        if (savedInstanceState != null) {
            listPosition = savedInstanceState.getInt(LIST_POSITION, 0);
        }

        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_contacts, container, false);
    }

    @Override
    public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Permissions.notifyIfNotGranted(getContext(), Permissions.WRITE_EXTERNAL_STORAGE);

        // snack bar
        snackBar = new ButtonsBar(view, R.id.three_buttons_bar);
        // "Cancel button" button
        snackBar.setButton(R.id.button_left, getString(R.string.CANCEL), new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                snackBar.dismiss();
                clearCheckedItems();
            }
        });
        // "Delete" button
        snackBar.setButton(R.id.button_center, getString(R.string.DELETE), new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                snackBar.dismiss();
                deleteCheckedItems();
            }
        });
        // "Select all" button
        snackBar.setButton(R.id.button_right, getString(R.string.SELECT_ALL), new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setAllItemsChecked();
            }
        });

        // cursor adapter
        cursorAdapter = new ContactsCursorAdapter(getContext());

        // on row click listener
        cursorAdapter.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View row) {
                if (cursorAdapter.hasCheckedItems()) {
                    snackBar.show();
                } else {
                    snackBar.dismiss();
                }
            }
        });

        // on row long click listener (receives clicked row)
        cursorAdapter.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View row) {
                // get contact from the clicked row
                final Contact contact = cursorAdapter.getContact(row);
                if (contact != null) {
                    // create and show menu dialog for actions with the contact
                    DialogBuilder dialog = new DialogBuilder(getContext());
                    dialog.setTitle(contact.name).
                    // add menu item of contact editing
                    addItem(R.string.Edit_contact, new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            // edit contact
                            editContact(contact.id);
                        }
                    }).
                    // add menu item of contact deletion
                    addItem(R.string.Remove_contact, new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            deleteContact(contact.id);
                            reloadItems(itemsFilter);
                        }
                    });
                    // add menu item of contact moving to opposite list
                    String itemTitle = (contact.type == Contact.TYPE_WHITE_LIST
                            ? getString(R.string.Move_to_black_list)
                            : getString(R.string.Move_to_white_list));
                    dialog.addItem(itemTitle, new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            moveContactToOppositeList(contact);
                            reloadItems(itemsFilter);
                        }
                    }).show();
                }
                return true;
            }
        });

        // add cursor listener to the list
        listView = (ListView) view.findViewById(R.id.contacts_list);
        listView.setAdapter(cursorAdapter);

        // on list empty comment
        TextView textEmptyView = (TextView) view.findViewById(R.id.text_empty);
        listView.setEmptyView(textEmptyView);

        // load the list view
        loadListViewItems(itemsFilter, false, listPosition);

        // prompt how to enable SMS-blocking
        showDefaultSMSAppPrompt();
    }

    @Override
    public void onDestroyView() {
        getLoaderManager().destroyLoader(0);
        super.onDestroyView();
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.main, menu);

        // tune menu options
        MenuItem itemSearch = menu.findItem(R.id.action_search);
        Utils.setMenuIconTint(getContext(), itemSearch, R.attr.colorAccent);
        itemSearch.setVisible(true);
        MenuItem itemAdd = menu.findItem(R.id.action_add);
        Utils.setMenuIconTint(getContext(), itemAdd, R.attr.colorAccent);
        itemAdd.setVisible(true);

        // get the view from search menu item
        SearchView searchView = (SearchView) MenuItemCompat.getActionView(itemSearch);
        searchView.setQueryHint(getString(R.string.Search_action));
        // set on text change listener
        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                return true;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                reloadItems(newText);
                return true;
            }
        });

        // on search cancelling
        // SearchView.OnCloseListener is not calling so use other way...
        MenuItemCompat.setOnActionExpandListener(itemSearch, new MenuItemCompat.OnActionExpandListener() {
            @Override
            public boolean onMenuItemActionExpand(MenuItem item) {
                return true;
            }

            @Override
            public boolean onMenuItemActionCollapse(MenuItem item) {
                reloadItems(null);
                return true;
            }
        });

        // item's 'add contact' on click listener
        itemAdd.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
            @Override
            public boolean onMenuItemClick(MenuItem item) {
                // show menu dialog
                showAddContactsMenuDialog();

                return true;
            }
        });

        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(LIST_POSITION, listView.getFirstVisiblePosition());
    }

    @Override
    public void onPause() {
        super.onPause();
        listPosition = listView.getFirstVisiblePosition();
    }

    //----------------------------------------------------

    // Deletes contact by id
    private void deleteContact(long id) {
        DatabaseAccessHelper db = DatabaseAccessHelper.getInstance(getContext());
        if (db != null) {
            db.deleteContact(id);
        }
    }

    // Move contact to opposite type list
    private void moveContactToOppositeList(Contact contact) {
        DatabaseAccessHelper db = DatabaseAccessHelper.getInstance(getContext());
        if (db != null) {
            db.moveContact(contact);
        }
    }

    // Clears all items selection
    private void clearCheckedItems() {
        if (cursorAdapter != null) {
            cursorAdapter.setAllItemsChecked(false);
        }
    }

    // Sets all items selected
    private void setAllItemsChecked() {
        if (cursorAdapter != null) {
            cursorAdapter.setAllItemsChecked(true);
        }
    }

    // Closes snack bar
    public boolean dismissSnackBar() {
        clearCheckedItems();
        return snackBar != null && snackBar.dismiss();
    }

    // Deletes checked items
    private void deleteCheckedItems() {
        int listPosition = listView.getFirstVisiblePosition();
        loadListViewItems(itemsFilter, true, listPosition);
    }

    // Reloads items
    private void reloadItems(String itemsFilter) {
        this.itemsFilter = itemsFilter;
        dismissSnackBar();

        int listPosition = listView.getFirstVisiblePosition();
        loadListViewItems(itemsFilter, false, listPosition);
    }

    // Loads SMS conversations to the list view
    private void loadListViewItems(String itemsFilter, boolean deleteItems, int listPosition) {
        if (!isAdded()) {
            return;
        }
        int loaderId = 0;
        ContactsLoaderCallbacks callbacks = new ContactsLoaderCallbacks(getContext(), contactType, cursorAdapter,
                itemsFilter, deleteItems, listView, listPosition);
        LoaderManager manager = getLoaderManager();
        if (manager.getLoader(loaderId) == null) {
            // init and run the items loader
            manager.initLoader(loaderId, null, callbacks);
        } else {
            // restart loader
            manager.restartLoader(loaderId, null, callbacks);
        }
    }

    // Opens fragment for contact editing
    private void editContact(long id) {
        Bundle arguments = new Bundle();
        arguments.putInt(CONTACT_ID, (int) id);
        arguments.putInt(CONTACT_TYPE, contactType);
        CustomFragmentActivity.show(getActivity(), getString(R.string.Editing_contact),
                AddOrEditContactFragment.class, arguments, 0);
    }

    //----------------------------------------------------

    // Contact items loader
    private static class ContactsLoader extends CursorLoader {
        private IdentifiersContainer deletingItems;
        private int contactType;
        private String itemsFilter;

        ContactsLoader(Context context, int contactType, String itemsFilter,
                @Nullable IdentifiersContainer deletingItems) {
            super(context);
            this.contactType = contactType;
            this.itemsFilter = itemsFilter;
            this.deletingItems = deletingItems;
        }

        @Override
        public Cursor loadInBackground() {
            DatabaseAccessHelper db = DatabaseAccessHelper.getInstance(getContext());
            if (db == null) {
                return null;
            }
            if (deletingItems != null) {
                db.deleteContacts(contactType, deletingItems, itemsFilter);
            }
            return db.getContacts(contactType, itemsFilter);
        }
    }

    // Contact items loader callbacks
    private static class ContactsLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> {
        private Context context;
        private int contactType;
        private String itemsFilter;
        private ContactsCursorAdapter cursorAdapter;
        private boolean deleteItems;
        private ListView listView;
        private int listPosition;

        ContactsLoaderCallbacks(Context context, int contactType, ContactsCursorAdapter cursorAdapter,
                String itemsFilter, boolean deleteItems, ListView listView, int listPosition) {
            this.context = context;
            this.itemsFilter = itemsFilter;
            this.contactType = contactType;
            this.cursorAdapter = cursorAdapter;
            this.deleteItems = deleteItems;
            this.listView = listView;
            this.listPosition = listPosition;
        }

        @Override
        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
            IdentifiersContainer deletingItems = null;
            if (deleteItems) {
                deletingItems = cursorAdapter.getCheckedItems().clone();
            }
            return new ContactsLoader(context, contactType, itemsFilter, deletingItems);
        }

        @Override
        public void onLoadFinished(Loader<Cursor> loader, final Cursor data) {
            cursorAdapter.changeCursor(data);

            // scroll list to the saved position
            listView.post(new Runnable() {
                @Override
                public void run() {
                    listView.setSelection(listPosition);
                }
            });
        }

        @Override
        public void onLoaderReset(Loader<Cursor> loader) {
            cursorAdapter.changeCursor(null);
        }
    }

    //----------------------------------------------------

    // Shows menu dialog of contacts adding
    private void showAddContactsMenuDialog() {
        // create and show menu dialog for actions with the contact
        DialogBuilder dialog = new DialogBuilder(getContext());
        dialog.setTitle(R.string.Add_contact).addItem(R.string.From_contacts_list, new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showAddContactsActivity(Permissions.READ_CONTACTS, ContactSourceType.FROM_CONTACTS,
                        R.string.List_of_contacts);
            }
        }).addItem(R.string.From_calls_list, new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showAddContactsActivity(Permissions.READ_CALL_LOG, ContactSourceType.FROM_CALLS_LOG,
                        R.string.List_of_calls);
            }
        }).addItem(R.string.From_SMS_list, new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showAddContactsActivity(Permissions.READ_SMS, ContactSourceType.FROM_SMS_LIST,
                        R.string.List_of_SMS);
            }
        });
        if (contactType == Contact.TYPE_WHITE_LIST) {
            dialog.addItem(R.string.From_Black_list, new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    showAddContactsActivity(Permissions.WRITE_EXTERNAL_STORAGE, ContactSourceType.FROM_BLACK_LIST,
                            R.string.Black_list);
                }
            });
        } else {
            dialog.addItem(R.string.From_White_list, new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    showAddContactsActivity(Permissions.WRITE_EXTERNAL_STORAGE, ContactSourceType.FROM_WHITE_LIST,
                            R.string.White_list);
                }
            });
        }
        dialog.addItem(R.string.Manually, new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showAddContactsActivity(Permissions.WRITE_EXTERNAL_STORAGE, null, R.string.Adding_contact);
            }
        }).show();
    }

    // Shows activity of of contacts adding
    private void showAddContactsActivity(String permission, ContactSourceType sourceType, @StringRes int titleId) {
        // if permission isn't granted
        if (Permissions.notifyIfNotGranted(getContext(), permission)) {
            return;
        }

        // permission is granted
        Bundle arguments = new Bundle();
        Class<? extends Fragment> fragmentClass;
        if (sourceType != null) {
            // fragment of adding contacts from sms-content / call log
            arguments.putInt(CONTACT_TYPE, contactType);
            arguments.putSerializable(SOURCE_TYPE, sourceType);
            arguments.putBoolean(SINGLE_NUMBER_MODE, true);
            fragmentClass = AddContactsFragment.class;
        } else {
            // fragment of adding contacts manually
            arguments.putInt(CONTACT_TYPE, contactType);
            fragmentClass = AddOrEditContactFragment.class;
        }

        // open activity with contacts adding fragment
        CustomFragmentActivity.show(getActivity(), getString(titleId), fragmentClass, arguments, 0);
    }

    // Prompts how to enable SMS-blocking
    private void showDefaultSMSAppPrompt() {
        if (defaultSMSAppPrompt && contactType == Contact.TYPE_BLACK_LIST) {
            defaultSMSAppPrompt = false;
            if (!DefaultSMSAppHelper.isDefault(getContext())) {
                Toast.makeText(getContext(), R.string.To_block_SMS_set_def_app, Toast.LENGTH_LONG).show();
            }
        }
    }
}