/* * Copyright (C) 2006 The Android Open Source Project * * 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.banking.xc.utils; import java.util.ArrayList; import java.util.WeakHashMap; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.content.Context; import android.net.Uri; import com.banking.xc.utils.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Checkable; import android.widget.Filter; import android.widget.Filterable; import android.widget.ImageView; import android.widget.SimpleAdapter; import android.widget.TextView; /** * An easy adapter to map static data to views defined in an XML file. You can * specify the data backing the list as an ArrayList of Maps. Each entry in the * ArrayList corresponds to one row in the list. The Maps contain the data for * each row. You also specify an XML file that defines the views used to display * the row, and a mapping from keys in the Map to specific views. * * Binding data to views occurs in two phases. First, if a * {@link android.widget.SimpleAdapter.ViewBinder} is available, * {@link ViewBinder#setViewValue(android.view.View, Object, String)} is * invoked. If the returned value is true, binding has occurred. If the returned * value is false, the following views are then tried in order: * <ul> * <li>A view that implements Checkable (e.g. CheckBox). The expected bind value * is a boolean. * <li>TextView. The expected bind value is a string and * {@link #setViewText(TextView, String)} is invoked. * <li>ImageView. The expected bind value is a resource id or a string and * {@link #setViewImage(ImageView, int)} or * {@link #setViewImage(ImageView, String)} is invoked. * </ul> * If no appropriate binding can be found, an {@link IllegalStateException} is * thrown. */ public class SimpleJsonAdapter extends BaseAdapter implements Filterable { private int[] mTo; private String[] mFrom; private ViewBinder mViewBinder; private JSONArray mData; private int mResource; private int mDropDownResource; private LayoutInflater mInflater; private final WeakHashMap<View, View[]> mHolders = new WeakHashMap<View, View[]>(); private ArrayList<View> viewList = new ArrayList<View>(); private SimpleFilter mFilter; private JSONArray mUnfilteredData; /** * Constructor * * @param context * The context where the View associated with this SimpleAdapter * is running * @param data * A List of Maps. Each entry in the List corresponds to one row * in the list. The Maps contain the data for each row, and * should include all the entries specified in "from" * @param resource * Resource identifier of a view layout that defines the views * for this list item. The layout file should include at least * those named views defined in "to" * @param from * A list of column names that will be added to the Map * associated with each item. * @param to * The views that should display column in the "from" parameter. * These should all be TextViews. The first N views in this list * are given the values of the first N columns in the from * parameter. */ public SimpleJsonAdapter(Context context, JSONArray data, int resource, String[] from, int[] to) { mData = data; mResource = mDropDownResource = resource; mFrom = from; mTo = to; mInflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); } /** * @see android.widget.Adapter#getCount() */ public int getCount() { return mData.length(); } /** * @see android.widget.Adapter#getItem(int) */ public Object getItem(int position) { try { return mData.get(position); } catch (JSONException e) { throw new RuntimeException(e); } } /** * @see android.widget.Adapter#getItemId(int) */ public long getItemId(int position) { return position; } /** * @see android.widget.Adapter#getView(int, View, ViewGroup) */ public View getView(int position, View convertView, ViewGroup parent) { return createViewFromResource(position, convertView, parent, mResource); } private View createViewFromResource(int position, View convertView, ViewGroup parent, int resource) { View v; if (convertView == null) { v = ImageUtil.inflate(resource, parent, false); final int[] to = mTo; final int count = to.length; final View[] holder = new View[count]; for (int i = 0; i < count; i++) { holder[i] = v.findViewById(to[i]); } mHolders.put(v, holder); viewList.add(v); } else { v = convertView; } bindView(position, v); return v; } /** * <p> * Sets the layout resource to create the drop down views. * </p> * * @param resource * the layout resource defining the drop down views * @see #getDropDownView(int, android.view.View, android.view.ViewGroup) */ public void setDropDownViewResource(int resource) { this.mDropDownResource = resource; } @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { return createViewFromResource(position, convertView, parent, mDropDownResource); } public View getView(int position) { if (Log.D) { Log.d("size", "" + viewList.size()); } return null;// viewList.get(position); } private void bindView(int position, View view) { JSONObject dataSet = null; try { dataSet = mData.getJSONObject(position); } catch (JSONException e) { throw new RuntimeException(e); } if (dataSet == null) { return; } final ViewBinder binder = mViewBinder; final View[] holder = mHolders.get(view); final String[] from = mFrom; final int[] to = mTo; final int count = to.length; for (int i = 0; i < count; i++) { final View v = holder[i]; if (v != null) { Object data = null; try { data = dataSet.get(from[i]); } catch (JSONException e) { throw new RuntimeException(e); } String text = data == null ? "" : data.toString(); if (text == null) { text = ""; } boolean bound = false; if (binder != null) { bound = binder.setViewValue(v, data, text); } if (!bound) { if (v instanceof Checkable) { if (data instanceof Boolean) { ((Checkable) v).setChecked((Boolean) data); } else { throw new IllegalStateException(v.getClass() .getName() + " should be bound to a Boolean, not a " + data.getClass()); } } else if (v instanceof TextView) { // Note: keep the instanceof TextView check at the // bottom of these // ifs since a lot of views are TextViews (e.g. // CheckBoxes). setViewText((TextView) v, text); } else if (v instanceof ImageView) { if (data instanceof Integer) { setViewImage((ImageView) v, (Integer) data); } else { setViewImage((ImageView) v, text); } } else { throw new IllegalStateException( v.getClass().getName() + " is not a " + " view that can be bounds by this SimpleAdapter"); } } } } } /** * Returns the {@link ViewBinder} used to bind data to views. * * @return a ViewBinder or null if the binder does not exist * * @see #setViewBinder(android.widget.SimpleAdapter.ViewBinder) */ public ViewBinder getViewBinder() { return mViewBinder; } /** * Sets the binder used to bind data to views. * * @param viewBinder * the binder used to bind data to views, can be null to remove * the existing binder * * @see #getViewBinder() */ public void setViewBinder(ViewBinder viewBinder) { mViewBinder = viewBinder; } /** * Called by bindView() to set the image for an ImageView but only if there * is no existing ViewBinder or if the existing ViewBinder cannot handle * binding to an ImageView. * * This method is called instead of {@link #setViewImage(ImageView, String)} * if the supplied data is an int or Integer. * * @param v * ImageView to receive an image * @param value * the value retrieved from the data set * * @see #setViewImage(ImageView, String) */ public void setViewImage(ImageView v, int value) { v.setImageResource(value); } /** * Called by bindView() to set the image for an ImageView but only if there * is no existing ViewBinder or if the existing ViewBinder cannot handle * binding to an ImageView. * * By default, the value will be treated as an image resource. If the value * cannot be used as an image resource, the value is used as an image Uri. * * This method is called instead of {@link #setViewImage(ImageView, int)} if * the supplied data is not an int or Integer. * * @param v * ImageView to receive an image * @param value * the value retrieved from the data set * * @see #setViewImage(ImageView, int) */ public void setViewImage(ImageView v, String value) { try { v.setImageResource(Integer.parseInt(value)); } catch (NumberFormatException nfe) { v.setImageURI(Uri.parse(value)); } } /** * Called by bindView() to set the text for a TextView but only if there is * no existing ViewBinder or if the existing ViewBinder cannot handle * binding to an TextView. * * @param v * TextView to receive text * @param text * the text to be set for the TextView */ public void setViewText(TextView v, String text) { v.setText(text); } public Filter getFilter() { if (mFilter == null) { mFilter = new SimpleFilter(); } return mFilter; } /** * This class can be used by external clients of SimpleAdapter to bind * values to views. * * You should use this class to bind values to views that are not directly * supported by SimpleAdapter or to change the way binding occurs for views * supported by SimpleAdapter. * * @see SimpleAdapter#setViewImage(ImageView, int) * @see SimpleAdapter#setViewImage(ImageView, String) * @see SimpleAdapter#setViewText(TextView, String) */ public static interface ViewBinder { /** * Binds the specified data to the specified view. * * When binding is handled by this ViewBinder, this method must return * true. If this method returns false, SimpleAdapter will attempts to * handle the binding on its own. * * @param view * the view to bind the data to * @param data * the data to bind to the view * @param textRepresentation * a safe String representation of the supplied data: it is * either the result of data.toString() or an empty String * but it is never null * * @return true if the data was bound to the view, false otherwise */ boolean setViewValue(View view, Object data, String textRepresentation); } /** * <p> * An array filters constrains the content of the array adapter with a * prefix. Each item that does not start with the supplied prefix is removed * from the list. * </p> */ private class SimpleFilter extends Filter { @Override protected FilterResults performFiltering(CharSequence prefix) { FilterResults results = new FilterResults(); if (mUnfilteredData == null) { mUnfilteredData = mData;// XXX new ArrayList<Map<String, // ?>>(mData); } if (prefix == null || prefix.length() == 0) { JSONArray list = mUnfilteredData; results.values = list; results.count = list.length(); } else { String prefixString = prefix.toString().toLowerCase(); JSONArray unfilteredValues = mUnfilteredData; int count = unfilteredValues.length(); JSONArray newValues = new JSONArray();// new // ArrayList<Map<String, // ?>>(count); for (int i = 0; i < count; i++) { JSONObject h = null; try { h = unfilteredValues.getJSONObject(i); } catch (JSONException e) { throw new RuntimeException(e); } if (h != null) { int len = mTo.length; for (int j = 0; j < len; j++) { String str = null; try { str = (String) h.getString(mFrom[j]); } catch (JSONException e) { throw new RuntimeException(e); } String[] words = str.split(" "); int wordCount = words.length; for (int k = 0; k < wordCount; k++) { String word = words[k]; if (word.toLowerCase().startsWith(prefixString)) { newValues.put(h); break; } } } } } results.values = newValues; results.count = newValues.length(); } return results; } @Override protected void publishResults(CharSequence constraint, FilterResults results) { // noinspection unchecked mData = (JSONArray) results.values; if (results.count > 0) { notifyDataSetChanged(); } else { notifyDataSetInvalidated(); } } } }