/*
* 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.assetallocation;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.money.manager.ex.Constants;
import com.money.manager.ex.R;
import com.money.manager.ex.assetallocation.events.AssetAllocationReloadRequestedEvent;
import com.money.manager.ex.assetallocation.events.AssetClassSelectedEvent;
import com.money.manager.ex.common.BaseListFragment;
import com.money.manager.ex.core.Core;
import com.money.manager.ex.core.FormatUtilities;
import com.money.manager.ex.core.UIHelper;
import com.money.manager.ex.datalayer.AssetClassStockRepository;
import com.money.manager.ex.domainmodel.AssetClass;
import com.money.manager.ex.domainmodel.Stock;
import com.money.manager.ex.servicelayer.AssetAllocationService;
import com.shamanland.fonticon.FontIconDrawable;
import org.greenrobot.eventbus.EventBus;
import org.parceler.Parcels;
/**
* A list fragment that displays the Asset Class contents (stocks or child asset classes).
*/
public class AssetAllocationContentsFragment
extends BaseListFragment {
public static final int REQUEST_STOCK_ID = 1;
public static final int REQUEST_EDIT_ALLOCATION = 2;
public static final String PARAM_ASSET_CLASS_ID = "assetClassId";
public static final String PARAM_DECIMAL_PLACES = "decimalPlaces";
public static final String PARAM_ASSET_ALLOCATION = "assetAllocation";
/**
* Creates a new fragment instance. Sets the appropriate required attributes.
* @param assetClassId Id of the Asset Class to show.
* @return Fragment
*/
public static AssetAllocationContentsFragment create(Integer assetClassId, int decimalPlaces, AssetClass assetAllocation) {
AssetAllocationContentsFragment fragment = new AssetAllocationContentsFragment();
Bundle arguments = new Bundle();
if (assetClassId == null) {
assetClassId = Constants.NOT_SET;
}
arguments.putInt(PARAM_ASSET_CLASS_ID, assetClassId);
arguments.putInt(PARAM_DECIMAL_PLACES, decimalPlaces);
arguments.putParcelable(PARAM_ASSET_ALLOCATION, Parcels.wrap(assetAllocation));
fragment.setArguments(arguments);
return fragment;
}
public AssetAllocationContentsFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// View view = inflater.inflate(R.layout.fragment_asset_allocation, container, false);
View view = super.onCreateView(inflater, container, savedInstanceState);
return view;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
renderHeader();
setEmptyText(getActivity().getResources().getString(R.string.asset_classes_empty));
// create and link the adapter
AssetAllocationAdapter adapter = new AssetAllocationAdapter(getActivity(), null);
setListAdapter(adapter);
registerForContextMenu(getListView());
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
setListShown(false);
setFloatingActionButtonVisible(true);
attachFloatingActionButtonToListView();
showData();
}
@Override
public void onResume() {
super.onResume();
}
@Override
public void onPause() {
super.onPause();
}
@Override
public void onDestroyView() {
super.onDestroyView();
setListAdapter(null);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_CANCELED) return;
switch (requestCode) {
case REQUEST_STOCK_ID:
// get the stock id
String symbol = data.getStringExtra(SecurityListFragment.INTENT_RESULT_STOCK_SYMBOL);
assignStockToAssetClass(symbol);
break;
case REQUEST_EDIT_ALLOCATION:
EventBus.getDefault().post(new AssetAllocationReloadRequestedEvent());
break;
}
}
@Override
public String getSubTitle() {
AssetClass assetClass = retrieveData();
if (assetClass == null) return null;
return assetClass.getName();
}
@Override
public void onFloatingActionButtonClicked() {
AssetClass assetClass = retrieveData();
// check which item to create
ItemType type = assetClass.getType();
if (type == null) {
new UIHelper(getActivity()).showToast("Item type not set.");
return;
}
switch (type) {
case Allocation:
boolean handled = false;
if (assetClass.getChildren().size() > 0) {
startEditAssetClassActivityForInsert();
handled = true;
}
if (assetClass.getStockLinks().size() > 0) {
pickStock();
handled = true;
}
// Offer a choice here - stocks or child allocation
if (!handled) {
showTypeSelectorDialog();
}
break;
case Group:
startEditAssetClassActivityForInsert();
break;
}
}
// Context menu
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
MatrixCursorColumns item = getSelectedItemType(info);
// This is the id of the item selected in the list.
// int id = (int) info.id;
menu.setHeaderTitle(getString(R.string.asset_allocation));
MenuInflater inflater = this.getActivity().getMenuInflater();
if (item.type.equals(ItemType.Stock)) {
inflater.inflate(R.menu.contextmenu_asset_allocation_stock, menu);
} else {
inflater.inflate(R.menu.contextmenu_asset_allocation, menu);
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
boolean handled = false;
// find out what is the type of the selected item.
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
MatrixCursorColumns selectedItem = getSelectedItemType(info);
int id = selectedItem.id;
AssetAllocationService service = new AssetAllocationService(getActivity());
switch (item.getItemId()) {
case R.id.menu_move_up:
// move item up
service.moveClassUp(id);
break;
case R.id.menu_move_down:
service.moveClassDown(id);
break;
case R.id.menu_edit:
startEditAssetClassActivityForEdit(id);
handled = true;
break;
case R.id.menu_delete:
confirmDelete(selectedItem);
handled = true;
break;
default:
super.onContextItemSelected(item);
break;
}
return handled;
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
MatrixCursor cursor = (MatrixCursor) l.getItemAtPosition(position);
int selectedId = cursor.getInt(cursor.getColumnIndex(MatrixCursorColumns.ID));
String typeName = cursor.getString(cursor.getColumnIndex(MatrixCursorColumns.TYPE));
ItemType type = ItemType.valueOf(typeName);
switch (type) {
case Allocation:
//raiseAssetClassSelected(selectedId);
EventBus.getDefault().post(new AssetClassSelectedEvent(selectedId));
break;
case Stock:
// ?
break;
}
// getActivity().openContextMenu(v);
}
public void showData() {
AssetClass assetAllocation = retrieveData();
Cursor matrixCursor = createMatrixCursor(assetAllocation);
AssetAllocationAdapter adapter = (AssetAllocationAdapter) getListAdapter();
// adapter.swapCursor(matrixCursor);
adapter.changeCursor(matrixCursor);
// refresh footer/totals
renderFooter(assetAllocation);
if (isResumed()) {
setListShown(true);
if (matrixCursor != null && matrixCursor.getCount() <= 0 && getFloatingActionButton() != null)
getFloatingActionButton().show(true);
} else {
setListShownNoAnimation(true);
}
}
// private
private void deleteAllocation(MatrixCursorColumns item) {
AssetAllocationService service = new AssetAllocationService(getActivity());
if (!service.deleteAllocation(item.id)) {
Toast.makeText(getActivity(), R.string.db_delete_failed, Toast.LENGTH_SHORT).show();
}
// reload data. Handled automagically. (by observer)
}
private void deleteStockLink(MatrixCursorColumns item) {
// remove stock link
String symbol = item.name;
AssetClassStockRepository repo = new AssetClassStockRepository(getActivity());
repo.delete(symbol);
}
private void confirmDelete(final MatrixCursorColumns item) {
new MaterialDialog.Builder(getContext())
.title(R.string.delete)
.icon(FontIconDrawable.inflate(getContext(), R.xml.ic_question))
.content(R.string.confirmDelete)
.positiveText(android.R.string.ok)
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
if (item.type.equals(ItemType.Stock)) {
deleteStockLink(item);
} else {
deleteAllocation(item);
}
}
})
.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 startEditAssetClassActivityForInsert() {
Intent intent = new Intent(getActivity(), AssetClassEditActivity.class);
intent.setAction(Intent.ACTION_INSERT);
intent.putExtra(AssetClassEditActivity.KEY_PARENT_ID, this.getAssetClassId());
startActivity(intent);
// REQUEST_CREATE_ALLOCATION
// Content observer will refresh the Asset Allocation automatically.
}
private void startEditAssetClassActivityForEdit(int assetClassId) {
Intent intent = new Intent(getActivity(), AssetClassEditActivity.class);
intent.setAction(Intent.ACTION_EDIT);
intent.putExtra(Intent.EXTRA_UID, assetClassId);
startActivityForResult(intent, REQUEST_EDIT_ALLOCATION);
}
private MatrixCursor createMatrixCursor(AssetClass allocation) {
String[] columns = new String[] {
MatrixCursorColumns.ID, MatrixCursorColumns.NAME,
MatrixCursorColumns.ALLOCATION, MatrixCursorColumns.VALUE,
MatrixCursorColumns.CURRENT_ALLOCATION, MatrixCursorColumns.CURRENT_VALUE,
MatrixCursorColumns.DIFFERENCE, MatrixCursorColumns.DIFFERENCE_PERCENT,
MatrixCursorColumns.TYPE
};
MatrixCursor cursor = new MatrixCursor(columns);
// ItemType type = allocation.getType();
if (allocation.getChildren().size() > 0) {
// group
addChildAssetsToCursor(cursor, allocation);
} else if (allocation.getStocks().size() > 0) {
// allocation, with stocks
addStocksToCursor(cursor, allocation);
} else {
// either empty allocation or a stock.
}
return cursor;
}
private void renderHeader() {
ListView listView = getListView();
if (listView.getHeaderViewsCount() > 0) return;
View view = View.inflate(getActivity(), R.layout.item_asset_allocation, null);
AssetClassViewHolder holder = AssetClassViewHolder.initialize(view);
MatrixCursorColumns values = new MatrixCursorColumns();
values.name = getString(R.string.name);
values.allocation = getString(R.string.allocation);
values.value = getString(R.string.value);
values.currentAllocation = getString(R.string.current);
values.currentValue = getString(R.string.current);
values.difference = getString(R.string.difference);
// int decimalPlaces = getArguments().getInt(PARAM_DECIMAL_PLACES);
UIHelpers.populateAssetClassRow(holder, values);
// formatting
Core core = new Core(getActivity());
holder.container.setBackgroundColor(core.getColourFromAttribute(R.attr.color_list_header));
// holder.container.setBackgroundResource(core.getColourFromAttribute(R.attr.color_list_header));
listView.addHeaderView(view, null, false);
// listView.addHeaderView(view);
}
private View mFooter;
private void renderFooter(AssetClass assetClass) {
if (assetClass == null) return;
View view = View.inflate(getActivity(), R.layout.item_asset_allocation, null);
AssetClassViewHolder holder = AssetClassViewHolder.initialize(view);
MatrixCursorColumns values = new MatrixCursorColumns();
int decimalPlaces = getArguments().getInt(PARAM_DECIMAL_PLACES);
FormatUtilities format = new FormatUtilities(getActivity());
values.name = getString(R.string.total);
values.allocation = assetClass.getAllocation().toString();
values.value = format.getValueFormattedInBaseCurrency(assetClass.getValue().truncate(decimalPlaces));
values.currentAllocation = assetClass.getCurrentAllocation().truncate(2).toString();
values.currentValue = format.getValueFormattedInBaseCurrency(assetClass.getCurrentValue().truncate(decimalPlaces));
values.difference = format.getValueFormattedInBaseCurrency(assetClass.getDifference().truncate(decimalPlaces));
UIHelpers.populateAssetClassRow(holder, values);
// todo: formatting & colours
Core core = new Core(getActivity());
holder.container.setBackgroundColor(core.getColourFromAttribute(R.attr.color_list_header));
// holder.container.setBackgroundResource(core.getColourFromAttribute(R.attr.color_list_header));
holder.assetClassTextView.setTypeface(null, Typeface.BOLD);
// holder.allocationTextView.setTypeface(null, Typeface.BOLD);
// holder.valueTextView.setTypeface(null, Typeface.BOLD);
ListView listView = getListView();
// remove any footers
if (mFooter != null) {
listView.removeFooterView(mFooter);
}
mFooter = view;
// listView.addFooterView(view);
listView.addFooterView(mFooter, null, false);
}
private void addChildAssetsToCursor(MatrixCursor cursor, AssetClass allocation) {
int precision = 2;
for (AssetClass item : allocation.getChildren()) {
Object[] values = new Object[] {
item.getId(), item.getName(),
item.getAllocation(), item.getValue().truncate(precision),
item.getCurrentAllocation().truncate(precision),
item.getCurrentValue().truncate(precision),
item.getDifference().truncate(precision),
item.getDiffAsPercentOfSet().truncate(precision),
ItemType.Allocation.toString()
};
cursor.addRow(values);
}
}
private void addStocksToCursor(MatrixCursor cursor, AssetClass allocation) {
int precision = 2;
for (Stock item : allocation.getStocks()) {
Object[] values = new Object[] {
item.getId(), item.getSymbol(),
null, null,
null, item.getValue().truncate(precision),
null, null,
ItemType.Stock.toString()
};
cursor.addRow(values);
}
}
/**
* Returns the asset class of the class displayed in this fragment.
* @return Current asset class's id.
*/
private int getAssetClassId() {
return getArguments().getInt(PARAM_ASSET_CLASS_ID);
}
private MatrixCursorColumns getSelectedItemType(AdapterView.AdapterContextMenuInfo info) {
Object fromAdapter = getListAdapter().getItem(info.position);
MatrixCursor cursor = (MatrixCursor) fromAdapter;
// todo: see how to solve this. Location has to be modified because of the header row.
cursor.moveToPosition(info.position - 1);
MatrixCursorColumns result = MatrixCursorColumns.fromCursor(getActivity(), cursor);
return result;
}
private AssetClass retrieveData() {
int id = getAssetClassId();
AssetAllocationService service = new AssetAllocationService(getContext());
AssetClass assetAllocation = Parcels.unwrap(getArguments().getParcelable(PARAM_ASSET_ALLOCATION));
AssetClass assetClass = service.findChild(id, assetAllocation);
return assetClass;
}
private void showTypeSelectorDialog() {
new MaterialDialog.Builder(getActivity())
.title(R.string.choose_type)
.items(R.array.new_asset_class_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();
switch (which) {
case 0:
// Asset Allocation
startEditAssetClassActivityForInsert();
break;
case 1:
// Stock
pickStock();
break;
}
return true;
}
})
.positiveText(android.R.string.ok)
// .negativeText(android.R.string.cancel)
.neutralText(android.R.string.cancel)
.show();
}
private void pickStock() {
Intent intent = new Intent(getActivity(), SecurityListActivity.class);
intent.setAction(Intent.ACTION_PICK);
intent.putExtra(SecurityListActivity.EXTRA_ASSET_CLASS_ID, this.getAssetClassId());
// ref: http://stackoverflow.com/questions/6147884/onactivityresult-not-being-called-in-fragment
startActivityForResult(intent, REQUEST_STOCK_ID);
// continues in onActivityResult
}
private void assignStockToAssetClass(String stockSymbol) {
AssetAllocationService service = new AssetAllocationService(getActivity());
service.assignStockToAssetClass(stockSymbol, getAssetClassId());
}
}