org.rm3l.ddwrt.DDWRTMainActivity.java Source code

Java tutorial

Introduction

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

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.view.GravityCompat;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.cocosw.undobar.UndoBarController;
import com.suredigit.inappfeedback.FeedbackDialog;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.rm3l.ddwrt.about.AboutDialog;
import org.rm3l.ddwrt.actions.RebootRouterAction;
import org.rm3l.ddwrt.actions.RestoreRouterDefaultsAction;
import org.rm3l.ddwrt.actions.RouterAction;
import org.rm3l.ddwrt.actions.RouterActionListener;
import org.rm3l.ddwrt.feedback.SendFeedbackDialog;
import org.rm3l.ddwrt.fragments.PageSlidingTabStripFragment;
import org.rm3l.ddwrt.mgmt.RouterManagementActivity;
import org.rm3l.ddwrt.mgmt.dao.DDWRTCompanionDAO;
import org.rm3l.ddwrt.prefs.sort.SortingStrategy;
import org.rm3l.ddwrt.resources.conn.Router;
import org.rm3l.ddwrt.settings.RouterSettingsActivity;
import org.rm3l.ddwrt.tiles.DDWRTTile;
import org.rm3l.ddwrt.utils.SSHUtils;
import org.rm3l.ddwrt.utils.Utils;

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

import static com.google.common.base.Strings.isNullOrEmpty;
import static org.rm3l.ddwrt.utils.DDWRTCompanionConstants.DEFAULT_SHARED_PREFERENCES_KEY;
import static org.rm3l.ddwrt.utils.DDWRTCompanionConstants.DEFAULT_THEME;
import static org.rm3l.ddwrt.utils.DDWRTCompanionConstants.SORTING_STRATEGY_PREF;
import static org.rm3l.ddwrt.utils.DDWRTCompanionConstants.SYNC_INTERVAL_MILLIS_PREF;
import static org.rm3l.ddwrt.utils.DDWRTCompanionConstants.THEMING_PREF;
import static org.rm3l.ddwrt.utils.DDWRTCompanionConstants.TILE_REFRESH_MILLIS;

/**
 * Main Android Activity
 * <p/>
 */
public class DDWRTMainActivity extends SherlockFragmentActivity
        implements ViewPager.OnPageChangeListener, UndoBarController.AdvancedUndoListener, RouterActionListener {

    public static final String TAG = DDWRTMainActivity.class.getSimpleName();
    public static final String SAVE_ITEM_SELECTED = "SAVE_ITEM_SELECTED";
    public static final String SAVE_ROUTER_SELECTED = "SAVE_ROUTER_SELECTED";
    public static final int ROUTER_SETTINGS_ACTIVITY_CODE = 1;
    public static final String IS_SORTING_STRATEGY_CHANGED = "isSortingStrategyChanged";
    public static final String ROUTER_ACTION = "ROUTER_ACTION";
    private static final int LISTENED_REQUEST_CODE = 77;
    private final Handler mAsyncHandler = new Handler();
    DrawerLayout mDrawerLayout;
    ListView mDrawerList;
    ActionBarDrawerToggle mDrawerToggle;
    @NotNull
    private DDWRTCompanionDAO dao;
    @NotNull
    private String mRouterUuid;
    private Menu optionsMenu;
    private CharSequence mDrawerTitle;
    private CharSequence mTitle;
    private String[] mDDWRTNavigationMenuSections;
    //    private PageSlidingTabStripFragment currentPageSlidingTabStripFragment;
    private int mPosition = 0;
    private FeedbackDialog mFeedbackDialog;
    private String mCurrentSortingStrategy;
    private long mCurrentSyncInterval;
    private long mCurrentTheme;
    @NotNull
    private SharedPreferences mPreferences;
    @NotNull
    private SharedPreferences mGlobalPreferences;
    @NotNull
    private Router mRouter;
    private DDWRTTile.ActivityResultListener mCurrentActivityResultListener;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        //SQLite
        this.dao = RouterManagementActivity.getDao(this);
        String uuid = getIntent().getStringExtra(RouterManagementActivity.ROUTER_SELECTED);
        if (uuid == null) {
            if (savedInstanceState != null) {
                uuid = savedInstanceState.getString(SAVE_ROUTER_SELECTED);
            }
        }
        final Router router = this.dao.getRouter(uuid);

        if (router == null) {
            Toast.makeText(this, "No router set or router no longer exists", Toast.LENGTH_LONG).show();
            finish();
            return;
        }

        final String routerName = router.getName();
        setTitle(isNullOrEmpty(routerName) ? router.getRemoteIpAddress() : routerName);

        this.mRouterUuid = router.getUuid();
        this.mRouter = router;

        this.mPreferences = this.getSharedPreferences(this.mRouterUuid, Context.MODE_PRIVATE);
        this.mGlobalPreferences = this.getSharedPreferences(DEFAULT_SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE);

        mTitle = mDrawerTitle = getTitle();
        mDDWRTNavigationMenuSections = getResources().getStringArray(R.array.navigation_drawer_items_array);
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mDrawerList = (ListView) findViewById(R.id.left_drawer);

        // set a custom shadow that overlays the main content when the drawer
        // opens
        mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
        // set up the drawer's list view with items and click listener
        mDrawerList.setAdapter(
                new ArrayAdapter<String>(this, R.layout.drawer_list_item, mDDWRTNavigationMenuSections));
        mDrawerList.setOnItemClickListener(new DrawerItemClickListener());

        // enable ActionBar app icon to behave as action to toggle nav drawer
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        getSupportActionBar().setHomeButtonEnabled(true);

        // ActionBarDrawerToggle ties together the the proper interactions
        // between the sliding drawer and the action bar app icon
        mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */
                mDrawerLayout, /* DrawerLayout object */
                R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */
                R.string.navigation_drawer_open, /* "open drawer" description for accessibility */
                R.string.navigation_drawer_close /* "close drawer" description for accessibility */
        ) {
            public void onDrawerClosed(View view) {
                getSupportActionBar().setTitle(mTitle);
                invalidateOptionsMenu(); // creates call to
                // onPrepareOptionsMenu()
            }

            public void onDrawerOpened(View drawerView) {
                getSupportActionBar().setTitle(mDrawerTitle);
                invalidateOptionsMenu(); // creates call to
                // onPrepareOptionsMenu()
            }
        };
        mDrawerLayout.setDrawerListener(mDrawerToggle);

        final Integer savedPosition;
        int position = 0;
        if (savedInstanceState != null && (savedPosition = savedInstanceState.getInt(SAVE_ITEM_SELECTED)) != null) {
            position = savedPosition;
        }
        mDrawerList.performItemClick(mDrawerList.getChildAt(position), position,
                mDrawerList.getAdapter().getItemId(position));

        mFeedbackDialog = new SendFeedbackDialog(this).getFeedbackDialog();

        //Load from Shared Preferences
        this.mCurrentSortingStrategy = this.mPreferences.getString(SORTING_STRATEGY_PREF, "");
        this.mCurrentSyncInterval = this.mPreferences.getLong(SYNC_INTERVAL_MILLIS_PREF, -1l);
        this.mCurrentTheme = this.mPreferences.getLong(THEMING_PREF, -1l);

        //Recreate Default Preferences if they are no longer available
        final boolean putDefaultSortingStrategy = isNullOrEmpty(this.mCurrentSortingStrategy);
        final boolean putDefaultSyncInterval = (this.mCurrentSyncInterval <= 0l);
        final boolean putDefaultTheme = (this.mCurrentTheme <= 0l);
        if (putDefaultSortingStrategy || putDefaultSyncInterval || putDefaultTheme) {
            //Add default preferences values
            final SharedPreferences.Editor editor = this.mPreferences.edit();
            if (putDefaultSortingStrategy) {
                editor.putString(SORTING_STRATEGY_PREF, SortingStrategy.DEFAULT);
            }
            if (putDefaultSyncInterval) {
                editor.putLong(SYNC_INTERVAL_MILLIS_PREF, TILE_REFRESH_MILLIS);
            }
            if (putDefaultTheme) {
                editor.putLong(THEMING_PREF, DEFAULT_THEME);
            }
            editor.apply();
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle savedInstanceState) {
        savedInstanceState.putInt(SAVE_ITEM_SELECTED, mPosition);
        savedInstanceState.putString(SAVE_ROUTER_SELECTED, mRouterUuid);
        super.onSaveInstanceState(savedInstanceState);
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mFeedbackDialog != null) {
            mFeedbackDialog.dismiss();
        }
        //Close SSH Session as well
        destroySSHSession();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mCurrentActivityResultListener = null;
        if (mFeedbackDialog != null) {
            mFeedbackDialog.dismiss();
        }
        //Close SSH Session as well
        destroySSHSession();
    }

    private void destroySSHSession() {
        //Async to avoid ANR because SSHUtils#destroySession makes use of locking mechanisms
        mAsyncHandler.post(new Runnable() {
            @Override
            public void run() {
                SSHUtils.destroySession(mRouter);
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(com.actionbarsherlock.view.Menu menu) {
        this.optionsMenu = menu;
        getSupportMenuInflater().inflate(R.menu.main_menu, menu);
        if (!BuildConfig.DONATIONS) {
            final MenuItem item = menu.findItem(R.id.action_donate);
            if (item != null) {
                item.setVisible(false);
            }
        }
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(@NotNull com.actionbarsherlock.view.MenuItem item) {

        final String routerName = mRouter.getName();
        final String displayName = isNullOrEmpty(routerName) ? mRouter.getRemoteIpAddress() : routerName;

        switch (item.getItemId()) {

        case android.R.id.home: {
            if (mDrawerLayout.isDrawerOpen(mDrawerList)) {
                mDrawerLayout.closeDrawer(mDrawerList);
            } else {
                mDrawerLayout.openDrawer(mDrawerList);
            }
            return true;
        }
        //            case R.id.action_refresh:
        //                //Disabled for now
        //                Toast.makeText(this.getApplicationContext(), "[FIXME] Hold on. Refresh in progress...", Toast.LENGTH_SHORT).show();
        //                //FIXME Refresh all tiles currently visible
        //                setRefreshActionButtonState(true);
        ////                if (this.mCurrentRefreshAsyncTask != null) {
        ////                    this.mCurrentRefreshAsyncTask.cancel(true);
        ////                }
        ////                this.mCurrentRefreshAsyncTask = new RefreshAsyncTask();
        ////                this.mCurrentRefreshAsyncTask.execute();
        //                return true;

        case R.id.action_settings:
            //Open Settings activity for this item
            final Intent ddWrtMainIntent = new Intent(this, RouterSettingsActivity.class);
            ddWrtMainIntent.putExtra(RouterManagementActivity.ROUTER_SELECTED, this.mRouterUuid);
            this.startActivityForResult(ddWrtMainIntent, ROUTER_SETTINGS_ACTIVITY_CODE);
            return true;
        case R.id.action_donate:
            Utils.openDonateActivity(this);
            return true;
        case R.id.action_about:
            new AboutDialog(this).show();
            return true;
        case R.id.action_feedback:
            if (mFeedbackDialog == null) {
                mFeedbackDialog = new SendFeedbackDialog(this).getFeedbackDialog();
            }
            mFeedbackDialog.show();
            return true;
        case R.id.action_ddwrt_actions_reboot_router:

            new AlertDialog.Builder(this).setIcon(R.drawable.ic_action_alert_warning)
                    .setTitle(String.format("Reboot '%s'", displayName))
                    .setMessage(String.format(
                            "Are you sure you wish to continue?\n'%s' will be rebooted, "
                                    + "and you might have to wait some time before connection is re-established.",
                            displayName))
                    .setCancelable(true).setPositiveButton("Proceed!", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(final DialogInterface dialogInterface, final int i) {
                            final Bundle token = new Bundle();
                            token.putString(ROUTER_ACTION, RouterAction.REBOOT.toString());

                            new UndoBarController.UndoBar(DDWRTMainActivity.this)
                                    .message(String.format("Router '%s' will be rebooted", displayName))
                                    .listener(DDWRTMainActivity.this).token(token).show();
                        }
                    }).setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            //Cancelled - nothing more to do!
                        }
                    }).create().show();

            return true;
        case R.id.action_ddwrt_actions_restore_factory_defaults:

            new AlertDialog.Builder(this).setIcon(R.drawable.ic_action_alert_warning)
                    .setTitle(String.format("Reset '%s'", displayName))
                    .setMessage(String.format("Are you sure you wish to continue?\n"
                            + "This will erase the entire NVRAM, thus resetting all settings back to factory defaults. "
                            + "All of your settings will be erased and '%s' will be rebooted. "
                            + "You might have to wait some time before connection is re-established.\n\n"
                            + "[CAUTION]\n"
                            + "- After resetting DD-WRT, you need to login with the default user name \"root\" and default password \"admin\".\n"
                            + "- Some devices may not boot properly after being reset. In this case, you will have to reflash them.",
                            displayName))
                    .setCancelable(true).setPositiveButton("I understand and I wish to proceed!",
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(final DialogInterface dialogInterface, final int i) {
                                    final Bundle token = new Bundle();
                                    token.putString(ROUTER_ACTION,
                                            RouterAction.RESTORE_FACTORY_DEFAULTS.toString());

                                    new UndoBarController.UndoBar(DDWRTMainActivity.this)
                                            .message(String.format("Router '%s' will be reset", displayName))
                                            .listener(DDWRTMainActivity.this).token(token).show();
                                }
                            })
                    .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            //Cancelled - nothing more to do!
                        }
                    }).create().show();

            return true;
        case R.id.action_ddwrt_actions_firmware_upgrade:
            //TODO
            return true;
        default:
            break;

        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
        // Check which request we're responding to
        switch (requestCode) {
        case LISTENED_REQUEST_CODE:
            //Forward to listener
            if (mCurrentActivityResultListener != null) {
                final DDWRTTile.ActivityResultListener listener = mCurrentActivityResultListener;
                mCurrentActivityResultListener = null;
                listener.onResultCode(resultCode, data);
            }
            break;
        case ROUTER_SETTINGS_ACTIVITY_CODE:
            // Make sure the request was successful and reload U if necessary
            if (resultCode == RESULT_OK) {
                if (this.mCurrentSyncInterval != this.mPreferences.getLong(SYNC_INTERVAL_MILLIS_PREF, -1l)
                        || this.mCurrentTheme != this.mPreferences.getLong(THEMING_PREF, -1l)
                        || !this.mCurrentSortingStrategy
                                .equals(this.mPreferences.getString(SORTING_STRATEGY_PREF, ""))) {
                    //Reload UI
                    final AlertDialog alertDialog = Utils.buildAlertDialog(this, null, "Reloading UI...", false,
                            false);
                    alertDialog.show();
                    ((TextView) alertDialog.findViewById(android.R.id.message))
                            .setGravity(Gravity.CENTER_HORIZONTAL);
                    new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            finish();
                            startActivity(getIntent());
                            alertDialog.cancel();
                        }
                    }, 2000);
                }
            }
            break;
        }

        super.onActivityResult(requestCode, resultCode, data);
    }

    public void setRefreshActionButtonState(final boolean refreshing) {
        if (optionsMenu != null) {
            final MenuItem refreshItem = optionsMenu.findItem(R.id.action_refresh);
            if (refreshItem != null) {
                if (refreshing) {
                    refreshItem.setActionView(R.layout.actionbar_indeterminate_progress);
                } else {
                    refreshItem.setActionView(null);
                }
            }
        }
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        mDrawerToggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // Pass any configuration change to the drawer toggles
        mDrawerToggle.onConfigurationChanged(newConfig);
    }

    private void selectItem(int position) {

        Log.d(TAG, "selectItem @" + position);
        if (position < 0) {
            return;
        }
        this.mPosition = position;

        getSupportFragmentManager().beginTransaction()
                .replace(R.id.content, PageSlidingTabStripFragment.newInstance(this, position, this.mRouterUuid))
                .commit();

        mDrawerLayout.closeDrawer(mDrawerList);
    }

    /**
     * This method will be invoked when the current page is scrolled, either as part
     * of a programmatically initiated smooth scroll or a user initiated touch scroll.
     *
     * @param position             Position index of the first page currently being displayed.
     *                             Page position+1 will be visible if positionOffset is nonzero.
     * @param positionOffset       Value from [0, 1) indicating the offset from the page at position.
     * @param positionOffsetPixels Value in pixels indicating the offset from position.
     */
    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        Log.d(TAG, "onPageScrolled @" + position);
    }

    /**
     * This method will be invoked when a new page becomes selected. Animation is not
     * necessarily complete.
     *
     * @param position Position index of the new selected page.
     */
    @Override
    public void onPageSelected(int position) {
        Log.d(TAG, "onPageSelected (" + position + ")");
    }

    /**
     * Called when the scroll state changes. Useful for discovering when the user
     * begins dragging, when the pager is automatically settling to the current page,
     * or when it is fully stopped/idle.
     *
     * @param state The new scroll state.
     * @see android.support.v4.view.ViewPager#SCROLL_STATE_IDLE
     * @see android.support.v4.view.ViewPager#SCROLL_STATE_DRAGGING
     * @see android.support.v4.view.ViewPager#SCROLL_STATE_SETTLING
     */
    @Override
    public void onPageScrollStateChanged(int state) {
        Log.d(TAG, "onPageScrollStateChanged (" + state + ")");
    }

    @Override
    public void onUndo(@android.support.annotation.Nullable Parcelable parcelable) {
        //Nothing to do
    }

    @Override
    public void onHide(@android.support.annotation.Nullable Parcelable parcelable) {
        if (parcelable instanceof Bundle) {
            final Bundle token = (Bundle) parcelable;
            final String routerAction = token.getString(ROUTER_ACTION);
            Log.d(TAG, "routerAction: [" + routerAction + "]");
            if (isNullOrEmpty(routerAction)) {
                return;
            }
            try {
                switch (RouterAction.valueOf(routerAction)) {
                case REBOOT:
                    new RebootRouterAction(this, mGlobalPreferences).execute(mRouter);
                    break;
                case RESTORE_FACTORY_DEFAULTS:
                    new RestoreRouterDefaultsAction(this, mGlobalPreferences).execute(mRouter);
                    break;
                case UPGRADE_FIRMWARE:
                    //TODO
                    break;
                default:
                    //Ignored
                    break;
                }
            } catch (IllegalArgumentException | NullPointerException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void onClear(@NonNull Parcelable[] parcelables) {
        //Nothing to do
    }

    @Override
    public void onRouterActionSuccess(@NotNull RouterAction routerAction, @NotNull Router router,
            Object returnData) {
        Utils.displayMessage(this, String.format("Action '%s' executed successfully on host '%s'",
                routerAction.toString().toLowerCase(), router.getRemoteIpAddress()), Style.CONFIRM);
    }

    @Override
    public void onRouterActionFailure(@NotNull RouterAction routerAction, @NotNull Router router,
            @Nullable Exception exception) {
        Utils.displayMessage(this, String.format("Error on action '%s': %s", routerAction.toString().toLowerCase(),
                ExceptionUtils.getRootCauseMessage(exception)), Style.ALERT);
    }

    public void startActivityForResult(Intent intent, DDWRTTile.ActivityResultListener listener) {
        Log.d(TAG, "startActivityForResult(" + intent + "," + listener + ") / mCurrentActivityResultListener="
                + mCurrentActivityResultListener);

        if (mCurrentActivityResultListener != null) {
            Log.e(TAG, "Activity trying to start more than one activity at a time...");
            return;
        }
        mCurrentActivityResultListener = listener;
        startActivityForResult(intent, LISTENED_REQUEST_CODE);
    }

    // The click listener for ListView in the navigation drawer
    private class DrawerItemClickListener implements ListView.OnItemClickListener {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            selectItem(position);
        }
    }

}