com.money.manager.ex.common.CategoryListFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.money.manager.ex.common.CategoryListFragment.java

Source

/*
 * Copyright (C) 2012-2016 The Android Money Manager Ex Project Team
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.money.manager.ex.common;

import android.app.Activity;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.text.TextUtils;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ExpandableListView;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.Toast;

import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.mikepenz.google_material_typeface_library.GoogleMaterial;
import com.mikepenz.iconics.IconicsDrawable;
import com.mikepenz.mmex_icon_font_typeface_library.MMXIconFont;
import com.money.manager.ex.Constants;
import com.money.manager.ex.R;
import com.money.manager.ex.adapter.CategoryExpandableListAdapter;
import com.money.manager.ex.core.ContextMenuIds;
import com.money.manager.ex.core.UIHelper;
import com.money.manager.ex.datalayer.CategoryRepository;
import com.money.manager.ex.datalayer.Select;
import com.money.manager.ex.datalayer.SubcategoryRepository;
import com.money.manager.ex.domainmodel.Category;
import com.money.manager.ex.domainmodel.Subcategory;
import com.money.manager.ex.servicelayer.CategoryService;
import com.money.manager.ex.core.Core;
import com.money.manager.ex.database.QueryCategorySubCategory;
import com.money.manager.ex.database.SQLTypeTransaction;
import com.money.manager.ex.search.CategorySub;
import com.money.manager.ex.search.SearchActivity;
import com.money.manager.ex.search.SearchParameters;
import com.money.manager.ex.settings.AppSettings;
import com.shamanland.fonticon.FontIconDrawable;

import org.parceler.Parcels;

import java.text.Normalizer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Categories list fragment. Used in Main Activity for editing of categories, and own activity
 * when selecting the category for a transaction.
 */
public class CategoryListFragment extends BaseExpandableListFragment
        implements LoaderManager.LoaderCallbacks<Cursor> {

    public String mAction = Intent.ACTION_EDIT;
    public Integer requestId;

    private static final int ID_LOADER_CATEGORYSUB = 0;

    private static final String KEY_ID_GROUP = "CategorySubCategory:idGroup";
    private static final String KEY_ID_CHILD = "CategorySubCategory:idChild";
    private static final String KEY_CUR_FILTER = "CategorySubCategory:curFilter";
    // table or query
    private static QueryCategorySubCategory mQuery;
    private int mLayout;
    private int mIdGroupChecked = ExpandableListView.INVALID_POSITION;
    private int mIdChildChecked = ExpandableListView.INVALID_POSITION;

    private List<Category> mCategories;
    private HashMap<Category, List<QueryCategorySubCategory>> mSubCategories;

    private ArrayList<Integer> mPositionToExpand;
    private String mCurFilter;

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // create category adapter
        mQuery = new QueryCategorySubCategory(getActivity());

        mCategories = new ArrayList<>();
        mSubCategories = new HashMap<>();
        mPositionToExpand = new ArrayList<>();

        if (savedInstanceState != null) {
            restoreInstanceState(savedInstanceState);
        }

        setShowMenuItemSearch(true);

        setHasOptionsMenu(true);

        // Focus on search menu if set in preferences.
        AppSettings settings = new AppSettings(getActivity());
        boolean focusOnSearch = settings.getBehaviourSettings().getFilterInSelectors();
        setMenuItemSearchIconified(!focusOnSearch);

        setEmptyText(getActivity().getResources().getString(R.string.category_empty_list));

        /*
        Define the layout.
        Show category selector (arrow) when used as a picker.
        Show simple list when opened independently.
        */
        mLayout = R.layout.simple_expandable_list_item_selector;

        // manage context menu
        registerForContextMenu(getExpandableListView());

        getExpandableListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        setListShown(false);

        addListClickHandlers();

        // start loader
        getLoaderManager().initLoader(ID_LOADER_CATEGORYSUB, null, this);

        setFloatingActionButtonVisible(true);
        setFloatingActionButtonAttachListView(true);

        // Hide default group indicator
        getExpandableListView().setGroupIndicator(null);
    }

    private void restoreInstanceState(Bundle savedInstanceState) {
        if (savedInstanceState.containsKey(KEY_ID_GROUP)) {
            mIdGroupChecked = savedInstanceState.getInt(KEY_ID_GROUP);
        }
        if (savedInstanceState.containsKey(KEY_ID_CHILD)) {
            mIdChildChecked = savedInstanceState.getInt(KEY_ID_CHILD);
        }
        if (savedInstanceState.containsKey(KEY_CUR_FILTER)) {
            mCurFilter = savedInstanceState.getString(KEY_CUR_FILTER, "");
        }
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;

        int type = ExpandableListView.getPackedPositionType(info.packedPosition);
        int group = ExpandableListView.getPackedPositionGroup(info.packedPosition);
        int child = ExpandableListView.getPackedPositionChild(info.packedPosition);

        if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
            menu.setHeaderTitle(mCategories.get(group).getName());
        } else if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
            QueryCategorySubCategory subCategory = mSubCategories.get(mCategories.get(group)).get(child);
            menu.setHeaderTitle(
                    subCategory.getCategName().toString() + ": " + subCategory.getSubcategoryName().toString());
        }

        // context menu from resource
        menu.add(Menu.NONE, ContextMenuIds.EDIT.getId(), Menu.NONE, getString(R.string.edit));
        menu.add(Menu.NONE, ContextMenuIds.DELETE.getId(), Menu.NONE, getString(R.string.delete));
        menu.add(Menu.NONE, ContextMenuIds.VIEW_TRANSACTIONS.getId(), Menu.NONE,
                getString(R.string.view_transactions));
    }

    @Override
    public boolean onContextItemSelected(android.view.MenuItem item) {
        ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) item
                .getMenuInfo();

        int type = ExpandableListView.getPackedPositionType(info.packedPosition);
        int group = ExpandableListView.getPackedPositionGroup(info.packedPosition);
        int child = ExpandableListView.getPackedPositionChild(info.packedPosition);

        CategorySub categoryIds = new CategorySub();
        categoryIds.categId = Constants.NOT_SET;
        categoryIds.categName = "";
        categoryIds.subCategId = Constants.NOT_SET;
        categoryIds.subCategName = "";

        if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
            categoryIds.categId = mCategories.get(group).getId();
            categoryIds.categName = mCategories.get(group).getName();
        } else if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
            categoryIds.categId = mSubCategories.get(mCategories.get(group)).get(child).getCategId();
            categoryIds.subCategId = mSubCategories.get(mCategories.get(group)).get(child).getSubCategId();
            categoryIds.subCategName = mSubCategories.get(mCategories.get(group)).get(child).getSubcategoryName()
                    .toString();
        }
        // manage select menu
        ContextMenuIds menuId = ContextMenuIds.get(item.getItemId());
        switch (menuId) {
        case EDIT:
            if (categoryIds.subCategId == ExpandableListView.INVALID_POSITION) {
                showDialogEditCategoryName(SQLTypeTransaction.UPDATE, categoryIds.categId, categoryIds.categName);
            } else {
                showDialogEditSubCategoryName(SQLTypeTransaction.UPDATE, categoryIds.categId,
                        categoryIds.subCategId, categoryIds.subCategName);
            }
            break;

        case DELETE:
            showDialogDeleteCategorySub(categoryIds);
            break;

        case VIEW_TRANSACTIONS: // view transactions
            SearchParameters parameters = new SearchParameters();
            parameters.category = categoryIds;

            showSearchActivityFor(parameters);
        }
        return false;
    }

    @Override
    public boolean onQueryTextChange(String newText) {
        // Called when the action bar search text has changed.  Update
        // the search filter, and restart the loader to do a new query
        // with this filter.
        mCurFilter = !TextUtils.isEmpty(newText) ? newText : null;
        restartLoader();
        return true;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        if (getExpandableListAdapter() != null && getExpandableListAdapter().getGroupCount() > 0) {
            outState.putInt(KEY_ID_GROUP,
                    ((CategoryExpandableListAdapter) getExpandableListAdapter()).getIdGroupChecked());
            outState.putInt(KEY_ID_CHILD,
                    ((CategoryExpandableListAdapter) getExpandableListAdapter()).getIdChildChecked());
            outState.putString(KEY_CUR_FILTER, mCurFilter);
        }
    }

    // Data loader

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        switch (id) {
        case ID_LOADER_CATEGORYSUB:
            // update id selected
            if (getExpandableListAdapter() != null && getExpandableListAdapter().getGroupCount() > 0) {
                CategoryExpandableListAdapter adapter = (CategoryExpandableListAdapter) getExpandableListAdapter();
                mIdGroupChecked = adapter.getIdGroupChecked();
                mIdChildChecked = adapter.getIdChildChecked();
            }
            // clear arraylist and hashmap
            mCategories.clear();
            mSubCategories.clear();

            // load data
            String whereClause = null;
            String selectionArgs[] = null;
            if (!TextUtils.isEmpty(mCurFilter)) {
                whereClause = QueryCategorySubCategory.CATEGNAME + " LIKE ? OR "
                        + QueryCategorySubCategory.SUBCATEGNAME + " LIKE ?";
                selectionArgs = new String[] { mCurFilter + "%", mCurFilter + "%" };
            }
            Select query = new Select(mQuery.getAllColumns()).where(whereClause, selectionArgs)
                    .orderBy(QueryCategorySubCategory.CATEGNAME + ", " + QueryCategorySubCategory.SUBCATEGNAME);

            return new MmxCursorLoader(getActivity(), mQuery.getUri(), query);
        }
        return null;
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        switch (loader.getId()) {
        case ID_LOADER_CATEGORYSUB:
            // clear the data storage collections.
            mCategories.clear();
            mSubCategories.clear();
        }
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        switch (loader.getId()) {
        case ID_LOADER_CATEGORYSUB:
            setListAdapter(getAdapter(data));

            if (isResumed()) {
                setListShown(true);

                boolean noData = data == null || data.getCount() <= 0;
                if (noData && getFloatingActionButton() != null) {
                    getFloatingActionButton().show(true);
                }
            } else {
                setListShownNoAnimation(true);
            }

            for (int i = 0; i < mPositionToExpand.size(); i++) {
                getExpandableListView().expandGroup(mPositionToExpand.get(i));
            }
        }
    }

    // Other

    @Override
    protected void setResult() {
        if (Intent.ACTION_PICK.equals(mAction)) {
            if (getExpandableListAdapter() == null)
                return;

            Intent result = null;

            if (getExpandableListAdapter() instanceof CategoryExpandableListAdapter) {
                CategoryExpandableListAdapter adapter = (CategoryExpandableListAdapter) getExpandableListAdapter();
                int categId = adapter.getIdGroupChecked();
                int subCategId = adapter.getIdChildChecked();

                if (categId == ExpandableListView.INVALID_POSITION)
                    return;

                for (int groupIndex = 0; groupIndex < mCategories.size(); groupIndex++) {
                    if (mCategories.get(groupIndex).getId() == categId) {
                        // Get subcategory
                        if (subCategId != ExpandableListView.INVALID_POSITION) {
                            for (int child = 0; child < mSubCategories.get(mCategories.get(groupIndex))
                                    .size(); child++) {
                                if (mSubCategories.get(mCategories.get(groupIndex)).get(child)
                                        .getSubCategId() == subCategId) {
                                    result = new Intent();
                                    result.putExtra(CategoryListActivity.INTENT_RESULT_CATEGID, categId);
                                    result.putExtra(CategoryListActivity.INTENT_RESULT_CATEGNAME, mSubCategories
                                            .get(mCategories.get(groupIndex)).get(child).getCategName().toString());
                                    result.putExtra(CategoryListActivity.INTENT_RESULT_SUBCATEGID, subCategId);
                                    result.putExtra(CategoryListActivity.INTENT_RESULT_SUBCATEGNAME,
                                            mSubCategories.get(mCategories.get(groupIndex)).get(child)
                                                    .getSubcategoryName().toString());
                                    break;
                                }
                            }
                        } else {
                            result = new Intent();
                            result.putExtra(CategoryListActivity.INTENT_RESULT_CATEGID, categId);
                            result.putExtra(CategoryListActivity.INTENT_RESULT_CATEGNAME,
                                    mCategories.get(groupIndex).getName());
                            result.putExtra(CategoryListActivity.INTENT_RESULT_SUBCATEGID, subCategId);
                            result.putExtra(CategoryListActivity.INTENT_RESULT_SUBCATEGNAME, "");
                        }
                    }
                }
            }

            if (result != null) {
                result.putExtra(CategoryListActivity.KEY_REQUEST_ID, this.requestId);

                getActivity().setResult(Activity.RESULT_OK, result);
            } else {
                getActivity().setResult(Activity.RESULT_CANCELED);
            }
        }
    }

    @Override
    public String getSubTitle() {
        return getString(R.string.categories);
    }

    @Override
    public void onFloatingActionButtonClickListener() {
        showTypeSelectorDialog();
    }

    public CategoryExpandableListAdapter getAdapter(Cursor data) {
        if (data == null)
            return null;

        mCategories.clear();
        mSubCategories.clear();
        mPositionToExpand.clear();
        // create core and fixed string filter to highlight
        Core core = new Core(getActivity().getApplicationContext());
        String filter = mCurFilter != null ? mCurFilter.replace("%", "") : "";

        int key = -1;
        List<QueryCategorySubCategory> listSubCategories = null;

        // reset cursor if getting back on the fragment.
        if (data.getPosition() > 0) {
            data.moveToPosition(Constants.NOT_SET);
        }

        while (data.moveToNext()) {
            if (key != data.getInt(data.getColumnIndex(QueryCategorySubCategory.CATEGID))) {
                // check if listCategories > 0
                if (mCategories.size() > 0 && listSubCategories != null) {
                    mSubCategories.put(mCategories.get(mCategories.size() - 1), listSubCategories);
                }
                // update key
                key = data.getInt(data.getColumnIndex(QueryCategorySubCategory.CATEGID));

                // create instance category
                Category category = new Category();
                category.setId(data.getInt(data.getColumnIndex(QueryCategorySubCategory.CATEGID)));
                category.setName(core
                        .highlight(filter, data.getString(data.getColumnIndex(QueryCategorySubCategory.CATEGNAME)))
                        .toString());

                // add list
                mCategories.add(category);
                listSubCategories = new ArrayList<>();
            }

            if (data.getInt(data.getColumnIndex(QueryCategorySubCategory.SUBCATEGID)) != Constants.NOT_SET) {
                QueryCategorySubCategory subCategory = new QueryCategorySubCategory(getActivity());
                // subcategory
                subCategory.setSubCategId(data.getInt(data.getColumnIndex(QueryCategorySubCategory.SUBCATEGID)));
                subCategory.setSubcategoryName(core.highlight(filter,
                        data.getString(data.getColumnIndex(QueryCategorySubCategory.SUBCATEGNAME))));
                subCategory.setCategId(data.getInt(data.getColumnIndex(QueryCategorySubCategory.CATEGID)));
                subCategory.setCategName(core.highlight(filter,
                        data.getString(data.getColumnIndex(QueryCategorySubCategory.CATEGNAME))));
                // add to hashmap
                listSubCategories.add(subCategory);
                // check if expand group
                if (!TextUtils.isEmpty(filter)) {
                    String normalizedText = Normalizer
                            .normalize(subCategory.getSubcategoryName(), Normalizer.Form.NFD)
                            .replaceAll("\\p{InCombiningDiacriticalMarks}+", "").toLowerCase();
                    if ((normalizedText.indexOf(filter) >= 0)
                            && (!mPositionToExpand.contains(mCategories.size() - 1))) {
                        mPositionToExpand.add(mCategories.size() - 1);
                    }
                }
            }
        }
        if (mCategories.size() > 0 && listSubCategories != null) {
            mSubCategories.put(mCategories.get(mCategories.size() - 1), listSubCategories);
        }

        boolean showSelector = mAction.equals(Intent.ACTION_PICK);
        CategoryExpandableListAdapter adapter = new CategoryExpandableListAdapter(getActivity(), mLayout,
                mCategories, mSubCategories, showSelector);
        adapter.setIdChildChecked(mIdGroupChecked, mIdChildChecked);
        return adapter;
    }

    // Private

    /**
     * Restart loader to view data
     */
    private void restartLoader() {
        getLoaderManager().restartLoader(ID_LOADER_CATEGORYSUB, null, this);
    }

    /**
     * Show alter binaryDialog confirm delete category or sub category
     */
    private void showDialogDeleteCategorySub(final CategorySub categoryIds) {
        boolean canDelete;
        CategoryService service = new CategoryService(getActivity());
        ContentValues values = new ContentValues();

        if (categoryIds.subCategId <= 0) {
            values.put(Category.CATEGID, categoryIds.categId);
            canDelete = !service.isCategoryUsed(categoryIds.categId);
        } else {
            values.put(Subcategory.SUBCATEGID, categoryIds.subCategId);
            canDelete = !service.isSubcategoryUsed(categoryIds.categId);
        }
        if (!(canDelete)) {
            new MaterialDialog.Builder(getContext()).title(R.string.attention)
                    .icon(new UIHelper(getActivity()).getIcon(GoogleMaterial.Icon.gmd_warning))
                    .content(R.string.category_can_not_deleted).positiveText(android.R.string.ok)
                    .onPositive(new MaterialDialog.SingleButtonCallback() {
                        @Override
                        public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
                            dialog.dismiss();
                        }
                    }).build().show();
            return;
        }

        // Prompt for deletion.
        new MaterialDialog.Builder(getContext()).title(R.string.delete_category)
                .icon(new UIHelper(getActivity()).getIcon(GoogleMaterial.Icon.gmd_warning))
                .content(R.string.confirmDelete).positiveText(android.R.string.ok)
                .onPositive(new MaterialDialog.SingleButtonCallback() {
                    @Override
                    public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
                        int rowsDelete;
                        if (categoryIds.subCategId <= 0) {
                            CategoryRepository repo = new CategoryRepository(getActivity());
                            rowsDelete = getActivity().getContentResolver().delete(repo.getUri(),
                                    Category.CATEGID + "=" + categoryIds.categId, null);
                        } else {
                            SubcategoryRepository repo = new SubcategoryRepository(getActivity());

                            rowsDelete = getActivity().getContentResolver().delete(repo.getUri(),
                                    Subcategory.CATEGID + "=" + categoryIds.categId + " AND "
                                            + Subcategory.SUBCATEGID + "=" + categoryIds.subCategId,
                                    null);
                        }
                        if (rowsDelete == 0) {
                            Toast.makeText(getActivity(), R.string.db_delete_failed, Toast.LENGTH_SHORT).show();
                        }
                        // restart loader
                        restartLoader();
                    }
                }).negativeText(android.R.string.cancel).onNegative(new MaterialDialog.SingleButtonCallback() {
                    @Override
                    public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
                        //                        dialog.dismiss();
                        dialog.cancel();
                    }
                }).build().show();
    }

    /**
     * Show alter binaryDialog, for create or edit new category
     */
    private void showDialogEditCategoryName(final SQLTypeTransaction type, final int categoryId,
            final CharSequence categoryName) {
        // inflate view
        View viewDialog = LayoutInflater.from(getActivity()).inflate(R.layout.dialog_new_edit_category, null);

        final EditText edtCategName = (EditText) viewDialog.findViewById(R.id.editTextCategName);
        // set category description
        edtCategName.setText(categoryName);
        if (!TextUtils.isEmpty(categoryName)) {
            edtCategName.setSelection(categoryName.length());
        }

        int titleId = type.equals(SQLTypeTransaction.INSERT) ? R.string.add_category : R.string.edit_categoryName;

        new MaterialDialog.Builder(getContext()).customView(viewDialog, true)
                .icon(FontIconDrawable.inflate(getContext(), R.xml.ic_tag)).title(titleId)
                .positiveText(android.R.string.ok).onPositive(new MaterialDialog.SingleButtonCallback() {
                    @Override
                    public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
                        // get category description
                        String name = edtCategName.getText().toString();
                        CategoryService service = new CategoryService(getActivity());

                        switch (type) {
                        case INSERT:
                            int insertResult = service.createNew(name);

                            if (insertResult <= 0) {
                                Toast.makeText(getActivity(), R.string.db_insert_failed, Toast.LENGTH_SHORT).show();
                            }
                            break;
                        case UPDATE:
                            int updateResult = service.update(categoryId, name);
                            if (updateResult <= 0) {
                                Toast.makeText(getActivity(), R.string.db_update_failed, Toast.LENGTH_SHORT).show();
                            }
                            break;
                        }
                        // restart loader
                        restartLoader();
                    }
                }).negativeText(android.R.string.cancel).onNegative(new MaterialDialog.SingleButtonCallback() {
                    @Override
                    public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
                        dialog.cancel();
                    }
                }).build().show();
    }

    /**
     * Show alter binaryDialog, for create or edit new category
     */
    private void showDialogEditSubCategoryName(final SQLTypeTransaction type, final int categoryId,
            final int subCategoryId, final CharSequence subCategName) {

        // inflate view
        View viewDialog = LayoutInflater.from(getActivity()).inflate(R.layout.dialog_new_edit_subcategory, null);

        final EditText edtSubCategName = (EditText) viewDialog.findViewById(R.id.editTextCategName);
        final Spinner spnCategory = (Spinner) viewDialog.findViewById(R.id.spinnerCategory);
        // set category description
        edtSubCategName.setText(subCategName);
        if (!TextUtils.isEmpty(subCategName)) {
            edtSubCategName.setSelection(subCategName.length());
        }

        // Fill categories list.
        CategoryService categoryService = new CategoryService(getActivity());
        final List<Category> categories = categoryService.getList();

        ArrayList<String> categoryNames = new ArrayList<>();
        ArrayList<Integer> categoryIds = new ArrayList<>();
        for (Category category : categories) {
            categoryIds.add(category.getId());
            categoryNames.add(category.getName().toString());
        }
        ArrayAdapter<String> adapterCategory = new ArrayAdapter<>(getActivity(),
                android.R.layout.simple_spinner_item, categoryNames);
        adapterCategory.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spnCategory.setAdapter(adapterCategory);
        //select category if present
        if (categoryId > 0) {
            spnCategory.setSelection(categoryIds.indexOf(categoryId), true);
        }

        int titleId = type.equals(SQLTypeTransaction.INSERT) ? R.string.add_subcategory
                : R.string.edit_categoryName;

        new MaterialDialog.Builder(getContext()).customView(viewDialog, true)
                .icon(FontIconDrawable.inflate(getContext(), R.xml.ic_tag)).title(titleId)
                .positiveText(android.R.string.ok).onPositive(new MaterialDialog.SingleButtonCallback() {
                    @Override
                    public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
                        // get category description
                        String name = edtSubCategName.getText().toString();
                        // check position
                        if (spnCategory.getSelectedItemPosition() == Spinner.INVALID_POSITION)
                            return;
                        // get category id
                        int categId = categories.get(spnCategory.getSelectedItemPosition()).getId();
                        ContentValues values = new ContentValues();
                        values.put(Subcategory.CATEGID, categId);
                        values.put(Subcategory.SUBCATEGNAME, name);

                        SubcategoryRepository repo = new SubcategoryRepository(getActivity());

                        // check type transaction is request
                        switch (type) {
                        case INSERT:
                            if (getActivity().getContentResolver().insert(repo.getUri(), values) == null) {
                                Toast.makeText(getActivity(), R.string.db_insert_failed, Toast.LENGTH_SHORT).show();
                            }
                            break;
                        case UPDATE:
                            if (getActivity().getContentResolver().update(repo.getUri(), values, Subcategory.CATEGID
                                    + "=" + categoryId + " AND " + Subcategory.SUBCATEGID + "=" + subCategoryId,
                                    null) == 0) {
                                Toast.makeText(getActivity(), R.string.db_update_failed, Toast.LENGTH_SHORT).show();
                            }
                            break;
                        }
                        // restart loader
                        restartLoader();
                    }
                }).negativeText(android.R.string.cancel).onNegative(new MaterialDialog.SingleButtonCallback() {
                    @Override
                    public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
                        dialog.cancel();
                    }
                }).build().show();
    }

    private void addListClickHandlers() {
        // the list handlers available only when selecting a category.
        if (mAction.equals(Intent.ACTION_PICK)) {
            getExpandableListView().setOnChildClickListener(new ExpandableListView.OnChildClickListener() {

                @Override
                public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition,
                        long id) {
                    if (getExpandableListAdapter() != null
                            && getExpandableListAdapter() instanceof CategoryExpandableListAdapter) {
                        CategoryExpandableListAdapter adapter = (CategoryExpandableListAdapter) getExpandableListAdapter();

                        QueryCategorySubCategory data = mSubCategories.get(mCategories.get(groupPosition))
                                .get(childPosition);

                        adapter.setIdChildChecked(data.getCategId(), data.getSubCategId());
                        adapter.notifyDataSetChanged();

                        // select sub-categories immediately.
                        setResultAndFinish();
                    }
                    return false;
                }
            });

            getExpandableListView().setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {

                @Override
                public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
                    if (getExpandableListAdapter() != null
                            && getExpandableListAdapter() instanceof CategoryExpandableListAdapter) {
                        CategoryExpandableListAdapter adapter = (CategoryExpandableListAdapter) getExpandableListAdapter();

                        Category category = mCategories.get(groupPosition);

                        adapter.setIdGroupChecked(category.getId());
                        adapter.notifyDataSetChanged();

                        int subCategoriesCount = mSubCategories.get(category).size();
                        if (subCategoriesCount == 0) {
                            setResultAndFinish();
                        }
                    }
                    return false;
                }
            });
        }
    }

    /**
     * Choose the item type: category / subcategory.
     */
    private void showTypeSelectorDialog() {
        new MaterialDialog.Builder(getActivity()).title(R.string.choose_type).items(R.array.category_type)
                .itemsCallbackSingleChoice(-1, new MaterialDialog.ListCallbackSingleChoice() {
                    @Override
                    public boolean onSelection(MaterialDialog dialog, View view, int which, CharSequence text) {
                        /**
                         * If you use alwaysCallSingleChoiceCallback(), which is discussed below,
                         * returning false here won't allow the newly selected radio button to actually be selected.
                         **/
                        //                        showNameEntryDialog();

                        // todo: depending on the choice, show the edit binaryDialog. 0-based
                        if (which == 0) {
                            showDialogEditCategoryName(SQLTypeTransaction.INSERT, -1, null);
                        } else {
                            showDialogEditSubCategoryName(SQLTypeTransaction.INSERT, -1, -1, null);
                        }

                        return true;
                    }
                }).positiveText(android.R.string.ok)
                //                .negativeText(android.R.string.cancel)
                .neutralText(android.R.string.cancel).show();
    }

    private void showSearchActivityFor(SearchParameters parameters) {
        Intent intent = new Intent(getActivity(), SearchActivity.class);
        intent.putExtra(SearchActivity.EXTRA_SEARCH_PARAMETERS, Parcels.wrap(parameters));
        intent.setAction(Intent.ACTION_INSERT);
        startActivity(intent);
    }

}