com.numenta.taurus.instance.InstanceDetailPageFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.numenta.taurus.instance.InstanceDetailPageFragment.java

Source

/*
 * Numenta Platform for Intelligent Computing (NuPIC)
 * Copyright (C) 2015, Numenta, Inc.  Unless you have purchased from
 * Numenta, Inc. a separate commercial license for this software code, the
 * following terms and conditions apply:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero Public License version 3 as
 * published by the Free Software Foundation.
 *
 * 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 Affero Public License for more details.
 *
 * You should have received a copy of the GNU Affero Public License
 * along with this program.  If not, see http://www.gnu.org/licenses.
 *
 * http://numenta.org/licenses/
 *
 */

package com.numenta.taurus.instance;

import com.numenta.core.utils.DataUtils;
import com.numenta.core.utils.Pair;
import com.numenta.taurus.R;
import com.numenta.taurus.TaurusApplication;
import com.numenta.taurus.chart.TimeSliderView;
import com.numenta.taurus.data.MarketCalendar;
import com.numenta.taurus.metric.MetricListFragment;
import com.numenta.taurus.metric.MetricType;

import android.app.Fragment;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;

import java.util.Date;
import java.util.EnumSet;
import java.util.List;
import java.util.ListIterator;

public class InstanceDetailPageFragment extends Fragment {

    private InstanceAnomalyChartData _chartData;

    private AsyncTask<InstanceAnomalyChartData, Void, Boolean> _instanceLoadTask;

    private MetricListFragment _metricListFrag;

    private InstanceAnomalyChartFragment _instanceChartFrag;

    private TimeSliderView _timeView;

    private CheckBox _marketHoursCheckbox;

    private boolean _scrolling;

    private boolean _collapseAfterHours;

    /**
     * Handles date/time scrolling
     */
    final class GestureListener implements GestureDetector.OnGestureListener {

        private final View _view;

        long _initialTimestamp;

        private int _pixelsBerBar;

        public GestureListener(View view) {
            this._view = view;
        }

        @Override
        public boolean onDown(MotionEvent e) {
            _pixelsBerBar = _view.getMeasuredWidth() / TaurusApplication.getTotalBarsOnChart();
            Date endDate = _chartData.getEndDate();
            _initialTimestamp = endDate == null ? TaurusApplication.getDatabase().getLastTimestamp()
                    : endDate.getTime();
            _scrolling = false;
            // Do not scale the chart while scrolling
            _metricListFrag.setRefreshScale(false);
            return false;
        }

        @Override
        public void onShowPress(MotionEvent e) {

        }

        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            return false;
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            int distance = Math.round(distanceX / _pixelsBerBar);
            if (_chartData != null && _chartData.hasData() && distance != 0) {
                // Calculate valid scroll range
                long maxDate = _chartData.getLastDbTimestamp();
                long minDate = maxDate - (TaurusApplication.getNumberOfDaysToSync() - 1) * DataUtils.MILLIS_PER_DAY;

                MarketCalendar marketHours = TaurusApplication.getMarketCalendar();
                List<Pair<Long, Float>> bars = _chartData.getData();
                long time;
                if (distance < 0) {
                    // If scrolling backwards, just get the date from the new bar position
                    int pos = Math.max(0, bars.size() + distance - 1);
                    time = bars.get(pos).first;

                    // Check if collapsing market hours
                    if (_marketHoursCheckbox.isChecked()) {
                        if (!marketHours.isOpen(time)) {
                            // Find previous open hour
                            List<Pair<Long, Long>> closed = marketHours.getClosedHoursForPeriod(time, time);
                            if (closed != null && !closed.isEmpty()) {
                                time = closed.get(0).first;
                            }
                        }
                    }
                    if (time < minDate) {
                        time = minDate;
                    }
                } else {
                    // If scrolling forward, calculate new date from the last bar
                    time = bars.get(bars.size() - 1).first + distance * _chartData.getAggregation().milliseconds();

                    // Check if collapsing market hours
                    if (_marketHoursCheckbox.isChecked()) {
                        if (!marketHours.isOpen(time)) {
                            // Find next open hour
                            List<Pair<Long, Long>> closed = marketHours.getClosedHoursForPeriod(time, time);
                            if (closed != null && !closed.isEmpty()) {
                                time = closed.get(0).second;
                            }
                        }
                    }
                    // Make sure to not scroll past the end of the data
                    if (time > maxDate) {
                        time = maxDate;
                    }
                }
                _chartData.setEndDate(time);
                reloadChartData();
            }
            _scrolling = distance != 0;
            return _scrolling;
        }

        @Override
        public void onLongPress(MotionEvent e) {

        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            return false;
        }
    }

    final class TouchListener implements View.OnTouchListener {

        // Attach gesture detector to values chart handling scrolling.
        final GestureDetector _gestureDetector;

        // The current view
        final View _view;

        public TouchListener(View view) {
            _view = view;
            _gestureDetector = new GestureDetector(getActivity(), new GestureListener(_view));
        }

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
                // Fall through
            case MotionEvent.ACTION_CANCEL:
                // Done scrolling, refresh the chart scale
                // Add update request to message queue otherwise the ListView may loose focus
                // and MetricListFragment#onListItemClick will not be called. See TAUR-685
                v.post(new Runnable() {
                    @Override
                    public void run() {
                        _metricListFrag.setRefreshScale(true);
                    }
                });
                break;
            default:
                break;
            }

            // Detect scroll gestures on the metric detail chart
            return _gestureDetector.onTouchEvent(event) || _scrolling;
        }
    }

    public void setRowData(InstanceAnomalyChartData row) {
        if (_chartData != null && _chartData.equals(row)) {
            return;
        }
        if (_instanceLoadTask != null) {
            _instanceLoadTask.cancel(true);
            _instanceLoadTask = null;
        }

        _chartData = row;
        reloadChartData();
    }

    void updateServerHeader() {
        if (_chartData == null) {
            return;
        }
        if (_instanceChartFrag == null) {
            return;
        }

        // Update server header
        _instanceChartFrag.setChartData(_chartData);

        // Update time slider
        Date endDate = _chartData.getEndDate();
        long endTime;
        if (endDate == null) {
            endTime = System.currentTimeMillis();
        } else {
            endTime = endDate.getTime();
        }
        _timeView.setAggregation(_chartData.getAggregation());
        _timeView.setEndDate(endTime);

        // Check if need to collapse to market hours
        if (_collapseAfterHours) {
            EnumSet<MetricType> anomalousMetrics = _chartData.getAnomalousMetrics();
            if (anomalousMetrics.contains(MetricType.TwitterVolume)) {
                // Collapse to market hours if twitter anomalies occurred during market hours
                MarketCalendar marketCalendar = TaurusApplication.getMarketCalendar();
                boolean collapsed = true;

                // Check if is there any twitter anomaly on the last visible bars
                List<Pair<Long, Float>> data = _chartData.getData();
                ListIterator<Pair<Long, Float>> iterator = data.listIterator(data.size());
                for (int i = 0; i < TaurusApplication.getTotalBarsOnChart() && iterator.hasPrevious(); i++) {
                    Pair<Long, Float> value = iterator.previous();
                    if (value != null && value.second != null && !Float.isNaN(value.second)) {
                        double scaled = DataUtils.logScale(value.second);
                        if (scaled >= TaurusApplication.getYellowBarFloor()
                                && !marketCalendar.isOpen(value.first)) {
                            // Found anomaly, don't collapse
                            collapsed = false;
                            break;
                        }
                    }
                }
                _marketHoursCheckbox.setChecked(collapsed);
            } else {
                // Collapse to market hours if we only have stock anomalies
                _marketHoursCheckbox.setChecked(true);
            }
            // Prevent collapsing during scroll
            _collapseAfterHours = false;
        }
    }

    public void updateRowData() {
        if (_chartData == null) {
            return;
        }
        // Update Metric List
        if (_metricListFrag == null) {
            return;
        }
        _metricListFrag.setChartData(_chartData);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_instance_detail_page, container, false);

        _metricListFrag = (MetricListFragment) getFragmentManager().findFragmentById(R.id.metric_list);
        View metricListView = _metricListFrag.getListView();
        metricListView.setOnTouchListener(new TouchListener(metricListView));

        View instanceChartView = view.findViewById(R.id.instance_anomaly_chart);
        instanceChartView.setOnTouchListener(new TouchListener(instanceChartView));
        _instanceChartFrag = (InstanceAnomalyChartFragment) instanceChartView.getTag();

        _timeView = (TimeSliderView) view.findViewById(R.id.time_slider);
        _timeView.setCollapsed(false);

        _marketHoursCheckbox = (CheckBox) view.findViewById(R.id.market_hours_checkbox);
        _marketHoursCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                _timeView.setCollapsed(isChecked);
                _instanceChartFrag.setCollapsed(isChecked);
                _metricListFrag.setCollapsed(isChecked);
            }
        });

        // Collapse after hours when opening the view
        _collapseAfterHours = true;
        return view;
    }

    private void reloadChartData() {
        if (_instanceLoadTask != null) {
            _instanceLoadTask.cancel(true);
            _instanceLoadTask = null;
        }

        if (_chartData != null) {
            _instanceLoadTask = new AsyncTask<InstanceAnomalyChartData, Void, Boolean>() {
                @Override
                protected Boolean doInBackground(InstanceAnomalyChartData... params) {
                    InstanceAnomalyChartData chartData = params[0];
                    return !isCancelled() && chartData != null && chartData.load();
                }

                @Override
                protected void onPostExecute(Boolean update) {
                    _instanceLoadTask = null;
                    if (update) {
                        updateServerHeader();
                        updateRowData();
                    }
                }
            }.executeOnExecutor(TaurusApplication.getWorkerThreadPool(), _chartData);
        }
    }

    /*
     * (non-Javadoc)
     * @see android.support.v4.app.Fragment#onStop()
     */
    @Override
    public void onStop() {
        super.onStop();
        if (_instanceLoadTask != null) {
            _instanceLoadTask.cancel(true);
            _instanceLoadTask = null;
        }
    }
}