Android Open Source - virtual-stat Stat List Fragment






From Project

Back to project page virtual-stat.

License

The source code is released under:

Copyright (c) 2014, Delta Controls Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are...

If you think the Android project virtual-stat listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/* Copyright (c) 2014, Delta Controls Inc.
All rights reserved.// w  w w  .j ava  2  s. c  om

Redistribution and use in source and binary forms, with or without modification, 
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this 
list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this 
list of conditions and the following disclaimer in the documentation and/or other 
materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may 
be used to endorse or promote products derived from this software without specific 
prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/
/**
 * StatListFragment.java
 */

package com.deltacontrols.virtualstat.fragment;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;

import org.apache.http.HttpStatus;
import org.json.JSONObject;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.SoundEffectConstants;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;
import android.widget.Filter;
import android.widget.ImageView;
import android.widget.ListView;

import com.deltacontrols.eweb.support.api.EwebConnection;
import com.deltacontrols.eweb.support.api.FetchJSON;
import com.deltacontrols.eweb.support.interfaces.GenericCallback;
import com.deltacontrols.virtualstat.App;
import com.deltacontrols.virtualstat.R;
import com.deltacontrols.virtualstat.StatListItem;
import com.deltacontrols.virtualstat.StatListItemAdapter;
import com.deltacontrols.virtualstat.UIFactory;
import com.deltacontrols.virtualstat.VirtualStat;
import com.deltacontrols.virtualstat.VirtualStat.ExposeVirtualStat;
import com.deltacontrols.virtualstat.activity.SummaryActivity;
import com.deltacontrols.virtualstat.controls.ToggleBar;
import com.deltacontrols.virtualstat.controls.ToggleBar.ToggleInteractionListener;

/**
 * StatListFragment
 * 
 * Controls user interaction with the stat list fragment. Responsible for loading in stat data from eWEB, caching stat data, and allowing 
 * users to load stats based on list selections.
 */
public class StatListFragment extends Fragment implements VirtualStat.UseVirtualStat {

    // --------------------------------------------------------------------------------
    // Static helper classes
    // --------------------------------------------------------------------------------
    /**
     * Caching class helper - cache Stat JSON strings to avoid extra requests. Made static so other activities can update the cached JSON to 
     * include updated values
     */
    public static class StatListCache {
        private static HashMap<String, String> cachedJSONStrs = new HashMap<String, String>();

        public static String get(String id) {
            return cachedJSONStrs.containsKey(id) ? cachedJSONStrs.get(id) : null;
        }

        public static void clear() {
            cachedJSONStrs.clear();
        }

        public static void set(String id, String json) {
            cachedJSONStrs.put(id, json);
        }

    }

    /**
     * Demo values for stats
     */
    public static class Demo {
        /*
         * getAllStats Mimics EwebConnection.getAllStats.
         */
        public static FetchJSON.Result getAllStats() {
            // It's fake! We're ok!
            FetchJSON.Result result = new FetchJSON.Result();
            result.success = true;
            result.statusCode = HttpStatus.SC_OK;

            // Fake values
            String[][] demoArr = new String[][] {
                    new String[] { "Training Room Demo", "0000-0000-0001",
                            "TEMP_SP", "16", "//Demo/100.AV1",
                            "TEMP", "18", "//Demo/100.AI1",
                            "LIGHTS1", "75", "//Demo/100.AV2",
                            "LIGHTS2", "75", "//Demo/100.AV3",
                            "LIGHTS3", "Active", "//Demo/100.BV1",
                            "LIGHTS4", "Active", "//Demo/100.BV2",
                            "FAN", "4", "//Demo/100.MV1",
                            "BLINDS", "50", "//Demo/100.AV3" },
                    new String[] { "Front Hallway Demo", "0000-0000-0002",
                            "TEMP_SP", "20", "//Demo/100.AV1",
                            "TEMP", "14", "//Demo/100.AI1",
                            "LIGHTS1", "100", "//Demo/100.AV2",
                            "LIGHTS2", "Active", "//Demo/100.BV1",
                            "LIGHTS3", "", "",
                            "LIGHTS4", "", "",
                            "FAN", "", "",
                            "BLINDS", "", "" },
                    new String[] { "Emma's Office Demo", "0000-0000-0003",
                            "TEMP_SP", "22", "//Demo/100.AV1",
                            "TEMP", "", "",
                            "LIGHTS1", "0", "//Demo/100.BV1",
                            "LIGHTS2", "", "",
                            "LIGHTS3", "", "",
                            "LIGHTS4", "", "",
                            "FAN", "", "",
                            "BLINDS", "", "" },
                    new String[] { "Liam's Office Demo", "0000-0000-0004",
                            "TEMP_SP", "19", "//Demo/100.AV1",
                            "TEMP", "", "",
                            "LIGHTS1", "0", "//Demo/100.AV1",
                            "LIGHTS2", "Inactive", "//Demo/100.BV1",
                            "LIGHTS3", "", "",
                            "LIGHTS4", "", "",
                            "FAN", "", "",
                            "BLINDS", "", "" }
            };

            // System format (based on result from eweb api/systems/list request
            String formatString =
                    " \"%s\": { id: \"%s\", " + // name, id
                            "\"%s\": { value: \"%s\", ref: \"%s\" }, " + // TEMP_SP
                            "\"%s\": { value: \"%s\", ref: \"%s\" }, " + // TEMP
                            "\"%s\": { value: \"%s\", ref: \"%s\" }, " + // LIGHTS1
                            "\"%s\": { value: \"%s\", ref: \"%s\" }, " + // LIGHTS2
                            "\"%s\": { value: \"%s\", ref: \"%s\" }, " + // LIGHTS3
                            "\"%s\": { value: \"%s\", ref: \"%s\" }, " + // LIGHTS4
                            "\"%s\": { value: \"%s\", ref: \"%s\" }, " + // FAN
                            "\"%s\": { value: \"%s\", ref: \"%s\" }  " + // BLINDS
                            "},";

            StringBuffer fakeData = new StringBuffer();
            JSONObject fakeJSON;
            try {
                fakeData.append("{ ");

                for (int i = 0; i < demoArr.length; i++) {
                    fakeData.append(String.format(formatString, (Object[]) demoArr[i]));
                }

                fakeData.deleteCharAt(fakeData.length() - 1); // remove last ,
                fakeData.append("}");
                fakeJSON = new JSONObject(fakeData.toString());
            } catch (Exception e) {
                fakeJSON = new JSONObject();
            }

            result.json = fakeJSON;

            // Pass fake data back
            return result;
        }
    }

    // --------------------------------------------------------------------------------
    // Properties
    // --------------------------------------------------------------------------------
    Context ctx;
    InputMethodManager inputManager;

    // List
    ListView statList;
    View statListHeader;
    Filter statFilter;
    StatListItemAdapter listAdapter;
    ArrayList<StatListItem> listItemsArray = new ArrayList<StatListItem>();

    // Outlets
    EditText searchText;
    ToggleBar switcher_toggle_onlist;
    ImageView icon_refresh;

    // VirtualStat (model) Interface delegate
    private VirtualStat virtualStatDelegate = null;

    /**
     * Listener used to handle clicks on the stat list. When an item in the stat list is selected, load the stat's points so that the summary 
     * fragment can show the data. Also, depending on screen size, hide the soft keyboard automatically.
     */
    private OnItemClickListener selectListItemListener = new OnItemClickListener() {
        /*
         * (non-Javadoc) When item is clicked in the stat list, attempt to load the stat that corresponds to the selection.
         * 
         * @see android.widget.AdapterView.OnItemClickListener#onItemClick(android.widget.AdapterView, android.view.View, int, long)
         */
        public void onItemClick(AdapterView<?> parent, final View view, int position, long id) {
            Object o = statList.getItemAtPosition(position);

            if (o instanceof StatListItem) {
                // Get ID from the stat list item clicked and use it to load the stat.
                String statId = ((StatListItem) o).id;
                loadStatJSON(statId);

                // Auto hide the soft keyboard
                inputManager.hideSoftInputFromWindow(searchText.getWindowToken(), 0);

                // If switcher_toggle_onlist exists then we are on a 4" device, so auto hide the stat list as well.
                if (switcher_toggle_onlist != null) {
                    hideStatList();
                }
            }
        }
    };

    // --------------------------------------------------------------------------------
    // Life cycle
    // --------------------------------------------------------------------------------
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getStatDelegate();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        ctx = getActivity();
        inputManager = (InputMethodManager) ctx.getSystemService(Context.INPUT_METHOD_SERVICE);

        View view = inflater.inflate(R.layout.fragment_stat_list, container, false);
        icon_refresh = (ImageView) view.findViewById(R.id.icon_refresh);

        // Setup list
        statList = (ListView) view.findViewById(R.id.statListView);
        statList.setOnItemClickListener(selectListItemListener);

        // switcher_toggle_onlist exists on < large screens (where the list takes up the full layout)
        // If this toggler exists then hookup gesture listeners.
        // Note: Currently, toggler only visible if list is being shown; this means we only have to listen for a tap/swipe down
        // and implement the hide list functionality.
        switcher_toggle_onlist = (ToggleBar) view.findViewById(R.id.switcher_toggle_onlist);
        if (switcher_toggle_onlist != null) {
            switcher_toggle_onlist.setToggleInteractionListener(new ToggleInteractionListener() {
                @Override
                public void onTap() {
                    hideStatList();
                }

                @Override
                public void onSwipeUp() {
                }

                @Override
                public void onSwipeDown() {
                    hideStatList();
                }
            });
        }

        // Search text will apply a real time filter on the stat list.
        searchText = ((EditText) view.findViewById(R.id.statListFilterText));
        searchText.addTextChangedListener(new TextWatcher() {
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }

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

            @Override
            public void afterTextChanged(Editable s) {
                applyFilterToList(s.toString());
            }
        });

        // Setup clear icon
        ((ImageView) view.findViewById(R.id.icon_clear)).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                getView().playSoundEffect(SoundEffectConstants.CLICK);
                searchText.setText(null);
            }
        });

        icon_refresh.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                getView().playSoundEffect(SoundEffectConstants.CLICK);
                updateList();
            }
        });

        return view;
    }

    @Override
    public void onResume() {
        super.onResume();

        if (listItemsArray.isEmpty()) {
            updateList(); // Do an initial update of the list; all subsequent updates must be triggered through the refresh button
        }
        else {
            setListAdapter(); // If coming back from a long period away, reset the list.
        }
    }

    // --------------------------------------------------------------------------------
    // Methods
    // --------------------------------------------------------------------------------
    /**
     * Listener used to handle the result of a request for the stat list. Loads the stat list with a given JSONObject containing a list of 
     * systems (from either eWEB or with demo data)
     */
    private GenericCallback<FetchJSON.Result> getListListener = new GenericCallback<FetchJSON.Result>() {
        @SuppressWarnings("unchecked")
        @Override
        public void onCallback(FetchJSON.Result result) {
            try {
                String name, id, jsonStr;
                Object value;
                StatListItem singleListItem;
                JSONObject valueObj;

                showLoadingIcon(false);
                listItemsArray.clear();

                // Iterate over system result to get each system.
                for (Iterator<String> iter = result.json.keys(); iter.hasNext();) {
                    try {
                        name = iter.next();
                        value = result.json.get(name);
                        valueObj = (JSONObject) value;

                        // Cache based on ID (not name)
                        id = valueObj.getString("id");

                        // Add stat into the list of stats, showing the name, but indexing on ID.
                        singleListItem = new StatListItem(name, id);
                        listItemsArray.add(singleListItem);

                        // Since current web response contains full system info, add to cache to avoid a second request.
                        // Cache json string, including name so that we can easily parse it out later.
                        jsonStr = "{ \"" + name + "\" :" + valueObj.toString() + "}";
                        StatListCache.cachedJSONStrs.put(id, jsonStr);
                    }
                    catch (Exception e) {
                        continue;
                    }
                }

                // Sort list alphabetically by name
                Collections.sort(listItemsArray, new Comparator<StatListItem>() {
                    public int compare(StatListItem o1, StatListItem o2) {
                        return o1.name.toLowerCase().compareTo(o2.name.toLowerCase());
                    }
                });

                setListAdapter();

                // If nothing selected, default to show first.
                if (virtualStatDelegate.ID.isEmpty()) {
                    loadFirstItemInList();
                }

            }
            catch (Exception e) {
                Log.e(App.TAG, "Error in getListListener: " + e.getMessage());
            }
        }
    };

    /**
     * Update the stat list with live or demo data depending on the connection to eWEB.
     */
    private void updateList() {
        // Get data from eWEB
        EwebConnection eweb = App.getEwebConnection();

        if (App.getDemoMode()) {
            // Get demo values
            getListListener.onCallback(Demo.getAllStats());
        }
        else if (eweb.isConnected()) {
            // Attempt to talk to eWEB
            showLoadingIcon(true);
            App.getStat(null, getListListener);
        }
    }

    /**
     * (Re)sets the list adapter with the items in listItemsArray
     */
    @SuppressWarnings("unchecked")
    private void setListAdapter() {
        // Note, make a clone of the full data list; because the adapter will keep a reference to to data, and that data may become filtered, we want
        // to preserve the original list for a case when we may want to reset it.
        ArrayList<StatListItem> observableData = (ArrayList<StatListItem>) listItemsArray.clone();

        if (listAdapter == null) {
            listAdapter = new StatListItemAdapter(ctx, R.layout.view_summary_list_item_layout, observableData);
            statList.setAdapter(listAdapter);
            statFilter = ((StatListItemAdapter) listAdapter).getFilter();
        }
        else {
            listAdapter.updateData(observableData);
        }

        applyFilterToList(searchText.getText().toString());
    }

    /**
     * Applies the string as a filter to the items in the list.
     */
    private void applyFilterToList(String s) {
        if (statFilter != null) {
            statFilter.filter(s);
        }
    }

    private void showLoadingIcon(boolean show) {
        if (show) {
            icon_refresh.setImageResource(R.drawable.icon_refresh_loading);
            icon_refresh.startAnimation(AnimationUtils.loadAnimation(ctx, R.animator.login_image_rotate));
        }
        else {
            icon_refresh.setImageResource(R.drawable.icon_refresh);
            icon_refresh.clearAnimation();
        }
    }

    private void loadFirstItemInList() {
        Object o = statList.getItemAtPosition(0);

        if (o instanceof StatListItem) {
            // Get ID from the stat list item clicked and use it to load the stat.
            String statId = ((StatListItem) o).id;
            loadStatJSON(statId);
        }

    }

    // --------------------------------------------------------------------------------
    // Stat loading functionality
    // --------------------------------------------------------------------------------
    /**
     * Call into the parent activity to hide the stat list (since the parent owns the display of the stat list, it must know how to hide it).
     */
    private void hideStatList() {
        ((SummaryActivity) getActivity()).hideStatList();
    }

    /**
     * loadStatJSON Given an id, lookup the stat's JSON String in cache, and load it into the virtual stat delegate.
     * 
     * @param id The ID of the stat to load
     */
    private void loadStatJSON(String id) {
        try {
            String statStr = StatListCache.get(id);
            if (statStr == null) {
                loadFirstItemInList();
                return;
             // If no cache, return, cannot load.
            }

            // Save statStr in shared preferences so that it can be used by other activities in the future.
            App.saveStatIntoSharedPreferences(statStr);

            // Load stat with new statStr and start the data refresh.
            virtualStatDelegate.loadFromJSON(statStr);
            virtualStatDelegate.startDataRefresh(0); // get data.
        } 
        catch (Exception e) {
            UIFactory.deltaToast(getActivity(), "Error loading stat JSON", null);
        }
    }

    // --------------------------------------------------------------------------------
    // VirtualStat.UseVirtualStat Interface
    // --------------------------------------------------------------------------------
    @Override
    public void getStatDelegate() {
        Activity activity = getActivity();

        try {
            virtualStatDelegate = ((ExposeVirtualStat) activity).getCurrentStat();
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement ExposeVirtualStat");
        }
    }

    @Override
    public void updateWithDelegate() {
        // Stat list does not use delegate.
    }
}




Java Source Code List

com.deltacontrols.virtualstat.App.java
com.deltacontrols.virtualstat.LoginInfo.java
com.deltacontrols.virtualstat.StatListItemAdapter.java
com.deltacontrols.virtualstat.StatListItem.java
com.deltacontrols.virtualstat.StatListView.java
com.deltacontrols.virtualstat.UIFactory.java
com.deltacontrols.virtualstat.VirtualStat.java
com.deltacontrols.virtualstat.activity.LoginActivity.java
com.deltacontrols.virtualstat.activity.NFCFetchActivity.java
com.deltacontrols.virtualstat.activity.SettingsActivity.java
com.deltacontrols.virtualstat.activity.SingleStatControlActivity.java
com.deltacontrols.virtualstat.activity.SummaryActivity.java
com.deltacontrols.virtualstat.controls.AlertWindow.java
com.deltacontrols.virtualstat.controls.OnOffToggle.java
com.deltacontrols.virtualstat.controls.RotatingImageView.java
com.deltacontrols.virtualstat.controls.SeekBarWithValue.java
com.deltacontrols.virtualstat.controls.SlidingWindow.java
com.deltacontrols.virtualstat.controls.StackedStates.java
com.deltacontrols.virtualstat.controls.ToggleBar.java
com.deltacontrols.virtualstat.fragment.SingleStatControlBlinds.java
com.deltacontrols.virtualstat.fragment.SingleStatControlFan.java
com.deltacontrols.virtualstat.fragment.SingleStatControlLights.java
com.deltacontrols.virtualstat.fragment.SingleStatControlTabs.java
com.deltacontrols.virtualstat.fragment.SingleStatControlTemperature.java
com.deltacontrols.virtualstat.fragment.SingleStatSummaryFragment.java
com.deltacontrols.virtualstat.fragment.StatListFragment.java
com.deltacontrols.virtualstat.nfc.NFCHelper.java
com.deltacontrols.virtualstat.points.BlindsPoint.java
com.deltacontrols.virtualstat.points.FanOverridePoint.java
com.deltacontrols.virtualstat.points.FanPoint.java
com.deltacontrols.virtualstat.points.LightsPoint.java
com.deltacontrols.virtualstat.points.TempPoint.java
com.deltacontrols.virtualstat.points.VirtualStatPoint.java