com.ruesga.rview.BaseActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.ruesga.rview.BaseActivity.java

Source

/*
 * Copyright (C) 2016 Jorge Ruesga
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ruesga.rview;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.Keep;
import android.support.annotation.MenuRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.view.GravityCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
import android.support.v4.widget.SlidingPaneLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.RecyclerView;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import com.ruesga.rview.databinding.ContentBinding;
import com.ruesga.rview.drawer.DrawerNavigationView;
import com.ruesga.rview.drawer.DrawerNavigationView.OnDrawerNavigationItemSelectedListener;
import com.ruesga.rview.fragments.PageableFragment.PageFragmentAdapter;
import com.ruesga.rview.fragments.SelectableFragment;
import com.ruesga.rview.misc.ActivityHelper;
import com.ruesga.rview.misc.AndroidHelper;
import com.ruesga.rview.misc.ExceptionHelper;
import com.ruesga.rview.misc.ModelHelper;
import com.ruesga.rview.model.Account;
import com.ruesga.rview.model.EmptyState;
import com.ruesga.rview.preferences.Preferences;
import com.ruesga.rview.widget.PagerControllerLayout;
import com.ruesga.rview.widget.PagerControllerLayout.OnPageSelectionListener;
import com.ruesga.rview.widget.PagerControllerLayout.PagerControllerAdapter;
import com.ruesga.rview.widget.ScrollAwareFloatingActionButtonBehavior;
import com.ruesga.rview.wizards.AuthorizationAccountSetupActivity;

import java.util.List;

import javax.net.ssl.SSLException;

public abstract class BaseActivity extends AppCompatDelegateActivity implements OnRefreshListener {

    public static final String FRAGMENT_TAG_LIST = "list";
    public static final String FRAGMENT_TAG_DETAILS = "details";

    public interface OnFabPressedListener {
        void onFabPressed(FloatingActionButton fab);
    }

    @Keep
    @SuppressWarnings("unused")
    public static class EventHandlers {
        BaseActivity mActivity;

        EventHandlers(BaseActivity activity) {
            mActivity = activity;
        }

        public void onFabPressed(View view) {
            if (mActivity.mOnFabPressedListener != null) {
                mActivity.mOnFabPressedListener.onFabPressed((FloatingActionButton) view);
            }
        }
    }

    @Keep
    public static class Model implements Parcelable {
        public boolean isInProgress = false;
        public boolean hasTabs = false;
        public boolean hasPages = false;
        public boolean useTowPane = true;
        public boolean hasMiniDrawer = true;
        public boolean hasForceSinglePanel = false;
        public boolean hasFab = false;

        public Model() {
        }

        protected Model(Parcel in) {
            isInProgress = in.readByte() != 0;
            hasTabs = in.readByte() != 0;
            hasPages = in.readByte() != 0;
            useTowPane = in.readByte() != 0;
            hasMiniDrawer = in.readByte() != 0;
            hasForceSinglePanel = in.readByte() != 0;
            hasFab = in.readByte() != 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeByte((byte) (isInProgress ? 1 : 0));
            dest.writeByte((byte) (hasTabs ? 1 : 0));
            dest.writeByte((byte) (hasPages ? 1 : 0));
            dest.writeByte((byte) (useTowPane ? 1 : 0));
            dest.writeByte((byte) (hasMiniDrawer ? 1 : 0));
            dest.writeByte((byte) (hasForceSinglePanel ? 1 : 0));
            dest.writeByte((byte) (hasFab ? 1 : 0));
        }

        @Override
        public int describeContents() {
            return 0;
        }

        public static final Creator<Model> CREATOR = new Creator<Model>() {
            @Override
            public Model createFromParcel(Parcel in) {
                return new Model(in);
            }

            @Override
            public Model[] newArray(int size) {
                return new Model[size];
            }
        };
    }

    private Handler mUiHandler;

    private Model mModel = new Model();
    private final EventHandlers mHandlers = new EventHandlers(this);
    private ViewPager mViewPager;
    private boolean mHasStateSaved;
    private OnFabPressedListener mOnFabPressedListener;

    private AlertDialog mDialog;

    private SlidingPaneLayout mMiniDrawerLayout;

    public abstract DrawerLayout getDrawerLayout();

    public abstract ContentBinding getContentBinding();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        AndroidHelper.configureTaskDescription(this);
        mUiHandler = new Handler();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        if (mDialog != null) {
            mDialog.dismiss();
            mDialog = null;
        }
    }

    protected void setupActivity() {
        mMiniDrawerLayout = getContentBinding().getRoot().findViewById(R.id.mini_drawer_layout);
        if (mMiniDrawerLayout != null) {
            // Disable drawer elevation to match AppBarLayout
            View v = getContentBinding().getRoot().findViewById(R.id.drawer_navigation_view);
            ViewCompat.setElevation(v, 0);
        }

        setSupportActionBar(getContentBinding().toolbar);
        if (getSupportActionBar() != null) {
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            getSupportActionBar().setHomeButtonEnabled(true);

            setupDrawer();
            configureOptionsDrawer();
        }

        getContentBinding().setHandlers(mHandlers);
    }

    private void setupDrawer() {
        if (mMiniDrawerLayout != null && mModel.useTowPane) {
            configureMiniDrawer();
        } else {
            configureFullDrawer();
        }
    }

    public void setupFab(OnFabPressedListener cb) {
        final Account account = Preferences.getAccount(this);
        mOnFabPressedListener = cb;
        mModel.hasFab = account != null && account.hasAuthenticatedAccessMode() && mOnFabPressedListener != null;
        if (!mModel.hasFab) {
            getContentBinding().fab.hide();
        } else {
            getContentBinding().fab.show();
        }
        getContentBinding().setModel(mModel);
    }

    public void registerFabWithRecyclerView(RecyclerView view) {
        final Account mAccount = Preferences.getAccount(BaseActivity.this);
        if (mAccount != null && mAccount.hasAuthenticatedAccessMode()) {
            view.addOnScrollListener(new RecyclerView.OnScrollListener() {
                final ScrollAwareFloatingActionButtonBehavior mBehavior = new ScrollAwareFloatingActionButtonBehavior(
                        BaseActivity.this, null);

                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    if (mOnFabPressedListener != null) {
                        //noinspection ConstantConditions
                        mBehavior.onNestedScroll(null, getContentBinding().fab, view, dx, dy, 0, 0, -1);
                    }
                }
            });
        }
    }

    private void configureMiniDrawer() {
        if (getDrawerLayout() != null) {
            mModel.hasMiniDrawer = true;

            ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(this, getDrawerLayout(),
                    getContentBinding().toolbar, 0, 0);
            getDrawerLayout().addDrawerListener(drawerToggle);
            getContentBinding().drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED,
                    GravityCompat.START);
            drawerToggle.syncState();
            getContentBinding().toolbar.setNavigationOnClickListener(view -> {
                if (mMiniDrawerLayout.isOpen()) {
                    mMiniDrawerLayout.closePane();
                } else {
                    mMiniDrawerLayout.openPane();
                }
            });
            mMiniDrawerLayout.closePane();
            getContentBinding().drawerNavigationView.configureWithMiniDrawer(mMiniDrawerLayout);

        } else {
            // Don't use mini drawer
            mModel.hasMiniDrawer = false;
        }
        getContentBinding().setModel(mModel);
    }

    SlidingPaneLayout getMiniDrawerLayout() {
        return mMiniDrawerLayout;
    }

    private void configureFullDrawer() {
        if (getDrawerLayout() != null) {
            ActionBarDrawerToggle drawerToggle = new ActionBarDrawerToggle(this, getDrawerLayout(),
                    getContentBinding().toolbar, 0, 0);
            getDrawerLayout().addDrawerListener(drawerToggle);
            getDrawerLayout().setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.START);
            drawerToggle.syncState();
        } else {
            final ViewGroup.LayoutParams params = getContentBinding().drawerLayout.getLayoutParams();
            if (!(params instanceof SlidingPaneLayout.LayoutParams)) {
                getContentBinding().drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED,
                        Gravity.START);
            }
            if (mModel.hasForceSinglePanel) {
                // Someones is requesting a single panel in a multipanel layout
                // Just hide the multipanel
                mModel.hasMiniDrawer = false;
            }
        }
    }

    private void configureOptionsDrawer() {
        // Options is open/closed programmatically
        getContentBinding().drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.END);

        // Listen for options close
        getContentBinding().drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
            @Override
            public void onDrawerSlide(View drawerView, float slideOffset) {

            }

            @Override
            public void onDrawerOpened(View drawerView) {

            }

            @Override
            public void onDrawerClosed(View drawerView) {
                if (getContentBinding().drawerOptionsView == drawerView) {
                    getContentBinding().drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED,
                            GravityCompat.END);
                }
            }

            @Override
            public void onDrawerStateChanged(int newState) {

            }
        });
    }

    public boolean hasStateSaved() {
        return mHasStateSaved;
    }

    public void setUseTwoPanel(boolean useTwoPanel) {
        mModel.useTowPane = useTwoPanel;
        getContentBinding().setModel(mModel);
    }

    public void setForceSinglePanel(boolean singlePanel) {
        mModel.hasForceSinglePanel = singlePanel;
        getContentBinding().setModel(mModel);
    }

    public void invalidateTabs() {
        mModel.hasPages = false;
        mModel.hasTabs = false;
        getContentBinding().tabs.setupWithViewPager(null);
        getContentBinding().setModel(mModel);
    }

    public void configureTabs(ViewPager viewPager, boolean fixedMode) {
        mViewPager = viewPager;
        mModel.hasPages = false;
        mModel.hasTabs = true;
        getContentBinding().tabs.setTabMode(fixedMode ? TabLayout.MODE_FIXED : TabLayout.MODE_SCROLLABLE);
        getContentBinding().tabs.setupWithViewPager(viewPager);
        getContentBinding().tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                PageFragmentAdapter adapter = (PageFragmentAdapter) mViewPager.getAdapter();
                Fragment fragment = adapter.getCachedFragment(tab.getPosition());
                if (fragment != null && fragment instanceof SelectableFragment) {
                    mUiHandler.post(((SelectableFragment) fragment)::onFragmentSelected);
                }

                // Show fab if necessary
                showFab();
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {
            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {
            }
        });
        viewPager.getAdapter().notifyDataSetChanged();
        getContentBinding().setModel(mModel);
    }

    public void invalidatePages() {
        mModel.hasPages = false;
        mModel.hasTabs = false;
        getContentBinding().pagerController.listenOn(null).with(null);
        getContentBinding().setModel(mModel);
    }

    private void showFab() {
        final Account account = Preferences.getAccount(this);
        if (account != null && account.hasAuthenticatedAccessMode() && mOnFabPressedListener != null) {
            getContentBinding().fab.show();
        }
    }

    public void configurePages(PagerControllerAdapter adapter, OnPageSelectionListener cb) {
        mViewPager = null;
        mModel.hasPages = true;
        mModel.hasTabs = false;
        getContentBinding().pagerController.listenOn((position, fromUser) -> {
            if (getSupportActionBar() != null) {
                getSupportActionBar().setSubtitle(
                        position == PagerControllerLayout.INVALID_PAGE ? null : adapter.getPageTitle(position));

                if (cb != null) {
                    cb.onPageSelected(position, fromUser);
                }
            }
        }).with(adapter);
        getContentBinding().setModel(mModel);
    }

    public void configureOptionsMenu(@MenuRes int menu, OnDrawerNavigationItemSelectedListener cb) {
        if (menu != 0) {
            getOptionsMenu().inflateMenu(menu);
            getOptionsMenu().setDrawerNavigationItemSelectedListener(cb);
        }
    }

    public void configureOptionsTitle(String title) {
        TextView tv = getContentBinding().drawerOptionsView.getHeaderView(0).findViewById(R.id.options_title);
        tv.setText(title);
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
            switch (event.getKeyCode()) {
            case KeyEvent.KEYCODE_MENU:
                if (getContentBinding().drawerLayout != null && getContentBinding().drawerLayout
                        .getDrawerLockMode(GravityCompat.START) != DrawerLayout.LOCK_MODE_LOCKED_CLOSED) {
                    if (getContentBinding().drawerLayout.isDrawerOpen(GravityCompat.START)) {
                        getContentBinding().drawerLayout.closeDrawer(GravityCompat.START);
                    } else {
                        getContentBinding().drawerLayout.openDrawer(GravityCompat.START);
                    }
                    return true;
                } else if (getOptionsMenu() != null && getOptionsMenu().getMenu() != null
                        && getOptionsMenu().getMenu().size() > 0) {
                    if (getContentBinding().drawerLayout.isDrawerOpen(GravityCompat.END)) {
                        closeOptionsDrawer();
                    } else {
                        openOptionsDrawer();
                    }
                    return true;
                }
                break;
            }
        }
        return super.dispatchKeyEvent(event);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        switch (keyCode) {
        case KeyEvent.KEYCODE_MENU:
            if (getContentBinding().drawerLayout != null && getContentBinding().drawerLayout
                    .getDrawerLockMode(GravityCompat.START) != DrawerLayout.LOCK_MODE_LOCKED_CLOSED) {
                if (getContentBinding().drawerLayout.isDrawerOpen(GravityCompat.START)) {
                    getContentBinding().drawerLayout.closeDrawer(GravityCompat.START);
                } else {
                    getContentBinding().drawerLayout.openDrawer(GravityCompat.START);
                }
                return true;
            } else if (getOptionsMenu() != null && getOptionsMenu().getMenu() != null
                    && getOptionsMenu().getMenu().size() > 0) {
                if (getContentBinding().drawerLayout.isDrawerOpen(GravityCompat.END)) {
                    closeOptionsDrawer();
                } else {
                    openOptionsDrawer();
                }
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public void onResume() {
        super.onResume();
        mHasStateSaved = false;
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedState) {
        if (savedState != null) {
            super.onRestoreInstanceState(savedState);
            mModel = savedState.getParcelable(getClass().getSimpleName() + "_base_activity_model");
        }
        mHasStateSaved = false;

        // We need to restore drawer layout lock state
        if (getContentBinding() != null) {
            setupDrawer();
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelable(getClass().getSimpleName() + "_base_activity_model", mModel);
        mHasStateSaved = true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case android.R.id.home:
            return ActivityHelper.performFinishActivity(this, false);
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    @Override
    public void onBackPressed() {
        if (getContentBinding() != null && getContentBinding().drawerLayout != null) {
            final DrawerLayout drawer = getContentBinding().drawerLayout;
            final DrawerNavigationView optionsView = getContentBinding().drawerOptionsView;
            final DrawerNavigationView navigationView = getContentBinding().drawerNavigationView;
            if (optionsView != null && drawer.isDrawerOpen(optionsView)) {
                closeOptionsDrawer();
                return;
            }
            if (mMiniDrawerLayout == null && navigationView != null && drawer.isDrawerOpen(navigationView)) {
                drawer.closeDrawer(navigationView);
                return;
            } else if (mMiniDrawerLayout != null && mMiniDrawerLayout.isOpen()) {
                mMiniDrawerLayout.closePane();
                return;
            }
        }
        if (!ActivityHelper.performFinishActivity(this, false)) {
            super.onBackPressed();
        }
    }

    public void showError(@StringRes int message) {
        AndroidHelper.showErrorSnackbar(this, getSnackBarTarget(getContentBinding().getRoot()), message);
    }

    public void showWarning(@StringRes int message) {
        AndroidHelper.showWarningSnackbar(this, getSnackBarTarget(getContentBinding().getRoot()), message);
    }

    private View getSnackBarTarget(View root) {
        View v = root.findViewById(R.id.page_content_layout);
        if (v != null && v instanceof CoordinatorLayout) {
            return v;
        }
        return root;
    }

    public void showToast(@StringRes int message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }

    public void handleException(String tag, Throwable cause) {
        handleException(tag, cause, null);
    }

    public void handleException(String tag, Throwable cause, final EmptyState.EventHandlers emptyHandler) {
        int level = ExceptionHelper.exceptionToLevel(cause);
        int res = ExceptionHelper.exceptionToMessage(this, tag, cause);
        if (level == 0) {
            showError(res);
        } else {
            showWarning(res);
        }

        // Handle authentication denied
        if (ExceptionHelper.isAuthenticationException(cause)) {
            if (!ExceptionHelper.hasPreviousAuthenticationFailure(this)) {
                //  Handle Unauthorized exception
                ExceptionHelper.markAsAuthenticationFailure(this);
                Intent i = new Intent(BaseActivity.this, AuthorizationAccountSetupActivity.class);
                i.putExtra(ExceptionHelper.EXTRA_AUTHENTICATION_FAILURE, true);
                startActivity(i);
                return;
            }
        }

        // Handle SSL exceptions, so we can ask the user for temporary trust the server connection
        final Account account = Preferences.getAccount(this);
        if (account != null && emptyHandler != null && ExceptionHelper.isException(cause, SSLException.class)
                && (account.mRepository == null || !account.mRepository.mTrustAllCertificates)
                && !ModelHelper.hasTemporaryTrustAllCertificatesAccessRequested(account)) {
            // Ask the user for temporary access
            mDialog = new AlertDialog.Builder(this).setTitle(R.string.untrusted_connection_title)
                    .setMessage(R.string.untrusted_connection_message).setIcon(R.drawable.ic_warning)
                    .setPositiveButton(R.string.untrusted_connection_action_trust, (dialogInterface, i) -> {
                        ModelHelper.setTemporaryTrustAllCertificatesAccessGrant(account, true);
                        emptyHandler.onRetry(null);
                    })
                    .setNegativeButton(R.string.untrusted_connection_action_no_trust,
                            (dialogInterface, i) -> ModelHelper.setTemporaryTrustAllCertificatesAccessGrant(account,
                                    false))
                    .setOnCancelListener(dialogInterface -> ModelHelper
                            .setTemporaryTrustAllCertificatesAccessGrant(account, false))
                    .setOnDismissListener(dialogInterface -> mDialog = null).create();
            mDialog.show();
        }
    }

    private void changeInProgressStatus(boolean status) {
        mModel.isInProgress = status;
        getContentBinding().setModel(mModel);
    }

    @Override
    public void onRefreshStart(Fragment from) {
        changeInProgressStatus(true);
    }

    @Override
    public <T> void onRefreshEnd(Fragment from, T result) {
        changeInProgressStatus(false);
    }

    public void openOptionsDrawer() {
        if (!getContentBinding().drawerLayout.isDrawerOpen(getContentBinding().drawerOptionsView)) {
            getContentBinding().drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END);
            getContentBinding().drawerLayout.openDrawer(getContentBinding().drawerOptionsView);
        }
    }

    public void closeOptionsDrawer() {
        if (getContentBinding().drawerLayout.isDrawerOpen(getContentBinding().drawerOptionsView)) {
            getContentBinding().drawerLayout.closeDrawer(getContentBinding().drawerOptionsView);
        }
        getContentBinding().drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.END);
    }

    public DrawerNavigationView getOptionsMenu() {
        return getContentBinding().drawerOptionsView;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
            @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        List<Fragment> fragments = getSupportFragmentManager().getFragments();
        if (fragments != null) {
            for (Fragment fragment : fragments) {
                fragment.onRequestPermissionsResult(requestCode, permissions, grantResults);
            }
        }
    }
}