/*
* @copyright 2012 Philip Warner
* @license GNU General Public License
*
* This file is part of Book Catalogue.
*
* Book Catalogue 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.
*
* Book Catalogue 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 Book Catalogue. If not, see <http://www.gnu.org/licenses/>.
*/
package com.eleybourn.bookcatalogue.properties;
import android.app.AlertDialog;
import android.graphics.Typeface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import com.eleybourn.bookcatalogue.BookCatalogueApp;
import com.eleybourn.bookcatalogue.R;
import com.eleybourn.bookcatalogue.utils.HintManager;
import com.eleybourn.bookcatalogue.utils.ViewTagger;
import java.util.ArrayList;
import java.util.Iterator;
/**
* Implement a generic list-of-values property.
*
* @author Philip Warner
*
* @param <T> Base type of list items
*/
public abstract class ListProperty<T extends Object> extends ValuePropertyWithGlobalDefault<T> {
/** List of valid values */
protected ItemEntries<T> mList = null;
/** Accessor */
public ItemEntries<T> getListItems() { return mList; };
/** Accessor */
public void setListItems(ItemEntries<T> list) { mList= list; };
public ListProperty(ItemEntries<T> list, String uniqueId, PropertyGroup group, int nameResourceId, T value, String defaultPref, T defaultValue) {
super(uniqueId, group, nameResourceId, value, defaultPref, defaultValue);
mList = list;
}
public ListProperty(ItemEntries<T> list, String uniqueId, PropertyGroup group, int nameResourceId, T value, T defaultValue) {
super(uniqueId, group, nameResourceId, value, null, defaultValue);
mList = list;
}
/**
* Return the default list editor view with associated event handlers.
*/
@Override
public View getView(final LayoutInflater inflater) {
View v = inflater.inflate(R.layout.property_value_list, null);
ViewTagger.setTag(v, R.id.TAG_PROPERTY, this);
// Display the list of values when clicked.
v.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
handleClick(v, inflater);
}
});
// Set the name
TextView text = (TextView) v.findViewById(R.id.name);
text.setText(getName());
// Try to find the list item that corresponds to the current stored value.
ItemEntry<T> val = null;
for(ItemEntry<T> e: getListItems() ) {
if (e.value == null) {
if (get() == null) {
val = e;
}
} else {
if (get() != null && get().equals(e.value)) {
val = e;
}
}
}
// Display current value
setValueInView(v, val);
return v;
}
private void handleClick(final View base, final LayoutInflater inflater) {
final ItemEntries<T> items = getListItems();
if (this.hasHint()) {
HintManager.displayHint(base.getContext(), this.getHint(), new Runnable(){
@Override
public void run() {
displayList(base, inflater, items);
}});
} else {
displayList(base, inflater, items);
}
}
/**
* Class to represent all items in a list-of-values property
*
* @author Philip Warner
*
* @param <T> Type of underlying list item
*/
public static class ItemEntry<T extends Object> {
/** Actual value */
T value;
/** Test description of the meaning of that value */
int textId;
Object[] textArgs;
/** Constructor. Instantiates string. */
public ItemEntry(T value, int resourceId, Object... args) {
this.value = value;
this.textId = resourceId; //BookCatalogueApp.getResourceString(resourceId);
this.textArgs = args;
}
// /** Constructor */
// public ItemEntry(T value, String text) {
// this.value = value;
// this.text = text;
// }
/** Accessor */
public String getString() {
return BookCatalogueApp.getResourceString(textId, textArgs);
}
/** Accessor */
public T getValue() {
return value;
}
/** Accessor */
public void setString(int value, Object... args) {
textId = value;
textArgs = args;
}
@Override
public String toString() {
return getString();
}
}
/**
* Class to represent a collection of list entries for a list-of-values property
*
* @author Philip Warner
*
* @param <T> Underlying list item data type.
*/
public static class ItemEntries<T> implements Iterable<ItemEntry<T>> {
ArrayList<ItemEntry<T>> mList = new ArrayList<ItemEntry<T>>();
/**
* Utility to make adding items easier.
*
* @param value Underlying value
* @param stringId String ID of description
* @return
*/
public ItemEntries<T> add(T value, int stringId, Object... args) {
mList.add(new ItemEntry<T>(value, stringId, args));
return this;
}
// /**
// * Utility to make adding items easier.
// *
// * @param value Underlying value
// * @param stringId Description
// * @return
// */
// public ItemEntries<T> add(T value, int string) {
// mList.add(new ItemEntry<T>(value, string));
// return this;
// }
/** Iterator access */
@Override
public Iterator<ItemEntry<T>> iterator() {
return mList.iterator();
}
}
/**
* Holder class for list items
*
* @author Philip Warner
*
* @param <T>
*/
private static class Holder<T extends Object> {
ItemEntry<T> item;
View baseView;
public Holder(ItemEntry<T> item, View baseView) {
this.item = item;
this.baseView = baseView;
}
}
/**
* Set the 'value' field in the passed view to match the passed item.
*
* @param baseView
* @param item
*/
private void setValueInView(View baseView, ItemEntry<T> item) {
TextView text = (TextView) baseView.findViewById(R.id.value);
if (item == null) {
text.setText("");
} else {
if (isDefault(item.value))
text.setTypeface(null, Typeface.NORMAL);
else
text.setTypeface(null, Typeface.BOLD);
text.setText(item.getString());
}
}
/**
* Called to display a list of values for this property.
*
* @param base Specific view that was clicked
* @param inflater LayoutInflater
* @param items All list items
* @return
*/
private boolean displayList(final View base, final LayoutInflater inflater, ItemEntries<T> items) {
// Get the view and the radio group
View root = inflater.inflate(R.layout.property_value_list_list, null);
RadioGroup grp = (RadioGroup) root.findViewById(R.id.values);
// Get the current value
T curr = get();
final AlertDialog dialog = new AlertDialog.Builder(inflater.getContext()).setView(root).create();
// Create a listener that responds to any click on the list
OnClickListener l = new OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
Holder<T> h = ViewTagger.getTag(v, R.id.TAG_HOLDER);
set(h.item.value);
setValueInView(h.baseView, h.item);
}
};
// Add each entry to the list
for(ItemEntry<T> e: items) {
// If we are looking at global-only values, NULL is invalid
if (e.value != null || !isGlobal()) {
// Check if this value is the currently selected value
boolean selected = false;
if (e.value == null && curr == null)
selected = true;
else if (e.value != null && curr != null && curr.equals(e.value))
selected = true;
// Make the view for this item
View v = inflater.inflate(R.layout.property_value_list_item, null);
TextView name = (TextView) v.findViewById(R.id.name);
RadioButton sel = (RadioButton) v.findViewById(R.id.selector);
//Set the various values
sel.setChecked(selected);
name.setText(e.getString());
// Listen for clicks
sel.setOnClickListener(l);
v.setOnClickListener(l);
// Set the tacks used by the listeners
ViewTagger.setTag(v, R.id.TAG_HOLDER, new Holder<T>(e, base));
ViewTagger.setTag(sel, R.id.TAG_HOLDER, new Holder<T>(e, base));
// Add it to the group
grp.addView(v);
}
}
// Display dialog
dialog.show();
return true;
}
}