edu.cens.loci.ui.widget.GenericEditorView.java Source code

Java tutorial

Introduction

Here is the source code for edu.cens.loci.ui.widget.GenericEditorView.java

Source

/*
 * Copyright (C) 2009 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 edu.cens.loci.ui.widget;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.json.JSONException;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Entity;
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.PhoneNumberFormattingTextWatcher;
import android.text.Editable;
import android.text.InputType;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RelativeLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import edu.cens.loci.LociConfig;
import edu.cens.loci.R;
import edu.cens.loci.classes.EntityDelta;
import edu.cens.loci.classes.EntityModifier;
import edu.cens.loci.classes.LociWifiFingerprint;
import edu.cens.loci.classes.EntityDelta.ValuesDelta;
import edu.cens.loci.classes.LociWifiFingerprint.APInfoMapItem;
import edu.cens.loci.classes.PlacesSource.DataKind;
import edu.cens.loci.classes.PlacesSource.EditField;
import edu.cens.loci.classes.PlacesSource.EditType;
import edu.cens.loci.provider.LociDbUtils;
import edu.cens.loci.provider.LociContract.CommonPlaceDataKinds.Keyword;
import edu.cens.loci.provider.LociContract.CommonPlaceDataKinds.WifiFingerprint;
import edu.cens.loci.ui.ViewIdGenerator;
import edu.cens.loci.ui.PlaceViewActivity.WifiViewListItem;
import edu.cens.loci.utils.MyDateUtils;
import edu.cens.loci.utils.MyLog;

/**
 * Simple editor that handles labels and any {@link EditField} defined for
 * the entry. Uses {@link ValuesDelta} to read any existing
 * {@link Entity} values, and to correctly write any changes values.
 */
public class GenericEditorView extends RelativeLayout implements Editor, View.OnClickListener {
    protected static final int RES_FIELD = R.layout.item_editor_field;
    protected static final int RES_LABEL_ITEM = android.R.layout.simple_list_item_1;
    protected static final int RES_WIFI_FIELD = R.layout.item_editor_wifi_field;
    protected static final int RES_AUTOCOMPLETE_FIELD = R.layout.item_editor_autocomplete_field;

    public static final String TAG = "GenericEditorView";

    protected LayoutInflater mInflater;

    protected static final int INPUT_TYPE_CUSTOM = EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;

    protected TextView mLabel;
    protected ViewGroup mFields;
    protected View mDelete;
    protected View mMore;
    protected View mLess;

    protected int mWifiFieldButtonId;
    private LociWifiFingerprint mWifiFingerprint;
    private String mWifiFingerprintTimeStamp;

    protected DataKind mKind;
    protected ValuesDelta mEntry;
    protected EntityDelta mState;
    protected boolean mReadOnly;

    protected boolean mHideOptional = true;

    protected EditType mType;

    private ViewIdGenerator mViewIdGenerator;

    public GenericEditorView(Context context) {
        super(context);
    }

    public GenericEditorView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /** {@inheritDoc} */
    @Override
    protected void onFinishInflate() {
        mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        mLabel = (TextView) findViewById(R.id.edit_label);
        mLabel.setOnClickListener(this);

        mFields = (ViewGroup) findViewById(R.id.edit_fields);

        mDelete = findViewById(R.id.edit_delete);
        mDelete.setOnClickListener(this);

        mMore = findViewById(R.id.edit_more);
        mMore.setOnClickListener(this);

        mLess = findViewById(R.id.edit_less);
        mLess.setOnClickListener(this);
    }

    protected EditorListener mListener;

    public void setEditorListener(EditorListener listener) {
        mListener = listener;
    }

    public void setDeletable(boolean deletable) {
        mDelete.setVisibility(deletable ? View.VISIBLE : View.INVISIBLE);
    }

    @Override
    public void setEnabled(boolean enabled) {
        mLabel.setEnabled(enabled);
        final int count = mFields.getChildCount();
        for (int pos = 0; pos < count; pos++) {
            final View v = mFields.getChildAt(pos);
            v.setEnabled(enabled);
        }
        mMore.setEnabled(enabled);
        mLess.setEnabled(enabled);
    }

    /**
     * Build the current label state based on selected {@link EditType} and
     * possible custom label string.
     */
    private void rebuildLabel() {
        // Handle undetected types
        if (mType == null) {
            mLabel.setText(R.string.unknown);
            return;
        }

        if (mType.customColumn != null) {
            // Use custom label string when present
            final String customText = mEntry.getAsString(mType.customColumn);
            if (customText != null) {
                mLabel.setText(customText);
                return;
            }
        }

        // Otherwise fall back to using default label
        mLabel.setText(mType.labelRes);
    }

    /** {@inheritDoc} */
    public void onFieldChanged(String column, String value) {
        // Field changes are saved directly
        mEntry.put(column, value);
        if (mListener != null) {
            mListener.onRequest(EditorListener.FIELD_CHANGED);
        }

        //Log.i("GenericEditorView", "onFieldChanged:" + mState.toString());
    }

    public boolean isAnyFieldFilledOut() {
        int childCount = mFields.getChildCount();
        for (int i = 0; i < childCount; i++) {
            EditText editorView = (EditText) mFields.getChildAt(i);
            if (!TextUtils.isEmpty(editorView.getText())) {
                return true;
            }
        }
        return false;
    }

    private void rebuildValues() {
        setValues(mKind, mEntry, mState, mReadOnly, mViewIdGenerator);
    }

    /**
     * Prepare this editor using the given {@link DataKind} for defining
     * structure and {@link ValuesDelta} describing the content to edit.
     */
    public void setValues(DataKind kind, ValuesDelta entry, EntityDelta state, boolean readOnly,
            ViewIdGenerator vig) {
        mKind = kind;
        mEntry = entry;
        mState = state;
        mReadOnly = readOnly;
        mViewIdGenerator = vig;

        setId(vig.getId(state, kind, entry, ViewIdGenerator.NO_VIEW_INDEX));

        final boolean enabled = !readOnly;

        //Log.d(TAG, "setValues: kind=" + mKind.mimeType);

        if (!entry.isVisible()) {
            // Hide ourselves entirely if deleted
            setVisibility(View.GONE);
            return;
        } else {
            setVisibility(View.VISIBLE);
        }

        // Display label selector if multiple types available
        final boolean hasTypes = EntityModifier.hasEditTypes(kind);
        mLabel.setVisibility(hasTypes ? View.VISIBLE : View.GONE);
        mLabel.setEnabled(enabled);
        if (hasTypes) {
            mType = EntityModifier.getCurrentType(entry, kind);
            rebuildLabel();
        }

        // Build out set of fields
        mFields.removeAllViews();
        boolean hidePossible = false;
        int n = 0;

        if (mKind.mimeType.equals(WifiFingerprint.CONTENT_ITEM_TYPE)) {

            //Log.d(TAG, "setValues: Wifi");

            for (EditField field : kind.fieldList) {
                Button fieldView = (Button) mInflater.inflate(RES_WIFI_FIELD, mFields, false);

                mWifiFieldButtonId = vig.getId(state, kind, entry, n++);

                fieldView.setId(mWifiFieldButtonId);

                final String column = field.column;
                final String value = entry.getAsString(column);
                fieldView.setText("Fingerprint on " + MyDateUtils.getAbrv_MMM_d_h_m(new Long(value)));

                final String extra1column = field.extra1;
                final String extra1value = entry.getAsString(extra1column);
                try {
                    mWifiFingerprint = new LociWifiFingerprint(extra1value);
                    mWifiFingerprintTimeStamp = MyDateUtils.getDateFormatLong(new Long(value));
                } catch (JSONException e) {
                    MyLog.e(LociConfig.D.JSON, TAG, "LociWifiFingerprint parsing failed");
                    e.printStackTrace();
                }

                // Hide field when empty and optional value
                final boolean couldHide = (field.optional);
                final boolean willHide = (mHideOptional && couldHide);
                fieldView.setVisibility(willHide ? View.GONE : View.VISIBLE);
                fieldView.setEnabled(enabled);
                hidePossible = hidePossible || couldHide;

                fieldView.setOnClickListener(this);

                mFields.addView(fieldView);
            }
        } else if (mKind.mimeType.equals(Keyword.CONTENT_ITEM_TYPE)) {

            //Log.d(TAG, "setValues: Keywords");

            for (EditField field : kind.fieldList) {

                AutoCompleteTextView fieldView = (AutoCompleteTextView) mInflater.inflate(RES_AUTOCOMPLETE_FIELD,
                        mFields, false);
                fieldView.setId(vig.getId(state, kind, entry, n++));
                if (field.titleRes > 0) {
                    fieldView.setHint(field.titleRes);
                }
                int inputType = field.inputType;
                fieldView.setInputType(inputType);
                fieldView.setMinLines(field.minLines);

                // Read current value from state
                final String column = field.column;
                final String value = entry.getAsString(column);
                fieldView.setText(value);

                // Prepare listener for writing changes
                fieldView.addTextChangedListener(new TextWatcher() {
                    public void afterTextChanged(Editable s) {
                        // Trigger event for newly changed value
                        onFieldChanged(column, s.toString());
                    }

                    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                    }

                    public void onTextChanged(CharSequence s, int start, int before, int count) {
                    }
                });

                // Hide field when empty and optional value
                final boolean couldHide = (field.optional);
                final boolean willHide = (mHideOptional && couldHide);
                fieldView.setVisibility(willHide ? View.GONE : View.VISIBLE);
                fieldView.setEnabled(enabled);
                hidePossible = hidePossible || couldHide;

                String[] usedKeywords = getResources().getStringArray(R.array.keyword_default);

                LociDbUtils myDb = new LociDbUtils(getContext());
                ArrayList<String> suggestedKeywords = myDb.getSavedKeywords();
                HashSet<String> suggestedKeywordsSet = new HashSet<String>();

                for (String keyword : suggestedKeywords) {
                    suggestedKeywordsSet.add(keyword);
                }

                //Log.d(TAG, "size of usedKeywords : " + usedKeywords.length);
                //Log.d(TAG, "size of suggestedKeywords : " + suggestedKeywords.size());

                for (String usedKeyword : usedKeywords) {
                    if (!suggestedKeywordsSet.contains(usedKeyword))
                        suggestedKeywords.add(usedKeyword);
                }

                //Log.d(TAG, "size of suggestedKeywords : " + suggestedKeywords.size());

                Collections.sort(suggestedKeywords);

                ArrayAdapter<String> adapter = new ArrayAdapter<String>(this.getContext(),
                        R.layout.item_suggestion_list, suggestedKeywords);
                fieldView.setAdapter(adapter);
                fieldView.setThreshold(0);

                mFields.addView(fieldView);
            }

        } else {

            //Log.d(TAG, "General Types...");

            for (EditField field : kind.fieldList) {
                // Inflate field from definition
                EditText fieldView = (EditText) mInflater.inflate(RES_FIELD, mFields, false);
                fieldView.setId(vig.getId(state, kind, entry, n++));
                if (field.titleRes > 0) {
                    fieldView.setHint(field.titleRes);
                }
                int inputType = field.inputType;
                fieldView.setInputType(inputType);
                if (inputType == InputType.TYPE_CLASS_PHONE) {
                    fieldView.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
                }
                fieldView.setMinLines(field.minLines);

                // Read current value from state
                final String column = field.column;
                final String value = entry.getAsString(column);
                fieldView.setText(value);

                //Log.d(TAG, "setValues: column=" + column);
                //Log.d(TAG, "setValues: value=" + value);

                // Prepare listener for writing changes
                fieldView.addTextChangedListener(new TextWatcher() {
                    public void afterTextChanged(Editable s) {
                        // Trigger event for newly changed value
                        onFieldChanged(column, s.toString());
                    }

                    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                    }

                    public void onTextChanged(CharSequence s, int start, int before, int count) {
                    }
                });

                // Hide field when empty and optional value
                final boolean couldHide = (field.optional);
                final boolean willHide = (mHideOptional && couldHide);
                fieldView.setVisibility(willHide ? View.GONE : View.VISIBLE);
                fieldView.setEnabled(enabled);
                hidePossible = hidePossible || couldHide;

                mFields.addView(fieldView);
            }
        }

        // When hiding fields, place expandable
        if (hidePossible) {
            mMore.setVisibility(mHideOptional ? View.VISIBLE : View.GONE);
            mLess.setVisibility(mHideOptional ? View.GONE : View.VISIBLE);
        } else {
            mMore.setVisibility(View.GONE);
            mLess.setVisibility(View.GONE);
        }
        mMore.setEnabled(enabled);
        mLess.setEnabled(enabled);
    }

    /** {@inheritDoc} */
    public void onClick(View v) {

        //Log.i(TAG, "onClick");

        switch (v.getId()) {
        case R.id.edit_delete: {

            //Log.i(TAG, "edit_delete");

            // Keep around in model, but mark as deleted
            mEntry.markDeleted();

            // Remove editor from parent view
            final ViewGroup parent = (ViewGroup) getParent();
            parent.removeView(this);

            if (mListener != null) {
                // Notify listener when present
                mListener.onDeleted(this);
            }
            break;
        }
        case R.id.edit_more:
        case R.id.edit_less: {
            //Log.i(TAG, "edit_more_less");           
            mHideOptional = !mHideOptional;
            rebuildValues();
            break;
        }
        default:
            //Log.i(TAG, "default");           

            if (mWifiFieldButtonId == v.getId()) {
                //Log.i("GenericEditorView", "onClick(): wifi button. " + v.getId());
                createWifiDialog();
            }
        }
    }

    private static class SavedState extends BaseSavedState {
        public boolean mHideOptional;
        public int[] mVisibilities;

        SavedState(Parcelable superState) {
            super(superState);
        }

        private SavedState(Parcel in) {
            super(in);
            mVisibilities = new int[in.readInt()];
            in.readIntArray(mVisibilities);
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeInt(mVisibilities.length);
            out.writeIntArray(mVisibilities);
        }

        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }

    /**
     * Saves the visibility of the child EditTexts, and mHideOptional.
     */
    @Override
    protected Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState ss = new SavedState(superState);

        ss.mHideOptional = mHideOptional;

        final int numChildren = mFields.getChildCount();
        ss.mVisibilities = new int[numChildren];
        for (int i = 0; i < numChildren; i++) {
            ss.mVisibilities[i] = mFields.getChildAt(i).getVisibility();
        }

        return ss;
    }

    /**
     * Restores the visibility of the child EditTexts, and mHideOptional.
     */
    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());

        mHideOptional = ss.mHideOptional;

        int numChildren = Math.min(mFields.getChildCount(), ss.mVisibilities.length);
        for (int i = 0; i < numChildren; i++) {
            mFields.getChildAt(i).setVisibility(ss.mVisibilities[i]);
        }
    }

    private void createWifiDialog() {

        //Log.i("GenericEditor", "createWifiDialog");

        Context context = getContext();
        LayoutInflater factory = LayoutInflater.from(context);

        final View wifiView = factory.inflate(R.layout.dialog_wifi_view, null);

        final TableLayout wifiTable = (TableLayout) wifiView.findViewById(R.id.wifi_table);

        updateWifiList(wifiTable, mWifiFingerprint);

        new AlertDialog.Builder(context).setIcon(R.drawable.ic_settings_wireless)
                .setTitle(mWifiFingerprintTimeStamp).setView(wifiView)
                .setPositiveButton("Close", new DialogInterface.OnClickListener() {

                    public void onClick(DialogInterface dialog, int which) {
                        // TODO Auto-generated method stub

                    }
                }).create().show();

    }

    private ArrayList<View> mAddedRows = new ArrayList<View>();

    private void updateWifiList(TableLayout table, LociWifiFingerprint wifi) {

        ArrayList<WifiViewListItem> items = new ArrayList<WifiViewListItem>();

        HashMap<String, APInfoMapItem> apMap = wifi.getAps();
        Set<String> keys = apMap.keySet();
        Iterator<String> iter = keys.iterator();
        while (iter.hasNext()) {
            String bssid = iter.next();
            APInfoMapItem ap = apMap.get(bssid);
            items.add(new WifiViewListItem(bssid, ap.ssid, ap.rss, ap.count, ap.rssBuckets));
        }

        Collections.sort(items);

        table.setColumnCollapsed(0, false);
        table.setColumnCollapsed(1, true);
        table.setColumnShrinkable(0, true);

        for (int i = 0; i < mAddedRows.size(); i++) {
            table.removeView(mAddedRows.get(i));
        }
        mAddedRows.clear();

        int totalCount = wifi.getScanCount();

        Context context = getContext();

        for (WifiViewListItem item : items) {
            TableRow row = new TableRow(context);

            TextView ssidView = new TextView(context);
            ssidView.setText(item.ssid);
            //ssidView.setText("very very very veryvery very very very very very");
            ssidView.setPadding(2, 2, 2, 2);
            ssidView.setTextColor(0xffffffff);

            TextView bssidView = new TextView(context);
            bssidView.setText(item.bssid);
            bssidView.setPadding(2, 2, 2, 2);
            bssidView.setTextColor(0xffffffff);

            TextView cntView = new TextView(context);
            cntView.setText("" + (item.count * 100) / totalCount);
            cntView.setPadding(2, 2, 2, 2);
            cntView.setGravity(Gravity.CENTER);
            cntView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12);

            TextView rssView = new TextView(context);
            rssView.setText("" + item.rss);
            rssView.setPadding(2, 2, 6, 2);
            rssView.setGravity(Gravity.CENTER);
            rssView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12);

            row.addView(ssidView, new TableRow.LayoutParams(0));
            row.addView(bssidView, new TableRow.LayoutParams(1));
            row.addView(cntView, new TableRow.LayoutParams(2));
            row.addView(rssView, new TableRow.LayoutParams(3));

            //Log.d(TAG, item.ssid);
            for (int i = 0; i < item.rssBuckets.length; i++) {
                TextView box = new TextView(context);
                box.setText("  ");
                box.setGravity(Gravity.RIGHT);
                box.setPadding(2, 2, 2, 2);
                box.setHeight(15);
                box.setGravity(Gravity.CENTER_VERTICAL);

                float colorVal = 256 * ((float) item.rssBuckets[i] / (float) wifi.getScanCount());
                //Log.d(TAG, "colorVal=" + (int) colorVal + ", " + item.histogram[i]);
                int colorValInt = ((int) colorVal) - 1;
                if (colorValInt < 0)
                    colorValInt = 0;

                box.setBackgroundColor(0xff000000 + colorValInt);//+ 0x000000ff * (item.histogram[i]/totScan));
                box.setTextColor(0xffffffff);

                row.addView(box, new TableRow.LayoutParams(4 + i));
            }

            row.setGravity(Gravity.CENTER);

            table.addView(row, new TableLayout.LayoutParams());
            table.setColumnStretchable(3, true);
            mAddedRows.add(row);
        }

    }

}