org.rm3l.ddwrt.tiles.admin.nvram.AdminNVRAMTile.java Source code

Java tutorial

Introduction

Here is the source code for org.rm3l.ddwrt.tiles.admin.nvram.AdminNVRAMTile.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.admin.nvram;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.FileProvider;
import android.support.v4.content.Loader;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.PopupMenu;
import android.widget.ShareActionProvider;
import android.widget.TextView;
import android.widget.Toast;

import com.actionbarsherlock.app.SherlockFragment;
import com.google.common.base.Joiner;
import com.google.common.base.Throwables;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;

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.None;
import org.rm3l.ddwrt.resources.conn.NVRAMInfo;
import org.rm3l.ddwrt.resources.conn.Router;
import org.rm3l.ddwrt.tiles.DDWRTTile;
import org.rm3l.ddwrt.tiles.status.router.StatusRouterSpaceUsageTile;
import org.rm3l.ddwrt.utils.SSHUtils;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;

import de.keyboardsurfer.android.widget.crouton.Crouton;
import de.keyboardsurfer.android.widget.crouton.Style;

import static com.google.common.base.Strings.isNullOrEmpty;
import static org.apache.commons.lang3.StringUtils.containsIgnoreCase;
import static org.rm3l.ddwrt.utils.DDWRTCompanionConstants.EMPTY_STRING;
import static org.rm3l.ddwrt.utils.Utils.isThemeLight;

public class AdminNVRAMTile extends DDWRTTile<None> implements PopupMenu.OnMenuItemClickListener {

    private static final String LOG_TAG = AdminNVRAMTile.class.getSimpleName();
    public static final String NVRAM_SIZE = AdminNVRAMTile.class.getSimpleName() + "::nvram_size";
    private static final String LAST_SEARCH = "lastSearch";
    public static final Comparator<Object> COMPARATOR_STRING_CASE_INSENSITIVE = new Comparator<Object>() {
        @Override
        public int compare(Object o1, Object o2) {
            if (o1 == o2) {
                return 0;
            }
            if (o1 == null) {
                return -1;
            }
            if (o2 == null) {
                return 1;
            }
            return o1.toString().compareToIgnoreCase(o2.toString());
        }
    };

    public static final Comparator<Object> COMPARATOR_REVERSE_STRING_CASE_INSENSITIVE = new Comparator<Object>() {
        @Override
        public int compare(Object o1, Object o2) {
            if (o1 == o2) {
                return 0;
            }
            if (o1 == null) {
                return 1;
            }
            if (o2 == null) {
                return -1;
            }
            return o2.toString().compareToIgnoreCase(o1.toString());
        }
    };
    public static final String SORT = "sort";
    public static final Joiner.MapJoiner PROPERTIES_JOINER_TO_FILE = Joiner.on('\n').withKeyValueSeparator("=");

    private final RecyclerView mRecyclerView;
    private final RecyclerView.Adapter mAdapter;
    private final RecyclerView.LayoutManager mLayoutManager;

    private final NVRAMInfo mNvramInfoDefaultSorting;
    private Map<Object, Object> mNvramInfoToDisplay = new HashMap<>();

    private ShareActionProvider mShareActionProvider;

    private final BiMap<Integer, Integer> sortIds = HashBiMap.create();

    public AdminNVRAMTile(@NotNull SherlockFragment parentFragment, @NotNull Bundle arguments,
            @Nullable Router router) {
        super(parentFragment, arguments, router, R.layout.tile_admin_nvram, R.id.tile_admin_nvram_togglebutton);

        sortIds.put(R.id.tile_admin_nvram_sort_default, 11);
        sortIds.put(R.id.tile_admin_nvram_sort_asc, 12);
        sortIds.put(R.id.tile_admin_nvram_sort_desc, 13);

        this.mNvramInfoDefaultSorting = new NVRAMInfo();
        mRecyclerView = (RecyclerView) layout.findViewById(R.id.tile_admin_nvram_ListView);

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        // allows for optimizations if all items are of the same size:
        mRecyclerView.setHasFixedSize(true);

        // use a linear layout manager
        mLayoutManager = new LinearLayoutManager(mParentFragmentActivity);
        mLayoutManager.scrollToPosition(0);
        mRecyclerView.setLayoutManager(mLayoutManager);

        // specify an adapter (see also next example)
        mAdapter = new NVRAMDataRecyclerViewAdapter(mParentFragmentActivity, router, mNvramInfoDefaultSorting);
        mRecyclerView.setAdapter(mAdapter);

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

        if (!isThemeLight(mParentFragmentActivity, mRouter.getUuid())) {
            //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(AdminNVRAMTile.this);
                final MenuInflater inflater = popup.getMenuInflater();

                final Menu menu = popup.getMenu();

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

                //Disable menu item from preference
                Integer currentSort = null;
                if (mParentFragmentPreferences != null) {
                    currentSort = sortIds.inverse().get(mParentFragmentPreferences.getInt(getFormattedPrefKey(SORT),
                            sortIds.get(R.id.tile_admin_nvram_sort_default)));
                }

                if (currentSort == null) {
                    currentSort = R.id.tile_admin_nvram_sort_default;
                }

                final MenuItem currentSortMenuItem = menu.findItem(currentSort);
                if (currentSortMenuItem != null) {
                    currentSortMenuItem.setEnabled(false);
                }

                // Locate MenuItem with ShareActionProvider
                final MenuItem shareMenuItem = menu.findItem(R.id.tile_admin_nvram_share);

                // Fetch and store ShareActionProvider
                mShareActionProvider = (ShareActionProvider) shareMenuItem.getActionProvider();

                popup.show();
            }

        });

        //Handle for Search EditText
        final EditText filterEditText = (EditText) this.layout.findViewById(R.id.tile_admin_nvram_filter);
        //Initialize with existing search data
        filterEditText.setText(mParentFragmentPreferences != null
                ? mParentFragmentPreferences.getString(getFormattedPrefKey(LAST_SEARCH), EMPTY_STRING)
                : EMPTY_STRING);

        filterEditText.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                final int DRAWABLE_LEFT = 0;
                final int DRAWABLE_TOP = 1;
                final int DRAWABLE_RIGHT = 2;
                final int DRAWABLE_BOTTOM = 3;

                if (event.getAction() == MotionEvent.ACTION_UP) {
                    if (event.getRawX() >= (filterEditText.getRight()
                            - filterEditText.getCompoundDrawables()[DRAWABLE_RIGHT].getBounds().width())) {
                        //'Clear' button - clear data, and reset everything out
                        //Reset everything
                        filterEditText.setText(EMPTY_STRING);

                        final Properties mNvramInfoDefaultSortingData = mNvramInfoDefaultSorting.getData();
                        //Update adapter in the preferences
                        if (mParentFragmentPreferences != null) {
                            final Integer currentSort = sortIds.inverse()
                                    .get(mParentFragmentPreferences.getInt(getFormattedPrefKey(SORT), -1));
                            if (currentSort == null || currentSort <= 0) {
                                mNvramInfoToDisplay = new HashMap<>(mNvramInfoDefaultSortingData);
                            } else {
                                switch (currentSort) {
                                case R.id.tile_admin_nvram_sort_asc:
                                    //asc
                                    mNvramInfoToDisplay = new TreeMap<>(COMPARATOR_STRING_CASE_INSENSITIVE);
                                    break;
                                case R.id.tile_admin_nvram_sort_desc:
                                    //desc
                                    mNvramInfoToDisplay = new TreeMap<>(COMPARATOR_REVERSE_STRING_CASE_INSENSITIVE);
                                    break;
                                case R.id.tile_admin_nvram_sort_default:
                                default:
                                    mNvramInfoToDisplay = new HashMap<>();
                                    break;
                                }
                                mNvramInfoToDisplay.putAll(mNvramInfoDefaultSortingData);
                            }
                        } else {
                            mNvramInfoToDisplay = new HashMap<>(mNvramInfoDefaultSortingData);
                        }

                        ((NVRAMDataRecyclerViewAdapter) mAdapter).setEntryList(mNvramInfoToDisplay);
                        mAdapter.notifyDataSetChanged();

                        if (mParentFragmentPreferences != null) {
                            final SharedPreferences.Editor editor = mParentFragmentPreferences.edit();
                            editor.putString(getFormattedPrefKey(LAST_SEARCH), EMPTY_STRING);
                            editor.apply();
                        }
                        return true;
                    }
                }
                return false;
            }
        });

        filterEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {

                if (actionId == EditorInfo.IME_ACTION_SEARCH) {
                    final String textToFind = filterEditText.getText().toString();
                    if (isNullOrEmpty(textToFind)) {
                        //extra-check, even though we can be pretty sure the button is enabled only if textToFind is present
                        return true;
                    }
                    final String existingSearch = mParentFragmentPreferences != null
                            ? mParentFragmentPreferences.getString(getFormattedPrefKey(LAST_SEARCH), null)
                            : null;

                    if (mParentFragmentPreferences != null) {
                        if (textToFind.equalsIgnoreCase(existingSearch)) {
                            //No need to go further as this is already the string we are looking for
                            return true;
                        }
                        final SharedPreferences.Editor editor = mParentFragmentPreferences.edit();
                        editor.putString(getFormattedPrefKey(LAST_SEARCH), textToFind);
                        editor.apply();
                    }

                    //Filter out (and sort by user-preference)
                    final Properties mNvramInfoDefaultSortingData = mNvramInfoDefaultSorting.getData();
                    //Update adapter in the preferences
                    final Map<Object, Object> mNvramInfoToDisplayCopy;
                    if (mParentFragmentPreferences != null) {
                        final Integer currentSort = sortIds.inverse()
                                .get(mParentFragmentPreferences.getInt(getFormattedPrefKey(SORT), -1));
                        if (currentSort == null || currentSort <= 0) {
                            mNvramInfoToDisplayCopy = new HashMap<>(mNvramInfoDefaultSortingData);
                        } else {
                            switch (currentSort) {
                            case R.id.tile_admin_nvram_sort_asc:
                                //asc
                                mNvramInfoToDisplayCopy = new TreeMap<>(COMPARATOR_STRING_CASE_INSENSITIVE);
                                break;
                            case R.id.tile_admin_nvram_sort_desc:
                                //desc
                                mNvramInfoToDisplayCopy = new TreeMap<>(COMPARATOR_REVERSE_STRING_CASE_INSENSITIVE);
                                break;
                            case R.id.tile_admin_nvram_sort_default:
                            default:
                                mNvramInfoToDisplayCopy = new HashMap<>();
                                break;
                            }
                            //noinspection ConstantConditions
                            for (Map.Entry<Object, Object> entry : mNvramInfoDefaultSortingData.entrySet()) {
                                final Object key = entry.getKey();
                                final Object value = entry.getValue();
                                if (key == null) {
                                    continue;
                                }
                                if (containsIgnoreCase(key.toString(), textToFind)
                                        || containsIgnoreCase(value.toString(), textToFind)) {
                                    mNvramInfoToDisplayCopy.put(key, value);
                                }
                            }
                        }
                    } else {
                        mNvramInfoToDisplayCopy = new HashMap<>();
                        //noinspection ConstantConditions
                        for (Map.Entry<Object, Object> entry : mNvramInfoDefaultSortingData.entrySet()) {
                            final Object key = entry.getKey();
                            final Object value = entry.getValue();
                            if (key == null) {
                                continue;
                            }
                            if (containsIgnoreCase(key.toString(), textToFind)
                                    || containsIgnoreCase(value.toString(), textToFind)) {
                                mNvramInfoToDisplayCopy.put(key, value);
                            }
                        }
                    }

                    ((NVRAMDataRecyclerViewAdapter) mAdapter).setEntryList(mNvramInfoToDisplayCopy);
                    mAdapter.notifyDataSetChanged();

                    return true;
                }
                return false;
            }
        });

    }

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

    // Call to update the share intent
    private void setShareIntent(Intent shareIntent) {
        if (mShareActionProvider != null) {
            mShareActionProvider.setShareIntent(shareIntent);
        }
    }

    private void setShareFile(File file) {
        if (mShareActionProvider == null) {
            return;
        }

        final Uri uriForFile = FileProvider.getUriForFile(mParentFragmentActivity, "org.rm3l.fileprovider", file);

        mShareActionProvider
                .setOnShareTargetSelectedListener(new ShareActionProvider.OnShareTargetSelectedListener() {
                    @Override
                    public boolean onShareTargetSelected(ShareActionProvider source, Intent intent) {
                        mParentFragmentActivity.grantUriPermission(intent.getComponent().getPackageName(),
                                uriForFile, Intent.FLAG_GRANT_READ_URI_PERMISSION);
                        return true;
                    }
                });

        final Intent sendIntent = new Intent();
        sendIntent.setAction(Intent.ACTION_SEND);
        sendIntent.putExtra(Intent.EXTRA_STREAM, uriForFile);
        if (mRouter != null) {
            sendIntent.putExtra(Intent.EXTRA_SUBJECT, String.format("NVRAM Variables dump from router '%s' (%s)",
                    mRouter.getName(), mRouter.getRemoteIpAddress()));
        }
        sendIntent.setData(uriForFile);
        sendIntent.setType("text/plain");
        sendIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        setShareIntent(sendIntent);

    }

    @Override
    public boolean onMenuItemClick(MenuItem item) {
        final int itemId = item.getItemId();
        //Store current value in preferences
        switch (itemId) {
        case R.id.tile_admin_nvram_sort_default:
        case R.id.tile_admin_nvram_sort_asc:
        case R.id.tile_admin_nvram_sort_desc:
            item.setEnabled(false);
            //Store in preferences if any
            final Integer currentSort;
            if (mParentFragmentPreferences != null) {
                currentSort = sortIds.inverse()
                        .get(mParentFragmentPreferences.getInt(getFormattedPrefKey(SORT), -1));
            } else {
                currentSort = null;
            }

            if (mParentFragmentPreferences != null && (currentSort == null || currentSort != itemId)) {
                //No pref on file or different pref on file => store in preferences
                final SharedPreferences.Editor editor = mParentFragmentPreferences.edit();
                editor.putInt(getFormattedPrefKey(SORT), sortIds.get(itemId));
                editor.apply();
            }
            break;
        default:
            break;
        }

        if (itemId != R.id.tile_admin_nvram_share) {
            Map<Object, Object> mNvramInfoToDisplayCopy = null;

            boolean notifyDatasetChanged = false;

            //Filter out by search and sort preferences
            final String textToFind = mParentFragmentPreferences != null
                    ? mParentFragmentPreferences.getString(getFormattedPrefKey(LAST_SEARCH), null)
                    : null;
            if (isNullOrEmpty(textToFind)) {
                //Filter on while dataset
                final Properties mNvramInfoDefaultSortingData = mNvramInfoDefaultSorting.getData();
                switch (itemId) {
                case R.id.tile_admin_nvram_sort_default:
                    mNvramInfoToDisplay = new HashMap<>(mNvramInfoDefaultSortingData);
                    notifyDatasetChanged = true;
                    break;
                case R.id.tile_admin_nvram_sort_asc:
                    mNvramInfoToDisplay = new TreeMap<>(COMPARATOR_STRING_CASE_INSENSITIVE);
                    mNvramInfoToDisplay.putAll(mNvramInfoDefaultSortingData);
                    notifyDatasetChanged = true;
                    break;
                case R.id.tile_admin_nvram_sort_desc:
                    mNvramInfoToDisplay = new TreeMap<>(COMPARATOR_REVERSE_STRING_CASE_INSENSITIVE);
                    mNvramInfoToDisplay.putAll(mNvramInfoDefaultSortingData);
                    notifyDatasetChanged = true;
                    break;
                default:
                    break;
                }
                mNvramInfoToDisplayCopy = mNvramInfoToDisplay;
            } else {
                //Already filtered data
                final Map<Object, Object> adapterNvramInfo = ((NVRAMDataRecyclerViewAdapter) mAdapter)
                        .getNvramInfo();
                switch (itemId) {
                case R.id.tile_admin_nvram_sort_default:
                    mNvramInfoToDisplayCopy = new HashMap<>(adapterNvramInfo);
                    notifyDatasetChanged = true;
                    break;
                case R.id.tile_admin_nvram_sort_asc:
                    mNvramInfoToDisplayCopy = new TreeMap<>(COMPARATOR_STRING_CASE_INSENSITIVE);
                    mNvramInfoToDisplayCopy.putAll(adapterNvramInfo);
                    notifyDatasetChanged = true;
                    break;
                case R.id.tile_admin_nvram_sort_desc:
                    mNvramInfoToDisplayCopy = new TreeMap<>(COMPARATOR_REVERSE_STRING_CASE_INSENSITIVE);
                    mNvramInfoToDisplayCopy.putAll(adapterNvramInfo);
                    notifyDatasetChanged = true;
                    break;
                default:
                    break;
                }
            }

            if (mNvramInfoToDisplayCopy != null && notifyDatasetChanged) {
                ((NVRAMDataRecyclerViewAdapter) mAdapter).setEntryList(mNvramInfoToDisplayCopy);
                mAdapter.notifyDataSetChanged();
            }
        } else {
            //Share action
            final Map<Object, Object> nvramInfo = ((NVRAMDataRecyclerViewAdapter) mAdapter).getNvramInfo();
            if (nvramInfo == null || nvramInfo.isEmpty()) {
                Crouton.makeText(mParentFragmentActivity, "Nothing to share!", Style.ALERT).show();
                return true;
            }

            Exception exception = null;
            File file = new File(mParentFragmentActivity.getCacheDir(), String.format("nvram_data_%s_%s_%s.txt",
                    mRouter.getUuid(), mRouter.getName(), mRouter.getRemoteIpAddress()));
            OutputStream outputStream = null;
            try {
                outputStream = new BufferedOutputStream(new FileOutputStream(file, false));
                outputStream.write(PROPERTIES_JOINER_TO_FILE.join(nvramInfo).getBytes());
            } catch (IOException e) {
                exception = e;
                e.printStackTrace();
            } finally {
                try {
                    if (outputStream != null) {
                        outputStream.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (exception != null) {
                Crouton.makeText(mParentFragmentActivity,
                        "Error while trying to share file - please try again later", Style.ALERT).show();
                return true;
            }

            setShareFile(file);

            return true;
        }

        return false;
    }

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

            @Nullable
            @Override
            public None loadInBackground() {

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

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

                    mNvramInfoToDisplay.clear();
                    mNvramInfoDefaultSorting.clear();

                    NVRAMInfo nvramInfoTmp = null;

                    try {
                        nvramInfoTmp = SSHUtils.getNVRamInfoFromRouter(mRouter, mGlobalPreferences);
                    } finally {
                        if (nvramInfoTmp != null) {
                            mNvramInfoDefaultSorting.putAll(nvramInfoTmp);
                        }

                        final String[] nvramSize = SSHUtils.getManualProperty(mRouter, mGlobalPreferences,
                                "nvram show 2>&1 1>/dev/null");
                        if (nvramSize != null && nvramSize.length > 0) {
                            final List<String> nvramUsageList = StatusRouterSpaceUsageTile.NVRAM_SIZE_SPLITTER
                                    .splitToList(nvramSize[0]);
                            if (nvramUsageList != null && !nvramUsageList.isEmpty()) {
                                final String value = nvramUsageList.get(0);
                                if (value != null) {
                                    mNvramInfoDefaultSorting.setProperty(NVRAM_SIZE, value);
                                }
                            }
                        }

                    }

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

                    //Now apply sorting here (as per user-preferences)

                    final Properties defaultNVRAMInfo = mNvramInfoDefaultSorting.getData();
                    if (mParentFragmentPreferences != null) {
                        final Integer currentSort = sortIds.inverse()
                                .get(mParentFragmentPreferences.getInt(getFormattedPrefKey(SORT), -1));
                        if (currentSort == null || currentSort <= 0) {
                            mNvramInfoToDisplay = new HashMap<>(defaultNVRAMInfo);
                        } else {
                            switch (currentSort) {
                            case R.id.tile_admin_nvram_sort_asc:
                                //asc
                                mNvramInfoToDisplay = new TreeMap<>(COMPARATOR_STRING_CASE_INSENSITIVE);
                                mNvramInfoToDisplay.putAll(defaultNVRAMInfo);
                                break;
                            case R.id.tile_admin_nvram_sort_desc:
                                //desc
                                mNvramInfoToDisplay = new TreeMap<>(COMPARATOR_REVERSE_STRING_CASE_INSENSITIVE);
                                mNvramInfoToDisplay.putAll(defaultNVRAMInfo);
                                break;
                            case R.id.tile_admin_nvram_sort_default:
                            default:
                                mNvramInfoToDisplay = new HashMap<>(defaultNVRAMInfo);
                                break;
                            }
                        }
                    } else {
                        mNvramInfoToDisplay = new HashMap<>(defaultNVRAMInfo);
                    }

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

            }
        };
    }

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

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

    @Override
    public void onLoadFinished(Loader<None> loader, None data) {
        //Set tiles
        Log.d(LOG_TAG, "onLoadFinished: loader=" + loader + " / data=" + data);

        layout.findViewById(R.id.tile_admin_nvram_menu).setVisibility(View.VISIBLE);
        layout.findViewById(R.id.tile_admin_nvram_toolbar).setVisibility(View.VISIBLE);

        if (data == null || mNvramInfoToDisplay.isEmpty()) {
            data = (None) new None().setException(new DDWRTNoDataException("No Data!"));
        }

        layout.findViewById(R.id.tile_admin_nvram_loading_view).setVisibility(View.GONE);

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

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

        //NVRAM
        final Object nvramSize = mNvramInfoToDisplay.remove(NVRAM_SIZE);
        ((TextView) this.layout.findViewById(R.id.tile_admin_nvram_size))
                .setText(nvramSize != null ? nvramSize.toString() : "-");

        if (!(exception instanceof DDWRTTileAutoRefreshNotAllowedException)) {

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

            //Filter out by search and sort preferences
            final String textToFind = mParentFragmentPreferences != null
                    ? mParentFragmentPreferences.getString(getFormattedPrefKey(LAST_SEARCH), null)
                    : null;

            if (isNullOrEmpty(textToFind)) {
                ((NVRAMDataRecyclerViewAdapter) mAdapter).setEntryList(mNvramInfoToDisplay);
                mAdapter.notifyDataSetChanged();
            } else {

                //Filter out (and sort by user-preference)
                final Properties mNvramInfoDefaultSortingData = mNvramInfoDefaultSorting.getData();
                //Update adapter in the preferences
                final Map<Object, Object> mNvramInfoToDisplayCopy;
                if (mParentFragmentPreferences != null) {
                    final Integer currentSort = sortIds.inverse()
                            .get(mParentFragmentPreferences.getInt(getFormattedPrefKey(SORT), -1));
                    if (currentSort == null || currentSort <= 0) {
                        mNvramInfoToDisplayCopy = new HashMap<>();
                    } else {
                        switch (currentSort) {
                        case R.id.tile_admin_nvram_sort_asc:
                            //asc
                            mNvramInfoToDisplayCopy = new TreeMap<>(COMPARATOR_STRING_CASE_INSENSITIVE);
                            break;
                        case R.id.tile_admin_nvram_sort_desc:
                            //desc
                            mNvramInfoToDisplayCopy = new TreeMap<>(COMPARATOR_REVERSE_STRING_CASE_INSENSITIVE);
                            break;
                        case R.id.tile_admin_nvram_sort_default:
                        default:
                            mNvramInfoToDisplayCopy = new HashMap<>();
                            break;
                        }
                    }
                } else {
                    mNvramInfoToDisplayCopy = new HashMap<>();
                }

                //noinspection ConstantConditions
                for (Map.Entry<Object, Object> entry : (mParentFragmentPreferences == null ? mNvramInfoToDisplay
                        : mNvramInfoDefaultSortingData).entrySet()) {
                    final Object key = entry.getKey();
                    final Object value = entry.getValue();
                    if (key == null) {
                        continue;
                    }
                    if (containsIgnoreCase(key.toString(), textToFind)
                            || containsIgnoreCase(value.toString(), textToFind)) {
                        mNvramInfoToDisplayCopy.put(key, value);
                    }
                }

                ((NVRAMDataRecyclerViewAdapter) mAdapter).setEntryList(mNvramInfoToDisplayCopy);
                mAdapter.notifyDataSetChanged();
            }

        }

        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_admin_nvram_togglebutton_title,
                R.id.tile_admin_nvram_togglebutton_separator);

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

}