org.rm3l.ddwrt.fragments.DDWRTBaseFragment.java Source code

Java tutorial

Introduction

Here is the source code for org.rm3l.ddwrt.fragments.DDWRTBaseFragment.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.fragments;

import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v7.widget.CardView;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;

import com.actionbarsherlock.app.SherlockFragment;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.rm3l.ddwrt.DDWRTMainActivity;
import org.rm3l.ddwrt.R;
import org.rm3l.ddwrt.fragments.access.AccessWANAccessFragment;
import org.rm3l.ddwrt.fragments.admin.AdminNVRAMFragment;
import org.rm3l.ddwrt.fragments.nat_qos.NATQoSDMZFragment;
import org.rm3l.ddwrt.fragments.nat_qos.NATQoSPortForwardingFragment;
import org.rm3l.ddwrt.fragments.nat_qos.NATQoSPortRangeForwardingFragment;
import org.rm3l.ddwrt.fragments.nat_qos.NATQoSPortTriggeringFragment;
import org.rm3l.ddwrt.fragments.nat_qos.NATQoSQoSFragment;
import org.rm3l.ddwrt.fragments.nat_qos.NATQoSUPnPFragment;
import org.rm3l.ddwrt.fragments.security.SecurityFirewallFragment;
import org.rm3l.ddwrt.fragments.security.SecurityVPNPassthroughFragment;
import org.rm3l.ddwrt.fragments.services.ServicesOpenVPNFragment;
import org.rm3l.ddwrt.fragments.setup.SetupBasicFragment;
import org.rm3l.ddwrt.fragments.setup.SetupDDNSFragment;
import org.rm3l.ddwrt.fragments.setup.SetupEoIPFragment;
import org.rm3l.ddwrt.fragments.setup.SetupIPv6Fragment;
import org.rm3l.ddwrt.fragments.setup.SetupMacCloningFragment;
import org.rm3l.ddwrt.fragments.setup.SetupNetworkingFragment;
import org.rm3l.ddwrt.fragments.setup.SetupRoutingFragment;
import org.rm3l.ddwrt.fragments.setup.SetupVLANFragment;
import org.rm3l.ddwrt.fragments.status.StatusBandwidthFragment;
import org.rm3l.ddwrt.fragments.status.StatusClientsFragment;
import org.rm3l.ddwrt.fragments.status.StatusLANFragment;
import org.rm3l.ddwrt.fragments.status.StatusRouterFragment;
import org.rm3l.ddwrt.fragments.status.StatusSyslogFragment;
import org.rm3l.ddwrt.fragments.status.StatusTimeFragment;
import org.rm3l.ddwrt.fragments.status.StatusWANFragment;
import org.rm3l.ddwrt.fragments.status.StatusWirelessFragment;
import org.rm3l.ddwrt.fragments.wireless.WirelessBasicFragment;
import org.rm3l.ddwrt.fragments.wireless.WirelessMACFilteringFragment;
import org.rm3l.ddwrt.fragments.wireless.WirelessRadiusFragment;
import org.rm3l.ddwrt.fragments.wireless.WirelessSecurityFragment;
import org.rm3l.ddwrt.mgmt.RouterManagementActivity;
import org.rm3l.ddwrt.prefs.sort.DDWRTSortingStrategy;
import org.rm3l.ddwrt.prefs.sort.SortingStrategy;
import org.rm3l.ddwrt.resources.conn.Router;
import org.rm3l.ddwrt.tiles.DDWRTTile;
import org.rm3l.ddwrt.utils.Utils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static android.widget.FrameLayout.LayoutParams;
import static org.rm3l.ddwrt.utils.Utils.getThemeBackgroundColor;
import static org.rm3l.ddwrt.utils.Utils.isThemeLight;

/**
 * Abstract base fragment
 *
 * @author <a href="mailto:apps+ddwrt@rm3l.org">Armel S.</a>
 */
public abstract class DDWRTBaseFragment<T> extends SherlockFragment implements LoaderManager.LoaderCallbacks<T> {

    public static final String TAB_TITLE = "fragment_tab_title";
    public static final String FRAGMENT_CLASS = "fragment_class";
    public static final String ROUTER_CONNECTION_INFO = "router_info";
    public static final String PARENT_SECTION_TITLE = "parent_section_title";

    private static final String LOG_TAG = DDWRTBaseFragment.class.getSimpleName();
    public static final String STATE_LOADER_IDS = "loaderIds";

    protected LinearLayout mLayout;
    protected Router router;
    private CharSequence mTabTitle;
    private CharSequence mParentSectionTitle;
    @Nullable
    private List<DDWRTTile> fragmentTiles; //fragment tiles and the ID they are assigned
    @NotNull
    private DDWRTMainActivity ddwrtMainActivity;
    private Class<? extends DDWRTBaseFragment> mClazz;

    private final Map<Integer, Object> loaderIdsInUse = Maps.newHashMap();

    protected boolean mLoaderStopped = true;

    protected ViewGroup viewGroup;

    @NotNull
    private PageSlidingTabStripFragment parentFragment;

    public void setLoaderStopped(boolean mLoaderStopped) {
        this.mLoaderStopped = mLoaderStopped;
    }

    @Nullable
    public static DDWRTBaseFragment newInstance(PageSlidingTabStripFragment parentFragment,
            @NotNull final Class<? extends DDWRTBaseFragment> clazz, @NotNull final CharSequence parentSectionTitle,
            @NotNull final CharSequence tabTitle, @Nullable final String router) {
        try {
            @NotNull
            final DDWRTBaseFragment fragment = clazz.newInstance().setTabTitle(tabTitle)
                    .setParentSectionTitle(parentSectionTitle);
            fragment.mClazz = clazz;
            fragment.parentFragment = parentFragment;

            @NotNull
            Bundle args = new Bundle();
            args.putCharSequence(TAB_TITLE, tabTitle);
            args.putCharSequence(PARENT_SECTION_TITLE, parentSectionTitle);
            args.putString(FRAGMENT_CLASS, clazz.getCanonicalName());
            args.putString(ROUTER_CONNECTION_INFO, router);
            fragment.setArguments(args);

            return fragment;

        } catch (java.lang.InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    @NotNull
    public static DDWRTBaseFragment[] getFragments(@NotNull PageSlidingTabStripFragment parentFragment,
            @NotNull final Resources resources, int parentSectionNumber, String sortingStrategy,
            @Nullable final String router) {
        Log.d(LOG_TAG, "getFragments(" + parentSectionNumber + ", " + sortingStrategy + ")");

        final Class sortingStrategyClass;
        @Nullable
        SortingStrategy sortingStrategyInstance = null;
        @Nullable
        Exception exception = null;
        try {
            sortingStrategyClass = Class.forName(sortingStrategy);
            sortingStrategyInstance = (SortingStrategy) sortingStrategyClass.newInstance();
        } catch (@NotNull final Exception e) {
            e.printStackTrace();
            exception = e;
        }

        if (exception != null) {
            //Default one
            Log.d(LOG_TAG, "An error occurred - using DDWRTSortingStrategy default strategy: " + exception);
            sortingStrategyInstance = new DDWRTSortingStrategy();
        }

        String parentSectionTitle;

        @NotNull
        final DDWRTBaseFragment[] tabsToSort;
        switch (parentSectionNumber) {
        case 0:
            parentSectionTitle = resources.getString(R.string.status);
            //1 = Status => {Router, WAN, LAN, Wireless, Bandwidth, Syslog, Sysinfo}
            tabsToSort = new DDWRTBaseFragment[8];
            tabsToSort[0] = DDWRTBaseFragment.newInstance(parentFragment, StatusRouterFragment.class,
                    parentSectionTitle, resources.getString(R.string.status_router), router);
            tabsToSort[1] = DDWRTBaseFragment.newInstance(parentFragment, StatusWANFragment.class,
                    parentSectionTitle, resources.getString(R.string.status_wan), router);
            tabsToSort[2] = DDWRTBaseFragment.newInstance(parentFragment, StatusLANFragment.class,
                    parentSectionTitle, resources.getString(R.string.status_lan), router);
            tabsToSort[3] = DDWRTBaseFragment.newInstance(parentFragment, StatusClientsFragment.class,
                    parentSectionTitle, resources.getString(R.string.status_clients), router);
            tabsToSort[4] = DDWRTBaseFragment.newInstance(parentFragment, StatusTimeFragment.class,
                    parentSectionTitle, resources.getString(R.string.status_time), router);
            tabsToSort[5] = DDWRTBaseFragment.newInstance(parentFragment, StatusWirelessFragment.class,
                    parentSectionTitle, resources.getString(R.string.status_wireless), router);
            tabsToSort[6] = DDWRTBaseFragment.newInstance(parentFragment, StatusBandwidthFragment.class,
                    parentSectionTitle, resources.getString(R.string.status_bandwidth), router);
            tabsToSort[7] = DDWRTBaseFragment.newInstance(parentFragment, StatusSyslogFragment.class,
                    parentSectionTitle, resources.getString(R.string.status_syslog), router);
            //                tabsToSort[6] = DDWRTBaseFragment.newInstance(StatusSysinfoFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.status_sysinfo), router);
            break;
        //            case 1: TODO Remove me
        case 1001:
            parentSectionTitle = resources.getString(R.string.setup);
            //2 = Setup => {Basic, IPv6, DDNS, MAC Cloning, Routing, VLANs, Networking, EoIP}
            tabsToSort = new DDWRTBaseFragment[8];
            tabsToSort[0] = DDWRTBaseFragment.newInstance(parentFragment, SetupBasicFragment.class,
                    parentSectionTitle, resources.getString(R.string.setup_basic), router);
            tabsToSort[1] = DDWRTBaseFragment.newInstance(parentFragment, SetupIPv6Fragment.class,
                    parentSectionTitle, resources.getString(R.string.setup_ipv6), router);
            tabsToSort[2] = DDWRTBaseFragment.newInstance(parentFragment, SetupDDNSFragment.class,
                    parentSectionTitle, resources.getString(R.string.setup_ddns), router);
            tabsToSort[3] = DDWRTBaseFragment.newInstance(parentFragment, SetupMacCloningFragment.class,
                    parentSectionTitle, resources.getString(R.string.setup_mac_cloning), router);
            tabsToSort[4] = DDWRTBaseFragment.newInstance(parentFragment, SetupRoutingFragment.class,
                    parentSectionTitle, resources.getString(R.string.setup_routing), router);
            tabsToSort[5] = DDWRTBaseFragment.newInstance(parentFragment, SetupVLANFragment.class,
                    parentSectionTitle, resources.getString(R.string.setup_vlans), router);
            tabsToSort[6] = DDWRTBaseFragment.newInstance(parentFragment, SetupNetworkingFragment.class,
                    parentSectionTitle, resources.getString(R.string.setup_networking), router);
            tabsToSort[7] = DDWRTBaseFragment.newInstance(parentFragment, SetupEoIPFragment.class,
                    parentSectionTitle, resources.getString(R.string.setup_eoip), router);
            break;
        case 111:
            //            case 1:
            parentSectionTitle = resources.getString(R.string.wireless);
            //3 = Wireless => {Basic, Radius, Security, MAC Filter, WL0, WL1, ...}
            tabsToSort = new DDWRTBaseFragment[4];
            tabsToSort[0] = DDWRTBaseFragment.newInstance(parentFragment, WirelessBasicFragment.class,
                    parentSectionTitle, resources.getString(R.string.wireless_basic_settings), router);
            tabsToSort[1] = DDWRTBaseFragment.newInstance(parentFragment, WirelessRadiusFragment.class,
                    parentSectionTitle, resources.getString(R.string.wireless_radius), router);
            tabsToSort[2] = DDWRTBaseFragment.newInstance(parentFragment, WirelessSecurityFragment.class,
                    parentSectionTitle, resources.getString(R.string.wireless_security), router);
            tabsToSort[3] = DDWRTBaseFragment.newInstance(parentFragment, WirelessMACFilteringFragment.class,
                    parentSectionTitle, resources.getString(R.string.wireless_mac_filter), router);
            break;
        case 1:
            //            case 2:
            parentSectionTitle = resources.getString(R.string.services);
            //4 = Services => {Services, FreeRadius, PPoE, VPN, USB, NAS, HotSpot, SIP Proxy, Adblocking, Webserver}
            tabsToSort = new DDWRTBaseFragment[1];
            tabsToSort[0] = DDWRTBaseFragment.newInstance(parentFragment, ServicesOpenVPNFragment.class,
                    parentSectionTitle, resources.getString(R.string.services_openvpn), router);

            //                tabsToSort = new DDWRTBaseFragment[10];
            //                tabsToSort[0] = DDWRTBaseFragment.newInstance(ServicesServicesFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.services_services), router);
            //                tabsToSort[1] = DDWRTBaseFragment.newInstance(ServicesFreeRadiusFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.services_freeradius), router);
            //                tabsToSort[2] = DDWRTBaseFragment.newInstance(ServicesPPoEFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.services_ppoe), router);
            //                tabsToSort[3] = DDWRTBaseFragment.newInstance(ServicesOpenVPNFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.services_vpn), router);
            //                tabsToSort[4] = DDWRTBaseFragment.newInstance(ServicesUSBFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.services_usb), router);
            //                tabsToSort[5] = DDWRTBaseFragment.newInstance(ServicesNASFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.services_nas), router);
            //                tabsToSort[6] = DDWRTBaseFragment.newInstance(ServicesHotSpotFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.services_hostspot), router);
            //                tabsToSort[7] = DDWRTBaseFragment.newInstance(ServicesSIPFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.services_sip), router);
            //                tabsToSort[8] = DDWRTBaseFragment.newInstance(ServicesAdBlockingFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.services_adblocking), router);
            //                tabsToSort[9] = DDWRTBaseFragment.newInstance(ServicesWebServerFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.services_webserver), router);
            break;
        case 333:
            //            case 3:
            //5 = Security => {Firewall, VPN Passthrough}
            parentSectionTitle = resources.getString(R.string.security);
            tabsToSort = new DDWRTBaseFragment[2];
            tabsToSort[0] = DDWRTBaseFragment.newInstance(parentFragment, SecurityFirewallFragment.class,
                    parentSectionTitle, resources.getString(R.string.security_firewall), router);
            tabsToSort[1] = DDWRTBaseFragment.newInstance(parentFragment, SecurityVPNPassthroughFragment.class,
                    parentSectionTitle, resources.getString(R.string.security_vpn_passthrough), router);
            break;
        case 444:
            //6 = Access => {WAN}
            parentSectionTitle = resources.getString(R.string.access_restrictions);
            tabsToSort = new DDWRTBaseFragment[1];
            tabsToSort[0] = DDWRTBaseFragment.newInstance(parentFragment, AccessWANAccessFragment.class,
                    parentSectionTitle, resources.getString(R.string.access_restrictions_wan), router);
            break;
        case 555:
            parentSectionTitle = resources.getString(R.string.nat_qos);
            //7 = NAT/QoS => {Port Fwding, Port Range Fwding, Port Triggerring, UPnP, DMZ, QoS}
            tabsToSort = new DDWRTBaseFragment[6];
            tabsToSort[0] = DDWRTBaseFragment.newInstance(parentFragment, NATQoSPortForwardingFragment.class,
                    parentSectionTitle, resources.getString(R.string.nat_qos_port_forwarding), router);
            tabsToSort[1] = DDWRTBaseFragment.newInstance(parentFragment, NATQoSPortRangeForwardingFragment.class,
                    parentSectionTitle, resources.getString(R.string.nat_qos_port_range_forwarding), router);
            tabsToSort[2] = DDWRTBaseFragment.newInstance(parentFragment, NATQoSPortTriggeringFragment.class,
                    parentSectionTitle, resources.getString(R.string.nat_qos_port_trigger), router);
            tabsToSort[3] = DDWRTBaseFragment.newInstance(parentFragment, NATQoSUPnPFragment.class,
                    parentSectionTitle, resources.getString(R.string.nat_qos_upnp), router);
            tabsToSort[4] = DDWRTBaseFragment.newInstance(parentFragment, NATQoSDMZFragment.class,
                    parentSectionTitle, resources.getString(R.string.nat_qos_dmz), router);
            tabsToSort[5] = DDWRTBaseFragment.newInstance(parentFragment, NATQoSQoSFragment.class,
                    parentSectionTitle, resources.getString(R.string.nat_qos_qos), router);
            break;
        case 2:
            //            case 6:
            parentSectionTitle = resources.getString(R.string.admin_area);
            //8 => Admin => {Management, Keep Alive, Commands, WOL, Factory, Upgrade, Backup}
            tabsToSort = new DDWRTBaseFragment[1];
            //                tabsToSort[0] = DDWRTBaseFragment.newInstance(AdminCommandsFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.admin_area_cmds), router);
            tabsToSort[0] = DDWRTBaseFragment.newInstance(parentFragment, AdminNVRAMFragment.class,
                    parentSectionTitle, resources.getString(R.string.admin_area_nvram), router);
            //                tabsToSort = new DDWRTBaseFragment[7];
            //                tabsToSort[0] = DDWRTBaseFragment.newInstance(AdminManagementFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.admin_area_mgmt), router);
            //                tabsToSort[1] = DDWRTBaseFragment.newInstance(AdminKeepAliveFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.admin_area_keep_alive), router);
            //                tabsToSort[2] = DDWRTBaseFragment.newInstance(AdminCommandsFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.admin_area_cmds), router);
            //                tabsToSort[3] = DDWRTBaseFragment.newInstance(AdminWOLFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.admin_area_wol), router);
            //                tabsToSort[4] = DDWRTBaseFragment.newInstance(AdminFactoryDefaultsFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.admin_area_factory), router);
            //                tabsToSort[5] = DDWRTBaseFragment.newInstance(AdminUpgradeFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.admin_area_upgrade), router);
            //                tabsToSort[6] = DDWRTBaseFragment.newInstance(AdminBackupFragment.class, parentSectionTitle,
            //                        resources.getString(R.string.admin_area_backup), router);
            break;
        default:
            //This should NOT happen => Error
            parentSectionTitle = (resources.getString(R.string.unknown) + " (" + parentSectionNumber + ")");
            tabsToSort = new DDWRTBaseFragment[1];
            tabsToSort[0] = DDWRTBaseFragment.newInstance(parentFragment, NoDataFragment.class, parentSectionTitle,
                    resources.getString(R.string.unknown), null);
            break;

        }

        return tabsToSort.length > 0 ? sortingStrategyInstance.sort(tabsToSort) : tabsToSort;

    }

    @NotNull
    public final DDWRTBaseFragment setParentSectionTitle(@NotNull final CharSequence parentSectionTitle) {
        this.mParentSectionTitle = parentSectionTitle;
        return this;
    }

    public final CharSequence getTabTitle() {
        return mTabTitle;
    }

    @NotNull
    public final DDWRTBaseFragment setTabTitle(@NotNull final CharSequence tabTitle) {
        this.mTabTitle = tabTitle;
        return this;
    }

    /**
     * Called to do initial creation of a fragment.  This is called after
     * {@link #onAttach(android.app.Activity)}} and before
     * {@link #onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)}.
     * <p/>
     * <p>Note that this can be called while the fragment's activity is
     * still in the process of being created.  As such, you can not rely
     * on things like the activity's content view hierarchy being initialized
     * at this point.  If you want to do work once the activity itself is
     * created, see {@link #onActivityCreated(android.os.Bundle)}.
     *
     * @param savedInstanceState If the fragment is being re-created from
     *                           a previous saved state, this is the state.
     */
    @Override
    public final void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setHasOptionsMenu(true);

        this.router = RouterManagementActivity.getDao(this.getActivity())
                .getRouter(getArguments().getString(ROUTER_CONNECTION_INFO));
        Log.d(LOG_TAG, "onCreate() loaderIdsInUse: " + loaderIdsInUse);
        if (savedInstanceState != null) {
            final ArrayList<Integer> loaderIdsSaved = savedInstanceState.getIntegerArrayList(STATE_LOADER_IDS);
            Log.d(LOG_TAG, "onCreate() loaderIdsSaved: " + loaderIdsSaved);
            if (loaderIdsSaved != null) {
                //Destroy existing IDs, if any, as new loaders will get created in onResume()
                final LoaderManager loaderManager = getLoaderManager();
                for (final Integer loaderId : loaderIdsSaved) {
                    if (loaderId == null) {
                        continue;
                    }
                    loaderManager.destroyLoader(loaderId);
                }
            }
        }
        this.loaderIdsInUse.clear();
        viewGroup = (ScrollView) getSherlockActivity().getLayoutInflater()
                .inflate(R.layout.base_tiles_container_scrollview, null);

        this.fragmentTiles = this.getTiles(savedInstanceState);
    }

    /**
     * Called to have the fragment instantiate its user interface view.
     * This is optional, and non-graphical fragments can return null (which
     * is the default implementation).  This will be called between
     * {@link #onCreate(android.os.Bundle)} and {@link #onActivityCreated(android.os.Bundle)}.
     * <p/>
     * <p>If you return a View from here, you will later be called in
     * {@link #onDestroyView} when the view is being released.
     *
     * @param inflater           The LayoutInflater object that can be used to inflate
     *                           any views in the fragment,
     * @param container          If non-null, this is the parent view that the fragment's
     *                           UI should be attached to.  The fragment should not add the view itself,
     *                           but this can be used to generate the LayoutParams of the view.
     * @param savedInstanceState If non-null, this fragment is being re-constructed
     *                           from a previous saved state as given here.
     * @return Return the View for the fragment's UI, or null.
     */
    @NotNull
    @Override
    public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return this.getLayout();
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        //Android disagrees with that: "Can't retain fragments that are nested in other fragments"
        //        // this is really important in order to save the state across screen
        //        // configuration changes for example
        //        setRetainInstance(true);
    }

    private void initLoaders() {
        // initiate the loaders to do the background work
        final LoaderManager loaderManager = getLoaderManager();

        loaderManager.initLoader(0, null, this);
        this.setLoaderStopped(false);
        loaderIdsInUse.put(0, this);

        Log.d(LOG_TAG, "fragmentTiles: " + this.fragmentTiles);

        if (this.fragmentTiles != null) {
            for (final DDWRTTile ddwrtTile : fragmentTiles) {
                if (ddwrtTile == null) {
                    continue;
                }
                final int nextLoaderId = Long.valueOf(Utils.getNextLoaderId()).intValue();
                loaderManager.initLoader(nextLoaderId, null, ddwrtTile);
                ddwrtTile.setLoaderStopped(false);
                loaderIdsInUse.put(nextLoaderId, ddwrtTile);
            }
        }
    }

    private void stopLoaders() {
        final LoaderManager loaderManager = getLoaderManager();
        for (final Map.Entry<Integer, Object> loaderIdInUse : loaderIdsInUse.entrySet()) {
            loaderManager.destroyLoader(loaderIdInUse.getKey());
            final Object value = loaderIdInUse.getValue();
            if (value == this) {
                //Mark this as stopped
                this.setLoaderStopped(true);
            } else if (value instanceof DDWRTTile) {
                ((DDWRTTile) value).setLoaderStopped(true);
            }
        }
        loaderIdsInUse.clear();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        //save the loader ids on file
        outState.putIntegerArrayList(STATE_LOADER_IDS, Lists.newArrayList(loaderIdsInUse.keySet()));

        super.onSaveInstanceState(outState);
    }

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

    @Override
    public void onPause() {
        stopLoaders();
        super.onPause();
    }

    @Override
    public void onStop() {
        stopLoaders();
        super.onStop();
    }

    @Override
    public void onDestroy() {
        stopLoaders();
        super.onDestroy();
    }

    @NotNull
    private ViewGroup getLayout() {
        @NotNull
        final LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        final int margin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8,
                getResources().getDisplayMetrics());
        params.setMargins(margin, margin, margin, margin);

        boolean atLeastOneTileAdded = false;

        final SherlockFragmentActivity sherlockActivity = getSherlockActivity();
        if (this.fragmentTiles != null && !this.fragmentTiles.isEmpty()) {

            @NotNull
            final List<CardView> cards = new ArrayList<CardView>();

            final CardView.LayoutParams cardViewLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT,
                    LayoutParams.WRAP_CONTENT);

            final int themeBackgroundColor = getThemeBackgroundColor(sherlockActivity, router.getUuid());

            final boolean isThemeLight = isThemeLight(sherlockActivity, themeBackgroundColor);

            for (@NotNull
            final DDWRTTile ddwrtTile : this.fragmentTiles) {
                @Nullable
                final ViewGroup viewGroupLayout = ddwrtTile.getViewGroupLayout();
                atLeastOneTileAdded |= (viewGroupLayout != null);

                if (viewGroupLayout == null) {
                    continue;
                }

                if (isThemeLight) {
                    ((TextView) viewGroupLayout.findViewById(ddwrtTile.getTileTitleViewId()))
                            .setTextColor(getResources().getColor(android.R.color.holo_blue_dark));
                }

                //Detach this from Parent
                final ViewParent parent = viewGroupLayout.getParent();
                if (parent instanceof ViewGroup) {
                    ((ViewGroup) parent).removeView(viewGroupLayout);
                }

                viewGroupLayout.setBackgroundColor(getResources().getColor(android.R.color.transparent));

                final CardView cardView = new CardView(sherlockActivity);
                cardView.setOnClickListener(ddwrtTile);
                cardView.setLayoutParams(cardViewLayoutParams);
                cardView.setCardBackgroundColor(themeBackgroundColor);
                //Add padding to CardView on v20 and before to prevent intersections between the Card content and rounded corners.
                cardView.setPreventCornerOverlap(true);
                //Add padding in API v21+ as well to have the same measurements with previous versions.
                cardView.setUseCompatPadding(true);

                cardView.addView(viewGroupLayout);

                cards.add(cardView);
            }

            atLeastOneTileAdded = (!cards.isEmpty());

            Log.d(LOG_TAG, "atLeastOneTileAdded: " + atLeastOneTileAdded + ", rows: " + cards.size());

            if (atLeastOneTileAdded) {
                //Drop Everything
                //                viewGroup.removeAllViews();

                mLayout = (LinearLayout) viewGroup.findViewById(R.id.tiles_container_scrollview_layout);

                for (@NotNull
                final CardView card : cards) {
                    //                    mTableLayout.removeView(row);
                    mLayout.addView(card);
                }

            }

            ((ScrollView) viewGroup).setFillViewport(true);
        }

        if (viewGroup == null || !atLeastOneTileAdded) {
            viewGroup = new FrameLayout(sherlockActivity);
            @NotNull
            final TextView view = new TextView(sherlockActivity);
            view.setGravity(Gravity.CENTER);
            view.setText(getResources().getString(R.string.no_data));
            view.setBackgroundResource(R.drawable.background_card);
            view.setLayoutParams(params);

            viewGroup.addView(view);
        }

        viewGroup.setBackgroundColor(getResources().getColor(android.R.color.transparent));
        viewGroup.setLayoutParams(params);

        return viewGroup;

    }

    /**
     * Instantiate and return a new Loader for the given ID.
     *
     * @param id   The ID whose loader is to be created.
     * @param args Any arguments supplied by the caller.
     * @return Return a new Loader instance that is ready to start loading.
     */
    @Nullable
    @Override
    public final Loader<T> onCreateLoader(int id, Bundle args) {
        final Loader<T> loader = this.getLoader(id, args);
        if (loader != null) {
            loader.forceLoad();
        }
        return loader;
    }

    @Nullable
    protected Loader<T> getLoader(int id, Bundle args) {
        return null;
    }

    /**
     * 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(Loader<T> loader, T data) {

    }

    /**
     * Called when a previously created loader is being reset, and thus
     * making its data unavailable.  The application should at this point
     * remove any references it has to the Loader's data.
     *
     * @param loader The Loader that is being reset.
     */
    @Override
    public void onLoaderReset(Loader<T> loader) {

    }

    @Nullable
    protected abstract List<DDWRTTile> getTiles(@Nullable Bundle savedInstanceState);

    public void startActivityForResult(Intent intent, DDWRTTile.ActivityResultListener listener) {
        parentFragment.startActivityForResult(intent, listener);
    }

}