org.rm3l.ddwrt.tiles.status.wireless.WirelessIfaceTile.java Source code

Java tutorial

Introduction

Here is the source code for org.rm3l.ddwrt.tiles.status.wireless.WirelessIfaceTile.java

Source

/*
 * 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.wireless;

import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageButton;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;

import com.actionbarsherlock.app.SherlockFragment;
import com.google.common.base.Throwables;
import com.google.common.collect.Maps;

import org.apache.commons.io.FileUtils;
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.mgmt.RouterManagementActivity;
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.SSHUtils;
import org.rm3l.ddwrt.utils.Utils;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.regex.Pattern;

import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.base.Strings.nullToEmpty;
import static org.apache.commons.lang3.StringUtils.startsWith;
import static org.rm3l.ddwrt.tiles.status.wireless.WirelessIfaceTile.TemperatureUnit.CELSIUS;
import static org.rm3l.ddwrt.tiles.status.wireless.WirelessIfaceTile.TemperatureUnit.FAHRENHEIT;
import static org.rm3l.ddwrt.utils.Utils.isThemeLight;

/**
 *
 */
public class WirelessIfaceTile extends DDWRTTile<NVRAMInfo> implements PopupMenu.OnMenuItemClickListener {

    public static final String CAT_SYS_CLASS_NET_S_STATISTICS = "cat /sys/class/net/%s/statistics";
    public static final Pattern HEX_ONLY_QR_CODE_PATTERN = Pattern.compile("/^[0-9a-f]+$/i");
    public static final char DEGREE_SYMBOL = '\u00B0';
    private static final String WIRELESS_IFACE = "wireless_iface";
    private static final String LOG_TAG = WirelessIfaceTile.class.getSimpleName();
    @NotNull
    private final String iface;

    @Nullable
    private final String parentIface;

    @Nullable
    private String wifiSsid;

    @Nullable
    private String wifiPassword;

    @Nullable
    private WirelessEncryptionTypeForQrCode wifiEncryptionType;

    public WirelessIfaceTile(@NotNull String iface, @NotNull SherlockFragment parentFragment,
            @NotNull Bundle arguments, @Nullable Router router) {
        this(iface, null, parentFragment, arguments, router);
    }

    public WirelessIfaceTile(@NotNull String iface, @Nullable String parentIface,
            @NotNull SherlockFragment parentFragment, @NotNull Bundle arguments, @Nullable Router router) {
        super(parentFragment, arguments, router, R.layout.tile_status_wireless_iface,
                R.id.tile_status_wireless_iface_togglebutton);
        this.iface = iface;
        this.parentIface = parentIface;
        ((TextView) this.layout.findViewById(R.id.tile_status_wireless_iface_title)).setText(this.iface);

        //Create Options Menu
        final ImageButton tileMenu = (ImageButton) layout.findViewById(R.id.tile_status_wireless_iface_menu);

        final boolean isThemeLight = isThemeLight(mParentFragmentActivity, mRouter.getUuid());

        if (!isThemeLight) {
            //Set menu background to white
            tileMenu.setImageResource(R.drawable.abs__ic_menu_moreoverflow_normal_holo_dark);
        }

        tileMenu.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                final PopupMenu popup = new PopupMenu(mParentFragmentActivity, v);
                popup.setOnMenuItemClickListener(WirelessIfaceTile.this);
                final MenuInflater inflater = popup.getMenuInflater();

                final Menu menu = popup.getMenu();

                inflater.inflate(R.menu.tile_wireless_iface_options, menu);

                if (wifiEncryptionType == null || (isNullOrEmpty(wifiSsid) && isNullOrEmpty(wifiPassword))) {
                    menu.findItem(R.id.tile_status_wireless_iface_qrcode).setEnabled(false);
                }

                popup.show();
            }
        });

    }

    @Override
    public int getTileTitleViewId() {
        return R.id.tile_status_wireless_iface_title;
    }

    @Nullable
    @Override
    protected Loader<NVRAMInfo> getLoader(int id, Bundle args) {
        return new AsyncTaskLoader<NVRAMInfo>(this.mParentFragmentActivity) {

            @Nullable
            @Override
            public NVRAMInfo loadInBackground() {

                try {
                    Log.d(LOG_TAG,
                            "Init background loader for " + WirelessIfaceTile.class + ": routerInfo=" + mRouter
                                    + " / this.mAutoRefreshToggle= " + mAutoRefreshToggle + " / nbRunsLoader="
                                    + nbRunsLoader);

                    if (nbRunsLoader > 0 && !mAutoRefreshToggle) {
                        //Skip run
                        Log.d(LOG_TAG, "Skip loader run");
                        return new NVRAMInfo().setException(new DDWRTTileAutoRefreshNotAllowedException());
                    }
                    nbRunsLoader++;

                    final NVRAMInfo nvramInfo = getIfaceNvramInfo(iface);
                    if (parentIface != null && !parentIface.isEmpty()) {
                        nvramInfo.putAll(getIfaceNvramInfo(parentIface));
                    }

                    if (nvramInfo.isEmpty()) {
                        throw new DDWRTNoDataException("No Data!");
                    }

                    return nvramInfo;

                } catch (@NotNull final Exception e) {
                    e.printStackTrace();
                    return new NVRAMInfo().setException(e);
                }
            }

            @NotNull
            private NVRAMInfo getIfaceNvramInfo(@NotNull String wlIface) throws Exception {

                if (DDWRTCompanionConstants.TEST_MODE) {
                    return new NVRAMInfo()
                            .setProperty(WIRELESS_IFACE + (wlIface.equals(parentIface) ? "_parent" : ""), wlIface)
                            .setProperty(wlIface + "_radio", String.valueOf(new Random().nextInt()))
                            .setProperty(wlIface + "_mode", "Mode")
                            .setProperty(wlIface + "_hwaddr", "hw:ad:dr:rd:da:wh")
                            .setProperty(wlIface + "_ifname", wlIface + " ifname")
                            .setProperty(wlIface + "_net_mode", "ng-only").setProperty(wlIface + "_ssid", "SSID")
                            .setProperty(wlIface + "_channel", String.valueOf(new Random().nextInt(100)))
                            .setProperty(wlIface + "_txpwr", String.valueOf(new Random().nextInt(100)))
                            .setProperty(wlIface + "_rate", String.valueOf(new Random().nextInt(100)))
                            .setProperty(wlIface + "_akm", "psk psk2");
                }

                final NVRAMInfo nvramInfo = new NVRAMInfo();

                NVRAMInfo nvramInfoTmp = null;
                try {
                    nvramInfoTmp = SSHUtils.getNVRamInfoFromRouter(mRouter, mGlobalPreferences, wlIface + "_radio",
                            wlIface + "_mode", wlIface + "_hwaddr", wlIface + "_ifname", wlIface + "_net_mode",
                            wlIface + "_ssid", wlIface + "_channel", wlIface + "_txpwr", wlIface + "_rate",
                            wlIface + "_akm", wlIface + "_wpa_psk");
                } finally {
                    if (nvramInfoTmp != null) {
                        nvramInfo.putAll(nvramInfoTmp);
                    }
                    nvramInfo.setProperty(WIRELESS_IFACE + (wlIface.equals(parentIface) ? "_parent" : ""), wlIface);

                    //Set Temp, RX and TX Network Bandwidths on Physical Interface
                    final String phyIface = nvramInfo.getProperty(wlIface + "_ifname");
                    if (!isNullOrEmpty(phyIface)) {

                        //Set temperature
                        //noinspection ConstantConditions
                        final Map<TemperatureUnit, String> ifaceTemperatures = getIfaceTemperature(phyIface);
                        final String celsius = ifaceTemperatures.get(CELSIUS);
                        final String fahrenheit = ifaceTemperatures.get(FAHRENHEIT);
                        if (!(isNullOrEmpty(celsius) || isNullOrEmpty(fahrenheit))) {
                            nvramInfo.setProperty(wlIface + "_temperature", celsius + DEGREE_SYMBOL + CELSIUS + " ("
                                    + fahrenheit + DEGREE_SYMBOL + FAHRENHEIT + ")");
                        }

                        //RX and TX Bytes
                        //noinspection ConstantConditions
                        final Map<IfaceStatsType, Long> ifaceRxAndTxRates = getIfaceRxAndTxRates(phyIface);
                        final Long rxBps = ifaceRxAndTxRates.get(IfaceStatsType.RX_BYTES);
                        final Long txBps = ifaceRxAndTxRates.get(IfaceStatsType.TX_BYTES);
                        if (rxBps != null) {
                            nvramInfo.setProperty(wlIface + "_rx_rate_human_readable",
                                    rxBps + " B/s (" + FileUtils.byteCountToDisplaySize(rxBps) + "/s)");
                        }
                        if (txBps != null) {
                            nvramInfo.setProperty(wlIface + "_tx_rate_human_readable",
                                    txBps + " B/s (" + FileUtils.byteCountToDisplaySize(txBps) + "/s)");
                        }

                        //Packet Info
                        final String sysClassNetStatsFolder = String.format(CAT_SYS_CLASS_NET_S_STATISTICS,
                                phyIface);
                        try {
                            final String[] packetsInfo = SSHUtils.getManualProperty(mRouter, mGlobalPreferences,
                                    String.format("%s/rx_packets", sysClassNetStatsFolder),
                                    String.format("%s/rx_errors", sysClassNetStatsFolder),
                                    String.format("%s/tx_packets", sysClassNetStatsFolder),
                                    String.format("%s/tx_errors", sysClassNetStatsFolder));

                            if (packetsInfo != null) {
                                final long rxErrors = Long.parseLong(packetsInfo[1]);
                                nvramInfo.setProperty(wlIface + "_rx_packets",
                                        String.format("%s (%s)", packetsInfo[0], rxErrors <= 0 ? "no error"
                                                : (rxErrors + String.format("error%s", rxErrors > 1 ? "s" : ""))));
                                final long txErrors = Long.parseLong(packetsInfo[3]);
                                nvramInfo.setProperty(wlIface + "_tx_packets",
                                        String.format("%s (%s)", packetsInfo[2], txErrors <= 0 ? "no error"
                                                : (txErrors + String.format(" error%s", txErrors > 1 ? "s" : ""))));
                            }
                        } catch (final Exception e) {
                            e.printStackTrace();
                            //No worries
                        }
                    }
                }

                return nvramInfo;
            }

            @NotNull
            private Map<TemperatureUnit, String> getIfaceTemperature(@NotNull final String phyIface) {
                final Map<TemperatureUnit, String> result = Maps.newHashMapWithExpectedSize(2);
                final String phyIfaceVarNameInRouter = WirelessIfaceTile.class.getSimpleName()
                        + "TemperatureCelsius";
                try {
                    final String[] temperatures = SSHUtils.getManualProperty(mRouter, mGlobalPreferences,
                            String.format(
                                    "%s=$(echo $((`wl -i %s phy_tempsense | awk {' print $1 '}`/2+20))); echo \"C:$%s\"; echo \"F:$(($%s*9/5+32))\"",
                                    phyIfaceVarNameInRouter, phyIface, phyIfaceVarNameInRouter,
                                    phyIfaceVarNameInRouter));
                    if (temperatures != null && temperatures.length >= 2) {
                        for (final String temperature : temperatures) {
                            if (temperature == null) {
                                continue;
                            }
                            if (temperature.startsWith("C:")) {
                                result.put(CELSIUS, temperature.replaceAll("C:", "").trim());
                            } else if (temperature.startsWith("F:")) {
                                result.put(FAHRENHEIT, temperature.replaceAll("F:", "").trim());
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return result;
            }

            @NotNull
            private Map<IfaceStatsType, Long> getIfaceRxAndTxRates(@NotNull final String phyIface) {
                final Map<IfaceStatsType, Long> result = Maps.newHashMapWithExpectedSize(2);
                final String sysClassNetStatsFolder = String.format(CAT_SYS_CLASS_NET_S_STATISTICS, phyIface);
                final String rxBytesCmd = String.format("%s/rx_bytes", sysClassNetStatsFolder);
                final String txBytesCmd = String.format("%s/tx_bytes", sysClassNetStatsFolder);

                try {
                    final long[] bytesBeforeAndAfter = parseFloatDataFromOutput(SSHUtils.getManualProperty(mRouter,
                            mGlobalPreferences, rxBytesCmd, txBytesCmd, "sleep 1", rxBytesCmd, txBytesCmd));
                    if (bytesBeforeAndAfter.length >= 4) {
                        result.put(IfaceStatsType.RX_BYTES,
                                Math.abs(bytesBeforeAndAfter[1] - bytesBeforeAndAfter[0]));
                        result.put(IfaceStatsType.TX_BYTES,
                                Math.abs(bytesBeforeAndAfter[3] - bytesBeforeAndAfter[2]));
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
                return result;
            }

            @NotNull
            private long[] parseFloatDataFromOutput(@Nullable final String[] output) {
                if (output == null || output.length == 0) {
                    throw new IllegalArgumentException("Output null or empty");
                }
                final long[] result = new long[output.length];
                for (int i = 0; i < output.length; i++) {
                    result[i] = Long.parseLong(output[i]);

                }
                return result;
            }

        };
    }

    @Nullable
    @Override
    protected String getLogTag() {
        return LOG_TAG;
    }

    /**
     * Called when a previously created loader has finished its load.  Note
     * that normally an application is <em>not</em> allowed to commit fragment
     * transactions while in this call, since it can happen after an
     * activity's state is saved.  See {@link android.support.v4.app.FragmentManager#beginTransaction()
     * FragmentManager.openTransaction()} for further discussion on this.
     * <p/>
     * <p>This function is guaranteed to be called prior to the release of
     * the last data that was supplied for this Loader.  At this point
     * you should remove all use of the old data (since it will be released
     * soon), but should not do your own release of the data since its Loader
     * owns it and will take care of that.  The Loader will take care of
     * management of its data so you don't have to.  In particular:
     * <p/>
     * <ul>
     * <li> <p>The Loader will monitor for changes to the data, and report
     * them to you through new calls here.  You should not monitor the
     * data yourself.  For example, if the data is a {@link android.database.Cursor}
     * and you place it in a {@link android.widget.CursorAdapter}, use
     * the {@link android.widget.CursorAdapter#CursorAdapter(android.content.Context,
     * android.database.Cursor, int)} constructor <em>without</em> passing
     * in either {@link android.widget.CursorAdapter#FLAG_AUTO_REQUERY}
     * or {@link android.widget.CursorAdapter#FLAG_REGISTER_CONTENT_OBSERVER}
     * (that is, use 0 for the flags argument).  This prevents the CursorAdapter
     * from doing its own observing of the Cursor, which is not needed since
     * when a change happens you will get a new Cursor throw another call
     * here.
     * <li> The Loader will release the data once it knows the application
     * is no longer using it.  For example, if the data is
     * a {@link android.database.Cursor} from a {@link android.content.CursorLoader},
     * you should not call close() on it yourself.  If the Cursor is being placed in a
     * {@link android.widget.CursorAdapter}, you should use the
     * {@link android.widget.CursorAdapter#swapCursor(android.database.Cursor)}
     * method so that the old Cursor is not closed.
     * </ul>
     *
     * @param loader The Loader that has finished.
     * @param data   The data generated by the Loader.
     */
    @Override
    public void onLoadFinished(@NotNull Loader<NVRAMInfo> loader, @Nullable NVRAMInfo data) {
        //Set tiles
        Log.d(LOG_TAG, "onLoadFinished: loader=" + loader);

        layout.findViewById(R.id.tile_status_wireless_iface_loading_view).setVisibility(View.GONE);
        layout.findViewById(R.id.tile_status_wireless_iface_gridlayout).setVisibility(View.VISIBLE);

        if (data == null) {
            data = new NVRAMInfo().setException(new DDWRTNoDataException("No Data!"));
        }

        @NotNull
        final TextView errorPlaceHolderView = (TextView) this.layout
                .findViewById(R.id.tile_status_wireless_iface_error);

        @Nullable
        final Exception exception = data.getException();

        if (!(exception instanceof DDWRTTileAutoRefreshNotAllowedException)) {

            if (exception == null) {
                errorPlaceHolderView.setVisibility(View.GONE);
            }

            //SSID
            @NotNull
            final TextView ssidView = (TextView) this.layout.findViewById(R.id.tile_status_wireless_iface_ssid);
            this.wifiSsid = data.getProperty(this.iface + "_ssid", DDWRTCompanionConstants.EMPTY_STRING);
            ssidView.setText(this.wifiSsid);

            this.wifiPassword = data.getProperty(this.iface + "_wpa_psk", DDWRTCompanionConstants.EMPTY_STRING);

            //Ifname
            @NotNull
            final TextView ifnameView = (TextView) this.layout.findViewById(R.id.tile_status_wireless_iface_ifname);
            ifnameView.setText(data.getProperty(this.iface + "_ifname", "-"));

            //MAC
            @NotNull
            final TextView hwAddrView = (TextView) this.layout
                    .findViewById(R.id.tile_status_wireless_iface_mac_address);
            hwAddrView.setText(data.getProperty(this.iface + "_hwaddr", "-"));

            //Radio
            @NotNull
            final CheckBox radioView = (CheckBox) this.layout.findViewById(R.id.tile_status_wireless_iface_radio);
            radioView.setEnabled(false);
            radioView.setChecked("1".equals(
                    data.getProperty(this.iface + "_radio", data.getProperty(this.parentIface + "_radio"))));

            //Mode
            @NotNull
            final TextView modeView = (TextView) this.layout.findViewById(R.id.tile_status_wireless_iface_mode);
            String property = data.getProperty(this.iface + "_mode", "-");
            modeView.setText(property != null ? property.toUpperCase() : "-");

            //Net Mode
            @NotNull
            final TextView netModeView = (TextView) this.layout
                    .findViewById(R.id.tile_status_wireless_iface_network);
            final String netmode = data.getProperty(this.iface + "_net_mode",
                    data.getProperty(this.parentIface + "_net_mode"));
            String mode = "-";
            if ("disabled".equalsIgnoreCase(netmode)) {
                mode = "Disabled";
            } else if ("mixed".equalsIgnoreCase(netmode)) {
                mode = "Mixed";
            } else if ("g-only".equalsIgnoreCase(netmode)) {
                mode = "G-Only";
            } else if ("b-only".equalsIgnoreCase(netmode)) {
                mode = "B-Only";
            } else if ("a-only".equalsIgnoreCase(netmode)) {
                mode = "A-Only";
            } else if ("n-only".equalsIgnoreCase(netmode)) {
                mode = "N-Only";
            } else if ("ng-only".equalsIgnoreCase(netmode)) {
                mode = "NG-Only";
            } else if ("n5-only".equalsIgnoreCase(netmode)) {
                mode = "N-Only (5GHz)";
            } else if (netmode != null) {
                mode = netmode;
            }
            netModeView.setText(mode);

            //Temperature
            @NotNull
            final TextView temperatureView = (TextView) this.layout
                    .findViewById(R.id.tile_status_wireless_iface_temperature);
            final String temperatureProperty = data.getProperty(this.iface + "_temperature",
                    data.getProperty(this.parentIface + "_temperature", "-"));
            temperatureView.setText(temperatureProperty);

            //Channel
            @NotNull
            final TextView channelView = (TextView) this.layout
                    .findViewById(R.id.tile_status_wireless_iface_channel);
            final String channelProperty = data.getProperty(this.iface + "_channel",
                    data.getProperty(this.parentIface + "_channel", "-"));
            channelView.setText("0".equals(channelProperty) ? "Auto" : channelProperty);

            //            //Rate
            //            @NotNull final TextView rateView = (TextView) this.layout.findViewById(R.id.tile_status_wireless_iface_rate);
            //            final String rateProperty = data.getProperty(this.iface + "_rate_human_readable", "-");
            //            rateView.setText(rateProperty);
            //Rate
            @NotNull
            final TextView rxRateView = (TextView) this.layout
                    .findViewById(R.id.tile_status_wireless_iface_rx_rate);
            final String rxRateProperty = data.getProperty(this.iface + "_rx_rate_human_readable", "-");
            rxRateView.setText(rxRateProperty);

            @NotNull
            final TextView txRateView = (TextView) this.layout
                    .findViewById(R.id.tile_status_wireless_iface_tx_rate);
            final String txRateProperty = data.getProperty(this.iface + "_tx_rate_human_readable", "-");
            txRateView.setText(txRateProperty);

            //Packet Info
            @NotNull
            final TextView rxPacketsView = (TextView) this.layout
                    .findViewById(R.id.tile_status_wireless_iface_rx_packets);
            rxPacketsView.setText(data.getProperty(this.iface + "_rx_packets", "-"));

            @NotNull
            final TextView txPacketsView = (TextView) this.layout
                    .findViewById(R.id.tile_status_wireless_iface_tx_packets);
            txPacketsView.setText(data.getProperty(this.iface + "_tx_packets", "-"));

            //TX Power
            @NotNull
            final TextView xmitView = (TextView) this.layout.findViewById(R.id.tile_status_wireless_iface_tx_power);
            xmitView.setText(
                    data.getProperty(this.iface + "_txpwr", data.getProperty(this.parentIface + "_txpwr", "-")));

            //Encryption
            @NotNull
            final TextView encryptionView = (TextView) this.layout
                    .findViewById(R.id.tile_status_wireless_iface_encryption);
            final String akm = data.getProperty(this.iface + "_akm");
            @NotNull
            String encryption = "-";
            if ("psk".equalsIgnoreCase(akm)) {
                encryption = "WPA Pre-shared Key";
            } else if ("wpa".equalsIgnoreCase(akm)) {
                encryption = "WPA RADIUS";
            } else if ("psk2".equalsIgnoreCase(akm)) {
                encryption = "WPA2 Pre-shared Key";
            } else if ("wpa2".equalsIgnoreCase(akm)) {
                encryption = "WPA2 RADIUS";
            } else if ("psk psk2".equalsIgnoreCase(akm)) {
                encryption = "WPA2 Pre-shared Key Mixed";
            } else if ("wpa wpa2".equalsIgnoreCase(akm)) {
                encryption = "WPA RADIUS Mixed";
            } else if ("radius".equalsIgnoreCase(akm)) {
                encryption = "RADIUS";
            } else if ("wep".equalsIgnoreCase(akm)) {
                encryption = "WEP";
                this.wifiEncryptionType = WirelessEncryptionTypeForQrCode.WEP;
            }

            if (startsWith(encryption, "WPA")) {
                this.wifiEncryptionType = WirelessEncryptionTypeForQrCode.WPA;
            } else if (startsWith(encryption, "WEP")) {
                this.wifiEncryptionType = WirelessEncryptionTypeForQrCode.WEP;
            } else if (!"radius".equalsIgnoreCase(encryption)) {
                this.wifiEncryptionType = WirelessEncryptionTypeForQrCode.NONE;
            }

            encryptionView.setText(encryption);

        }

        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);
        }

        doneWithLoaderInstance(this, loader, R.id.tile_status_wireless_iface_togglebutton_title,
                R.id.tile_status_wireless_iface_togglebutton_separator);

        Log.d(LOG_TAG, "onLoadFinished(): done loading!");
    }

    @Nullable
    @Override
    protected OnClickIntent getOnclickIntent() {
        //TODO
        return null;
    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.tile_status_wireless_iface_qrcode:
            if (wifiEncryptionType == null || (isNullOrEmpty(wifiSsid) && isNullOrEmpty(wifiPassword))) {
                //menu item should have been disabled, but anyways, you never know :)
                Toast.makeText(mParentFragmentActivity, "Missing parameters to generate QR-Code - try again later",
                        Toast.LENGTH_SHORT).show();
                return true;
            }
            //https://github.com/zxing/zxing/wiki/Barcode-Contents
            //noinspection ConstantConditions
            final String wifiSsidNullToEmpty = nullToEmpty(wifiSsid);
            final String wifiQrCodeString = String.format("WIFI:S:%s;T:%s;P:%s;%s;",
                    escapeString(wifiSsidNullToEmpty), wifiEncryptionType.toString(),
                    escapeString(nullToEmpty(wifiPassword)), wifiSsidNullToEmpty.isEmpty() ? "H:true" : "");

            final String routerUuid = mRouter.getUuid();
            final Class<?> activityClass = Utils.isThemeLight(mParentFragmentActivity, routerUuid)
                    ? WirelessIfaceQrCodeActivityLight.class
                    : WirelessIfaceQrCodeActivity.class;

            final Intent intent = new Intent(mParentFragmentActivity, activityClass);
            intent.putExtra(RouterManagementActivity.ROUTER_SELECTED, routerUuid);
            intent.putExtra(WirelessIfaceQrCodeActivity.SSID, wifiSsidNullToEmpty);
            intent.putExtra(WirelessIfaceQrCodeActivity.WIFI_QR_CODE, wifiQrCodeString);

            final AlertDialog alertDialog = Utils.buildAlertDialog(mParentFragmentActivity, null,
                    String.format("Generating QR Code for '%s'", wifiSsidNullToEmpty), false, false);
            alertDialog.show();
            ((TextView) alertDialog.findViewById(android.R.id.message)).setGravity(Gravity.CENTER_HORIZONTAL);
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    mParentFragmentActivity.startActivity(intent);
                    alertDialog.cancel();
                }
            }, 2500);

            return true;
        default:
            break;
        }
        return false;
    }

    @NotNull
    private String escapeString(@NotNull final String string) {
        final List<Character> toEscape = Arrays.asList('\\', ';', ',', ':', '"');
        String output = "";
        for (int i = 0; i < string.length(); i++) {
            final char charAt = string.charAt(i);
            if (toEscape.contains(charAt)) {
                output += ('\\' + charAt);
            } else {
                output += charAt;
            }
        }

        if (HEX_ONLY_QR_CODE_PATTERN.matcher(output).matches()) {
            output = ("\"" + output + "\"");
        }
        return output;
    }

    public enum IfaceStatsType {
        RX_BYTES("rx_bytes"), TX_BYTES("tx_bytes");

        final String metric;

        IfaceStatsType(String metric) {
            this.metric = metric;
        }

        public String getMetric() {
            return metric;
        }
    }

    public enum WirelessEncryptionTypeForQrCode {
        WPA("WPA"), WEP("WEP"), NONE("nopass");

        private final String encType;

        WirelessEncryptionTypeForQrCode(String encType) {
            this.encType = encType;
        }

        @Override
        public String toString() {
            return encType;
        }
    }

    public enum TemperatureUnit {
        CELSIUS("C"), FAHRENHEIT("F");

        private final String unitDisplay;

        TemperatureUnit(String unitDisplay) {
            this.unitDisplay = unitDisplay;
        }

        @Override
        public String toString() {
            return unitDisplay;
        }
    }
}