io.github.hidroh.materialistic.BaseListActivity.java Source code

Java tutorial

Introduction

Here is the source code for io.github.hidroh.materialistic.BaseListActivity.java

Source

/*
 * Copyright (c) 2015 Ha Duy Trung
 *
 * 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 io.github.hidroh.materialistic;

import android.app.SearchManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

import javax.inject.Inject;

import io.github.hidroh.materialistic.annotation.Synthetic;
import io.github.hidroh.materialistic.data.ItemManager;
import io.github.hidroh.materialistic.data.SessionManager;
import io.github.hidroh.materialistic.data.WebItem;
import io.github.hidroh.materialistic.widget.ItemPagerAdapter;
import io.github.hidroh.materialistic.widget.NavFloatingActionButton;
import io.github.hidroh.materialistic.widget.PopupMenu;
import io.github.hidroh.materialistic.widget.ViewPager;

/**
 * List activity that renders alternative layouts for portrait/landscape
 */
public abstract class BaseListActivity extends DrawerActivity implements MultiPaneListener {

    protected static final String LIST_FRAGMENT_TAG = BaseListActivity.class.getName() + ".LIST_FRAGMENT_TAG";
    private static final String STATE_SELECTED_ITEM = "state:selectedItem";
    private static final String STATE_STORY_VIEW_MODE = "state:storyViewMode";
    private static final String STATE_EXTERNAL_BROWSER = "state:externalBrowser";
    private static final String STATE_FULLSCREEN = "state:fullscreen";
    private static final String STATE_MULTI_WINDOW_ENABLED = "state:multiWindowEnabled";
    private boolean mIsMultiPane;
    protected WebItem mSelectedItem;
    private Preferences.StoryViewMode mStoryViewMode;
    private boolean mExternalBrowser;
    private ViewPager mViewPager;
    @Inject
    ActionViewResolver mActionViewResolver;
    @Inject
    PopupMenu mPopupMenu;
    @Inject
    SessionManager mSessionManager;
    @Inject
    CustomTabsDelegate mCustomTabsDelegate;
    @Inject
    KeyDelegate mKeyDelegate;
    private AppBarLayout mAppBar;
    private TabLayout mTabLayout;
    private FloatingActionButton mReplyButton;
    private NavFloatingActionButton mNavButton;
    private View mListView;
    @Synthetic
    boolean mFullscreen;
    private boolean mMultiWindowEnabled;
    private final Preferences.Observable mPreferenceObservable = new Preferences.Observable();
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            mFullscreen = intent.getBooleanExtra(BaseWebFragment.EXTRA_FULLSCREEN, false);
            setFullscreen();
        }
    };
    private ItemPagerAdapter mAdapter;

    @SuppressWarnings("ConstantConditions")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list);
        setTitle(getDefaultTitle());
        setSupportActionBar((Toolbar) findViewById(R.id.toolbar));
        getSupportActionBar().setDisplayOptions(
                ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE);
        findViewById(R.id.toolbar).setOnClickListener(v -> {
            Scrollable scrollable = getScrollableList();
            if (scrollable != null) {
                scrollable.scrollToTop();
            }
        });
        mAppBar = (AppBarLayout) findViewById(R.id.appbar);
        mIsMultiPane = getResources().getBoolean(R.bool.multi_pane);
        if (mIsMultiPane) {
            LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver,
                    new IntentFilter(BaseWebFragment.ACTION_FULLSCREEN));
            mListView = findViewById(android.R.id.list);
            mTabLayout = (TabLayout) findViewById(R.id.tab_layout);
            mTabLayout.setVisibility(View.GONE);
            mViewPager = (ViewPager) findViewById(R.id.content);
            mViewPager.setVisibility(View.GONE);
            mReplyButton = (FloatingActionButton) findViewById(R.id.reply_button);
            mNavButton = (NavFloatingActionButton) findViewById(R.id.navigation_button);
            mNavButton.setNavigable(direction ->
            // if callback is fired navigable should not be null
            ((Navigable) ((ItemPagerAdapter) mViewPager.getAdapter()).getItem(0)).onNavigate(direction));
            AppUtils.toggleFab(mNavButton, false);
            AppUtils.toggleFab(mReplyButton, false);
        }
        if (savedInstanceState == null) {
            mMultiWindowEnabled = Preferences.multiWindowEnabled(this);
            mStoryViewMode = Preferences.getDefaultStoryView(this);
            mExternalBrowser = Preferences.externalBrowserEnabled(this);
            getSupportFragmentManager().beginTransaction()
                    .replace(android.R.id.list, instantiateListFragment(), LIST_FRAGMENT_TAG).commit();
        } else {
            mMultiWindowEnabled = savedInstanceState.getBoolean(STATE_MULTI_WINDOW_ENABLED);
            mStoryViewMode = Preferences.StoryViewMode.values()[savedInstanceState.getInt(STATE_STORY_VIEW_MODE,
                    0)];
            mExternalBrowser = savedInstanceState.getBoolean(STATE_EXTERNAL_BROWSER);
            mSelectedItem = savedInstanceState.getParcelable(STATE_SELECTED_ITEM);
            mFullscreen = savedInstanceState.getBoolean(STATE_FULLSCREEN);
            if (mIsMultiPane) {
                openMultiPaneItem();
            } else {
                unbindViewPager();
            }
        }
        mPreferenceObservable.subscribe(this, this::onPreferenceChanged, R.string.pref_navigation,
                R.string.pref_external, R.string.pref_story_display, R.string.pref_multi_window);
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        if (!Preferences.isReleaseNotesSeen(this)) {
            startActivity(new Intent(this, ReleaseNotesActivity.class));
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        mCustomTabsDelegate.bindCustomTabsService(this);
        mKeyDelegate.attach(this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        if (mIsMultiPane) {
            getMenuInflater().inflate(R.menu.menu_item, menu);
        }
        if (isSearchable()) {
            getMenuInflater().inflate(R.menu.menu_search, menu);
            MenuItem menuSearch = menu.findItem(R.id.menu_search);
            SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
            SearchView searchView = (SearchView) mActionViewResolver.getActionView(menuSearch);
            searchView.setSearchableInfo(
                    searchManager.getSearchableInfo(new ComponentName(this, SearchActivity.class)));
            searchView.setIconified(true);
            searchView.setQuery("", false);
        }
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        if (mIsMultiPane) {
            menu.findItem(R.id.menu_share).setVisible(mSelectedItem != null);
            menu.findItem(R.id.menu_external).setVisible(mSelectedItem != null);
        }
        return isSearchable() || super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.menu_share) {
            View anchor = findViewById(R.id.menu_share);
            AppUtils.share(this, mPopupMenu, anchor == null ? findViewById(R.id.toolbar) : anchor, mSelectedItem);
            return true;
        }
        if (item.getItemId() == R.id.menu_external) {
            View anchor = findViewById(R.id.menu_external);
            AppUtils.openExternal(this, mPopupMenu, anchor == null ? findViewById(R.id.toolbar) : anchor,
                    mSelectedItem, mCustomTabsDelegate.getSession());
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelable(STATE_SELECTED_ITEM, mSelectedItem);
        outState.putInt(STATE_STORY_VIEW_MODE, mStoryViewMode.ordinal());
        outState.putBoolean(STATE_EXTERNAL_BROWSER, mExternalBrowser);
        outState.putBoolean(STATE_FULLSCREEN, mFullscreen);
        outState.putBoolean(STATE_MULTI_WINDOW_ENABLED, mMultiWindowEnabled);
    }

    @Override
    protected void onStop() {
        super.onStop();
        mCustomTabsDelegate.unbindCustomTabsService(this);
        mKeyDelegate.detach(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mPreferenceObservable.unsubscribe(this);
        if (mIsMultiPane) {
            LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
        }
    }

    @Override
    public void onBackPressed() {
        if (!mIsMultiPane || !mFullscreen) {
            super.onBackPressed();
        } else {
            LocalBroadcastManager.getInstance(this).sendBroadcast(
                    new Intent(WebFragment.ACTION_FULLSCREEN).putExtra(WebFragment.EXTRA_FULLSCREEN, false));
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        mKeyDelegate.setScrollable(getScrollableList(), mAppBar);
        mKeyDelegate.setBackInterceptor(getBackInterceptor());
        return mKeyDelegate.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
    }

    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        return mKeyDelegate.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event);
    }

    @Override
    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
        return mKeyDelegate.onKeyLongPress(keyCode, event) || super.onKeyLongPress(keyCode, event);
    }

    @NonNull
    @Override
    public ActionBar getSupportActionBar() {
        //noinspection ConstantConditions
        return super.getSupportActionBar();
    }

    @Override
    public void onItemSelected(@Nullable WebItem item) {
        WebItem previousItem = mSelectedItem;
        mSelectedItem = item;
        if (mIsMultiPane) {
            if (previousItem != null && item != null && TextUtils.equals(item.getId(), previousItem.getId())) {
                return;
            }
            if (previousItem == null && item != null || previousItem != null && item == null) {
                supportInvalidateOptionsMenu();
            }
            openMultiPaneItem();
        } else if (item != null) {
            openSinglePaneItem();
        }
    }

    @Override
    public WebItem getSelectedItem() {
        return mSelectedItem;
    }

    @Override
    public boolean isMultiPane() {
        return mIsMultiPane;
    }

    /**
     * Checks if activity should have search view
     * @return true if is searchable, false otherwise
     */
    protected boolean isSearchable() {
        return true;
    }

    /**
     * Gets default title to be displayed in list-only layout
     * @return displayed title
     */
    protected abstract String getDefaultTitle();

    /**
     * Creates list fragment to host list data
     * @return list fragment
     */
    protected abstract Fragment instantiateListFragment();

    /**
     * Gets cache mode for {@link ItemManager}
     * @return  cache mode
     */
    @ItemManager.CacheMode
    protected int getItemCacheMode() {
        return ItemManager.MODE_DEFAULT;
    }

    @Synthetic
    void setFullscreen() {
        mAppBar.setExpanded(!mFullscreen, true);
        mTabLayout.setVisibility(mFullscreen ? View.GONE : View.VISIBLE);
        mListView.setVisibility(mFullscreen ? View.GONE : View.VISIBLE);
        mKeyDelegate.setAppBarEnabled(!mFullscreen);
        mViewPager.setSwipeEnabled(!mFullscreen);
        AppUtils.toggleFab(mReplyButton, !mFullscreen);
    }

    private Scrollable getScrollableList() {
        // TODO landscape behavior?
        return (Scrollable) getSupportFragmentManager().findFragmentByTag(LIST_FRAGMENT_TAG);
    }

    private KeyDelegate.BackInterceptor getBackInterceptor() {
        if (mViewPager == null || mViewPager.getAdapter() == null || mViewPager.getCurrentItem() < 0) {
            return null;
        }
        Fragment item = ((ItemPagerAdapter) mViewPager.getAdapter()).getItem(mViewPager.getCurrentItem());
        if (item instanceof KeyDelegate.BackInterceptor) {
            return (KeyDelegate.BackInterceptor) item;
        } else {
            return null;
        }
    }

    private void openSinglePaneItem() {
        if (mExternalBrowser) {
            AppUtils.openWebUrlExternal(this, mSelectedItem, mSelectedItem.getUrl(),
                    mCustomTabsDelegate.getSession());
        } else {
            Intent intent = new Intent(this, ItemActivity.class)
                    .putExtra(ItemActivity.EXTRA_CACHE_MODE, getItemCacheMode())
                    .putExtra(ItemActivity.EXTRA_ITEM, mSelectedItem);
            startActivity(mMultiWindowEnabled ? AppUtils.multiWindowIntent(this, intent) : intent);
        }
    }

    private void openMultiPaneItem() {
        if (mSelectedItem == null) {
            setTitle(getDefaultTitle());
            findViewById(R.id.empty_selection).setVisibility(View.VISIBLE);
            mTabLayout.setVisibility(View.GONE);
            mViewPager.setVisibility(View.GONE);
            mViewPager.setAdapter(null);
            AppUtils.toggleFab(mNavButton, false);
            AppUtils.toggleFab(mReplyButton, false);
        } else {
            setTitle(mSelectedItem.getDisplayedTitle());
            findViewById(R.id.empty_selection).setVisibility(View.GONE);
            mTabLayout.setVisibility(View.VISIBLE);
            mViewPager.setVisibility(View.VISIBLE);
            bindViewPager();
            mSessionManager.view(this, mSelectedItem.getId());
        }
    }

    private void bindViewPager() {
        if (mAdapter != null) {
            mAdapter.unbind(mTabLayout);
        }
        mAdapter = new ItemPagerAdapter(this, getSupportFragmentManager(),
                new ItemPagerAdapter.Builder().setItem(mSelectedItem).setShowArticle(true)
                        .setCacheMode(getItemCacheMode()).setDefaultViewMode(mStoryViewMode));
        mAdapter.bind(mViewPager, mTabLayout, mNavButton, mReplyButton);
        if (mFullscreen) {
            setFullscreen();
        }
    }

    private void unbindViewPager() {
        // fragment manager always restores view pager fragments,
        // even when view pager no longer exists (e.g. after rotation),
        // so we have to explicitly remove those with view pager ID
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        //noinspection Convert2streamapi
        for (Fragment fragment : getSupportFragmentManager().getFragments()) {
            if (fragment != null && fragment.getId() == R.id.content) {
                transaction.remove(fragment);
            }
        }
        transaction.commit();
    }

    private void onPreferenceChanged(int key, boolean contextChanged) {
        switch (key) {
        case R.string.pref_external:
            mExternalBrowser = Preferences.externalBrowserEnabled(this);
            break;
        case R.string.pref_story_display:
            mStoryViewMode = Preferences.getDefaultStoryView(this);
            break;
        case R.string.pref_navigation:
            if (mNavButton != null) {
                AppUtils.toggleFab(mNavButton,
                        mViewPager.getCurrentItem() == 0 && Preferences.navigationEnabled(this));
            }
            break;
        case R.string.pref_multi_window:
            mMultiWindowEnabled = Preferences.multiWindowEnabled(this);
            break;
        }
    }
}