Java tutorial
/* * DD-WRT Companion is a mobile app that lets you connect to, * monitor and manage your DD-WRT routers on the go. * * Copyright (C) 2014 Armel Soro * * This program 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. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Contact Info: Armel Soro <apps+ddwrt@rm3l.org> */ package org.rm3l.ddwrt.tiles.status.wan; import android.app.AlertDialog; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.os.Handler; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; import android.view.Gravity; import android.view.View; import android.widget.TextView; import android.widget.Toast; import com.actionbarsherlock.app.SherlockFragment; import com.google.common.base.Splitter; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableTable; import com.google.common.collect.Lists; import org.achartengine.ChartFactory; import org.achartengine.chart.BarChart; import org.achartengine.model.XYMultipleSeriesDataset; import org.achartengine.model.XYSeries; import org.achartengine.renderer.XYMultipleSeriesRenderer; import org.achartengine.renderer.XYSeriesRenderer; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.rm3l.ddwrt.R; import org.rm3l.ddwrt.exceptions.DDWRTNoDataException; import org.rm3l.ddwrt.exceptions.DDWRTTileAutoRefreshNotAllowedException; import org.rm3l.ddwrt.resources.conn.NVRAMInfo; import org.rm3l.ddwrt.resources.conn.Router; import org.rm3l.ddwrt.tiles.DDWRTTile; import org.rm3l.ddwrt.utils.DDWRTCompanionConstants; import org.rm3l.ddwrt.utils.NVRAMParser; import org.rm3l.ddwrt.utils.SSHUtils; import org.rm3l.ddwrt.utils.Utils; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import static com.google.common.base.Strings.isNullOrEmpty; /** * */ public class WANMonthlyTrafficTile extends DDWRTTile<NVRAMInfo> { private static final String LOG_TAG = WANMonthlyTrafficTile.class.getSimpleName(); public static final Splitter MONTHLY_TRAFF_DATA_SPLITTER = Splitter.on(" ").omitEmptyStrings(); private static final Splitter DAILY_TRAFF_DATA_SPLITTER = Splitter.on(":").omitEmptyStrings(); private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("MM-yyyy"); private ImmutableTable.Builder<String, Integer, ArrayList<Double>> traffDataTableBuilder; private ImmutableTable<String, Integer, ArrayList<Double>> traffData; public WANMonthlyTrafficTile(@NotNull SherlockFragment parentFragment, @NotNull Bundle arguments, Router router) { super(parentFragment, arguments, router, R.layout.tile_status_wan_monthly_traffic, R.id.tile_status_wan_monthly_traffic_togglebutton); final TextView monthYearTextViewToDisplay = (TextView) this.layout .findViewById(R.id.tile_status_wan_monthly_month_displayed); monthYearTextViewToDisplay.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { final String toDisplay = monthYearTextViewToDisplay.getText().toString(); final boolean isCurrentMonthYear = SIMPLE_DATE_FORMAT.format(new Date()).equals(toDisplay); WANMonthlyTrafficTile.this.layout .findViewById(R.id.tile_status_wan_monthly_traffic_graph_placeholder_current) .setEnabled(!isCurrentMonthYear); WANMonthlyTrafficTile.this.layout .findViewById(R.id.tile_status_wan_monthly_traffic_graph_placeholder_next) .setEnabled(!isCurrentMonthYear); WANMonthlyTrafficTile.this.layout .findViewById(R.id.tile_status_wan_monthly_traffic_graph_placeholder_display_button) .setVisibility(isNullOrEmpty(toDisplay) ? View.GONE : View.VISIBLE); } }); } @Override public int getTileTitleViewId() { return R.id.tile_status_wan_monthly_traffic_title; } @Nullable @Override protected Loader<NVRAMInfo> getLoader(int id, Bundle args) { if (nbRunsLoader <= 0 || mAutoRefreshToggle) { setLoadingViewVisibility(View.VISIBLE); } return new AsyncTaskLoader<NVRAMInfo>(this.mParentFragmentActivity) { @Nullable @Override public NVRAMInfo loadInBackground() { try { Log.d(LOG_TAG, "Init background loader for " + WANMonthlyTrafficTile.class + ": routerInfo=" + mRouter + " / this.mAutoRefreshToggle= " + mAutoRefreshToggle + " / nbRunsLoader=" + nbRunsLoader); if (nbRunsLoader > 0 && !mAutoRefreshToggle) { //Skip run Log.d(LOG_TAG, "Skip loader run"); throw new DDWRTTileAutoRefreshNotAllowedException(); } nbRunsLoader++; @NotNull final NVRAMInfo nvramInfo = new NVRAMInfo(); NVRAMInfo nvramInfoTmp = null; try { //noinspection ConstantConditions nvramInfoTmp = NVRAMParser.parseNVRAMOutput(SSHUtils.getManualProperty(mRouter, mGlobalPreferences, "nvram show 2>/dev/null | grep traff[-_]")); } finally { if (nvramInfoTmp != null) { nvramInfo.putAll(nvramInfoTmp); } } traffDataTableBuilder = ImmutableTable.builder(); if (nvramInfo.isEmpty()) { throw new DDWRTNoDataException("No Data!"); } @SuppressWarnings("ConstantConditions") final Set<Map.Entry<Object, Object>> entries = nvramInfo.getData().entrySet(); for (final Map.Entry<Object, Object> entry : entries) { final Object key; final Object value; if (entry == null || (key = entry.getKey()) == null || (value = entry.getValue()) == null) { continue; } if (!StringUtils.startsWithIgnoreCase(key.toString(), "traff-")) { continue; } final String month = key.toString().replace("traff-", DDWRTCompanionConstants.EMPTY_STRING); final String yearlyTraffData = value.toString(); final List<String> yearlyTraffDataList = MONTHLY_TRAFF_DATA_SPLITTER .splitToList(yearlyTraffData); if (yearlyTraffDataList == null || yearlyTraffDataList.isEmpty()) { continue; } int dayNum = 1; for (final String dailyInOutTraffData : yearlyTraffDataList) { if (StringUtils.contains(dailyInOutTraffData, "[")) { continue; } final List<String> dailyInOutTraffDataList = DAILY_TRAFF_DATA_SPLITTER .splitToList(dailyInOutTraffData); if (dailyInOutTraffDataList == null || dailyInOutTraffDataList.size() < 2) { continue; } final String inTraff = dailyInOutTraffDataList.get(0); final String outTraff = dailyInOutTraffDataList.get(1); traffDataTableBuilder.put(month, dayNum++, Lists.newArrayList(Double.parseDouble(inTraff), Double.parseDouble(outTraff))); } } traffData = traffDataTableBuilder.build(); return nvramInfo; } catch (@NotNull final Exception e) { e.printStackTrace(); return new NVRAMInfo().setException(e); } } }; } @Nullable @Override protected String getLogTag() { return LOG_TAG; } @Nullable @Override protected OnClickIntent getOnclickIntent() { //TODO return null; } private void setLoadingViewVisibility(final int visibility) { this.layout.findViewById(R.id.tile_status_wan_monthly_month_loading).setVisibility(visibility); } @Override public void onLoadFinished(Loader<NVRAMInfo> loader, NVRAMInfo data) { Log.d(LOG_TAG, "onLoadFinished: loader=" + loader + " / data=" + data + " / traffData=" + traffData); setLoadingViewVisibility(View.GONE); Exception preliminaryCheckException = null; if (data == null) { preliminaryCheckException = new DDWRTNoDataException("No Data!"); } else //noinspection ThrowableResultOfMethodCallIgnored if (data.getException() == null) { if (!"1".equals(data.getProperty(NVRAMInfo.TTRAFF_ENABLE))) { preliminaryCheckException = new IllegalStateException("Traffic monitoring disabled!"); } else if (traffData == null || traffData.isEmpty()) { preliminaryCheckException = new DDWRTNoDataException("No Traffic Data!"); } } if (preliminaryCheckException != null) { data = new NVRAMInfo().setException(preliminaryCheckException); } @NotNull final TextView errorPlaceHolderView = (TextView) this.layout .findViewById(R.id.tile_status_wan_monthly_traffic_error); @Nullable final Exception exception = data.getException(); final View displayButton = this.layout .findViewById(R.id.tile_status_wan_monthly_traffic_graph_placeholder_display_button); final View currentButton = this.layout .findViewById(R.id.tile_status_wan_monthly_traffic_graph_placeholder_current); final View previousButton = this.layout .findViewById(R.id.tile_status_wan_monthly_traffic_graph_placeholder_previous); final View nextButton = this.layout .findViewById(R.id.tile_status_wan_monthly_traffic_graph_placeholder_next); final TextView monthYearDisplayed = (TextView) this.layout .findViewById(R.id.tile_status_wan_monthly_month_displayed); final View[] ctrlViews = new View[] { monthYearDisplayed, displayButton, currentButton, previousButton, nextButton }; if (exception == null) { errorPlaceHolderView.setVisibility(View.GONE); final String currentMonthYearAlreadyDisplayed = monthYearDisplayed.getText().toString(); final Date currentDate = new Date(); final String currentMonthYear = (isNullOrEmpty(currentMonthYearAlreadyDisplayed) ? SIMPLE_DATE_FORMAT.format(currentDate) : currentMonthYearAlreadyDisplayed); //TODO Load last value from preferences monthYearDisplayed.setText(currentMonthYear); displayButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final Intent intent = WANMonthlyTrafficTile.this .renderTraffDateForMonth(monthYearDisplayed.getText().toString()); if (intent == null) { Toast.makeText(WANMonthlyTrafficTile.this.mParentFragmentActivity, String.format("No traffic data for '%s'", monthYearDisplayed.getText()), Toast.LENGTH_SHORT).show(); } else { final AlertDialog alertDialog = Utils.buildAlertDialog(mParentFragmentActivity, null, String.format("Loading traffic data for '%s'", monthYearDisplayed.getText()), false, false); alertDialog.show(); ((TextView) alertDialog.findViewById(android.R.id.message)) .setGravity(Gravity.CENTER_HORIZONTAL); new Handler().postDelayed(new Runnable() { @Override public void run() { WANMonthlyTrafficTile.this.mParentFragmentActivity.startActivity(intent); alertDialog.cancel(); } }, 2500); } } }); currentButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { monthYearDisplayed.setText(SIMPLE_DATE_FORMAT.format(currentDate)); } }); previousButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final int[] currentYearMonth = getCurrentYearAndMonth(currentDate, monthYearDisplayed.getText().toString()); if (currentYearMonth.length < 2) { return; } final int currentMonth = currentYearMonth[1]; final int currentYear = currentYearMonth[0]; final int previousMonth = currentMonth - 1; final String previousMonthYear = ((previousMonth <= 0) ? ("12-" + (currentYear - 1)) : (((previousMonth <= 9) ? ("0" + previousMonth) : previousMonth) + "-" + currentYear)); monthYearDisplayed.setText(previousMonthYear); } }); nextButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final int[] currentYearMonth = getCurrentYearAndMonth(currentDate, monthYearDisplayed.getText().toString()); if (currentYearMonth.length < 2) { return; } final int currentMonth = currentYearMonth[1]; final int currentYear = currentYearMonth[0]; final int nextMonth = currentMonth + 1; final String nextMonthYear = ((nextMonth >= 13) ? ("01-" + (currentYear + 1)) : (((nextMonth <= 9) ? ("0" + nextMonth) : nextMonth) + "-" + currentYear)); monthYearDisplayed.setText(nextMonthYear); } }); setVisibility(ctrlViews, View.VISIBLE); } if (exception != null && !(exception instanceof DDWRTTileAutoRefreshNotAllowedException)) { //noinspection ThrowableResultOfMethodCallIgnored final Throwable rootCause = Throwables.getRootCause(exception); errorPlaceHolderView.setText("Error: " + (rootCause != null ? rootCause.getMessage() : "null")); final Context parentContext = this.mParentFragmentActivity; errorPlaceHolderView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { //noinspection ThrowableResultOfMethodCallIgnored if (rootCause != null) { Toast.makeText(parentContext, rootCause.getMessage(), Toast.LENGTH_LONG).show(); } } }); errorPlaceHolderView.setVisibility(View.VISIBLE); setVisibility(ctrlViews, View.GONE); } else { if (traffData == null || traffData.isEmpty()) { errorPlaceHolderView.setText("Error: No Data!"); errorPlaceHolderView.setVisibility(View.VISIBLE); setVisibility(ctrlViews, View.GONE); } } doneWithLoaderInstance(this, loader, R.id.tile_status_wan_monthly_traffic_togglebutton_title, R.id.tile_status_wan_monthly_traffic_togglebutton_separator); Log.d(LOG_TAG, "onLoadFinished(): done loading!"); } private static void setVisibility(@NotNull final View[] views, final int visibility) { for (final View view : views) { view.setVisibility(visibility); } } @NotNull private int[] getCurrentYearAndMonth(final Date currentDate, final String monthYearDisplayed) { final int[] currentYearAndMonth = new int[2]; String monthDisplayed = null; String yearDisplayed = null; final List<String> monthYearTextViewSplit = Splitter.on("-").omitEmptyStrings() .splitToList(monthYearDisplayed); if (monthYearTextViewSplit != null && monthYearTextViewSplit.size() >= 2) { monthDisplayed = monthYearTextViewSplit.get(0); yearDisplayed = monthYearTextViewSplit.get(1); } currentYearAndMonth[0] = Integer.parseInt( isNullOrEmpty(yearDisplayed) ? new SimpleDateFormat("yyyy").format(currentDate) : yearDisplayed); currentYearAndMonth[1] = Integer.parseInt( isNullOrEmpty(monthDisplayed) ? new SimpleDateFormat("MM").format(currentDate) : monthDisplayed); return currentYearAndMonth; } private Intent renderTraffDateForMonth(@NotNull final String monthFormatted) { Log.d(LOG_TAG, "renderTraffDateForMonth: " + monthFormatted); if (traffData == null) { return null; } final ImmutableMap<Integer, ArrayList<Double>> row = traffData.row(monthFormatted); if (row == null) { return null; } final SortedMap<Integer, ArrayList<Double>> dailyTraffMap = new TreeMap<>(row); if (dailyTraffMap.isEmpty()) { return null; } Log.d(LOG_TAG, "renderTraffDateForMonth: " + monthFormatted + " / dailyTraffMap=" + dailyTraffMap); final int size = dailyTraffMap.size(); final int[] days = new int[size]; final double[] inData = new double[size]; final double[] outData = new double[size]; int i = 0; for (final Map.Entry<Integer, ArrayList<Double>> dailyTraffMapEntry : dailyTraffMap.entrySet()) { final ArrayList<Double> dailyTraffMapEntryValue = dailyTraffMapEntry.getValue(); if (dailyTraffMapEntryValue == null || dailyTraffMapEntryValue.size() < 2) { continue; } final Double in = dailyTraffMapEntryValue.get(0); final Double out = dailyTraffMapEntryValue.get(1); if (in == null || out == null) { continue; } days[i] = dailyTraffMapEntry.getKey(); inData[i] = in; outData[i] = out; i++; } // Creating an XYSeries for Inbound final XYSeries inboundSeries = new XYSeries("Inbound"); // Creating an XYSeries for Outbound final XYSeries outboundSeries = new XYSeries("Outbound"); // Adding data to In and Out Series for (int j = 0; j < i; j++) { inboundSeries.add(j, inData[j]); outboundSeries.add(j, outData[j]); } // Creating a dataset to hold each series final XYMultipleSeriesDataset dataset = new XYMultipleSeriesDataset(); // Adding inbound Series to the dataset dataset.addSeries(inboundSeries); // Adding outbound Series to dataset dataset.addSeries(outboundSeries); // Creating XYSeriesRenderer to customize inboundSeries final XYSeriesRenderer inboundRenderer = new XYSeriesRenderer(); inboundRenderer.setColor(Color.rgb(130, 130, 230)); inboundRenderer.setFillPoints(true); inboundRenderer.setLineWidth(2); inboundRenderer.setDisplayChartValues(true); // Creating XYSeriesRenderer to customize outboundSeries final XYSeriesRenderer outboundRenderer = new XYSeriesRenderer(); outboundRenderer.setColor(Color.rgb(220, 80, 80)); outboundRenderer.setFillPoints(true); outboundRenderer.setLineWidth(2); outboundRenderer.setDisplayChartValues(true); // Creating a XYMultipleSeriesRenderer to customize the whole chart final XYMultipleSeriesRenderer multiRenderer = new XYMultipleSeriesRenderer(); multiRenderer.setXLabels(i); multiRenderer.setChartTitle("Traffic Data for '" + monthFormatted + "'"); multiRenderer.setXTitle("Days"); multiRenderer.setYTitle("Traffic (MB)"); multiRenderer.setZoomButtonsVisible(true); for (int k = 0; k < i; k++) { multiRenderer.addXTextLabel(k, String.valueOf(days[k])); } // for(int k = 0; k < days.length; k++) { // multiRenderer.addXTextLabel(k, String.valueOf(k)); // } // Adding inboundRenderer and outboundRenderer to multipleRenderer // Note: The order of adding dataseries to dataset and renderers to multipleRenderer // should be same multiRenderer.addSeriesRenderer(inboundRenderer); multiRenderer.addSeriesRenderer(outboundRenderer); return ChartFactory.getBarChartIntent(this.mParentFragmentActivity, dataset, multiRenderer, BarChart.Type.DEFAULT); } }