com.antew.redditinpictures.library.ui.base.BaseFragmentActivityWithMenu.java Source code

Java tutorial

Introduction

Here is the source code for com.antew.redditinpictures.library.ui.base.BaseFragmentActivityWithMenu.java

Source

/*
 * Copyright (C) 2014 Antew
 *
 * 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.antew.redditinpictures.library.ui.base;

import android.app.LoaderManager;
import android.content.ContentResolver;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.Toast;
import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick;
import butterknife.OnLongClick;
import com.antew.redditinpictures.library.Constants;
import com.antew.redditinpictures.library.adapter.SubredditMenuDrawerCursorAdapter;
import com.antew.redditinpictures.library.annotation.ForApplication;
import com.antew.redditinpictures.library.database.RedditContract;
import com.antew.redditinpictures.library.dialog.AboutSubredditDialogFragment;
import com.antew.redditinpictures.library.dialog.AddSubredditDialogFragment;
import com.antew.redditinpictures.library.dialog.ExitDialogFragment;
import com.antew.redditinpictures.library.dialog.LoginDialogFragment;
import com.antew.redditinpictures.library.dialog.SetDefaultSubredditsDialogFragment;
import com.antew.redditinpictures.library.interfaces.RedditDataProvider;
import com.antew.redditinpictures.library.listener.OnSubredditActionListener;
import com.antew.redditinpictures.library.model.Age;
import com.antew.redditinpictures.library.model.Category;
import com.antew.redditinpictures.library.model.reddit.RedditLoginInformation;
import com.antew.redditinpictures.library.model.reddit.SubredditData;
import com.antew.redditinpictures.library.reddit.MySubredditsResponse;
import com.antew.redditinpictures.library.service.RedditService;
import com.antew.redditinpictures.library.util.Strings;
import com.antew.redditinpictures.library.util.SubredditUtil;
import com.antew.redditinpictures.pro.R;
import com.google.analytics.tracking.android.EasyTracker;
import com.google.analytics.tracking.android.MapBuilder;
import com.squareup.picasso.Picasso;
import java.util.Date;
import javax.inject.Inject;
import net.simonvt.menudrawer.MenuDrawer;
import net.simonvt.menudrawer.Position;

public abstract class BaseFragmentActivityWithMenu extends BaseFragmentActivity
        implements LoaderManager.LoaderCallbacks<Cursor>,
        SetDefaultSubredditsDialogFragment.SetDefaultSubredditsDialogListener,
        AddSubredditDialogFragment.AddSubredditDialogListener, RedditDataProvider,
        ExitDialogFragment.ExitDialogListener {
    protected MenuDrawer mMenuDrawer;
    protected SubredditMenuDrawerCursorAdapter mSubredditAdapter;
    @Inject
    @ForApplication
    protected InputMethodManager mInputMethodManager;
    @InjectView(R.id.et_subreddit_filter)
    protected EditText mSubredditFilter;
    @InjectView(R.id.ib_clear)
    protected ImageButton mClearSubredditFilter;
    @InjectView(R.id.lv_subreddits)
    protected ListView mSubredditList;
    @InjectView(R.id.ib_add)
    protected ImageButton mAddSubreddit;
    @InjectView(R.id.ib_sort)
    protected ImageButton mSortSubreddits;
    @InjectView(R.id.ib_refresh)
    protected ImageButton mRefreshSubreddits;
    protected String mSelectedSubreddit = Constants.Reddit.REDDIT_FRONTPAGE;
    protected Category mCategory = Category.HOT;
    protected Age mAge = Age.TODAY;
    protected String mSubredditSort = RedditContract.Subreddits.SORT_ALPHABETICALLY;
    private long mLastTimeBackPressed;
    private OnSubredditActionListener mSubredditActionListener = new OnSubredditActionListener() {

        @Override
        public void onAction(SubredditData subredditData, SubredditAction action) {
            // The keyboard doesn't always close, so let's make sure that we close it in all cases.
            hideKeyboard();

            switch (action) {
            case View:
                EasyTracker.getInstance(BaseFragmentActivityWithMenu.this)
                        .send(MapBuilder.createEvent(Constants.Analytics.Category.SUBREDDIT_MENU_ACTION,
                                Constants.Analytics.Action.OPEN_SUBREDDIT, subredditData.getDisplay_name(), null)
                                .build());
                mMenuDrawer.closeMenu(true);
                loadSubredditFromMenu(subredditData.getDisplay_name());
                break;
            case Subscribe:
                EasyTracker.getInstance(BaseFragmentActivityWithMenu.this)
                        .send(MapBuilder.createEvent(Constants.Analytics.Category.SUBREDDIT_MENU_ACTION,
                                Constants.Analytics.Action.SUBSCRIBE_TO_SUBREDDIT, subredditData.getDisplay_name(),
                                null).build());
                subscribeToSubreddit(subredditData.getName());
                break;
            case Unsubscribe:
                EasyTracker.getInstance(BaseFragmentActivityWithMenu.this)
                        .send(MapBuilder.createEvent(Constants.Analytics.Category.SUBREDDIT_MENU_ACTION,
                                Constants.Analytics.Action.UNSUBSCRIBE_FROM_SUBREDDIT,
                                subredditData.getDisplay_name(), null).build());
                unsubscribeToSubreddit(subredditData.getName());
                break;
            case Info:
                EasyTracker.getInstance(BaseFragmentActivityWithMenu.this)
                        .send(MapBuilder.createEvent(Constants.Analytics.Category.SUBREDDIT_MENU_ACTION,
                                Constants.Analytics.Action.DISPLAY_SUBREDDIT_INFO, subredditData.getDisplay_name(),
                                null).build());
                displaySubredditInfo(subredditData);
                break;
            case Delete:
                EasyTracker.getInstance(BaseFragmentActivityWithMenu.this)
                        .send(MapBuilder.createEvent(Constants.Analytics.Category.SUBREDDIT_MENU_ACTION,
                                Constants.Analytics.Action.DELETE_SUBREDDIT, subredditData.getDisplay_name(), null)
                                .build());
                deleteSubreddit(subredditData.getName());
                break;
            }
        }
    };
    private TextWatcher mSubredditFilterWatcher = new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            return;
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
            if (s == null) {
                return;
            }

            if (Strings.isEmpty(s) && mClearSubredditFilter.getVisibility() == View.VISIBLE) {
                mClearSubredditFilter.setVisibility(View.INVISIBLE);
            } else {
                mClearSubredditFilter.setVisibility(View.VISIBLE);
            }

            filterSubreddits(s.toString());
        }

        @Override
        public void afterTextChanged(Editable s) {
            return;
        }
    };
    private AdapterView.OnItemClickListener mSubredditClickListener = new AdapterView.OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            Cursor cursor = (Cursor) parent.getItemAtPosition(position);
            String subredditName = cursor
                    .getString(cursor.getColumnIndex(RedditContract.SubredditColumns.DISPLAY_NAME));
            int priority = cursor.getInt(cursor.getColumnIndex(RedditContract.SubredditColumns.PRIORITY));

            EasyTracker
                    .getInstance(BaseFragmentActivityWithMenu.this).send(
                            MapBuilder
                                    .createEvent(Constants.Analytics.Category.MENU_DRAWER_ACTION,
                                            Constants.Analytics.Action.OPEN_SUBREDDIT, subredditName, null)
                                    .build());

            // TODO: Make this less hacky...
            // Load the actual frontpage of reddit if selected
            if (priority == MySubredditsResponse.DefaultSubreddit.FRONTPAGE.getPriority()) {
                subredditName = Constants.Reddit.REDDIT_FRONTPAGE;
            }

            mMenuDrawer.setActiveView(view, position);
            mSubredditAdapter.setActivePosition(position);
            mMenuDrawer.closeMenu(true);
            // The keyboard doesn't always close, so let's make sure that we close it in all cases.
            hideKeyboard();
            loadSubredditFromMenu(subredditName);
        }
    };

    protected void hideKeyboard() {
        View view = getCurrentFocus();
        if (view != null) {
            mInputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
        }
        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
    }

    @Override
    public void onFinishExitDialog() {
        finish();
    }

    @OnClick(R.id.ib_add)
    protected void onAddSubreddit() {
        EasyTracker.getInstance(BaseFragmentActivityWithMenu.this)
                .send(MapBuilder.createEvent(Constants.Analytics.Category.MENU_DRAWER_ACTION,
                        Constants.Analytics.Action.SHOW_ADD_SUBREDDIT, null, null).build());
        AddSubredditDialogFragment.newInstance().show(getFragmentManager(), Constants.Dialog.DIALOG_ADD_SUBREDDIT);
    }

    @OnLongClick({ R.id.ib_add, R.id.ib_sort, R.id.ib_refresh, R.id.ib_clear })
    protected boolean onLongClickMenuOption(View view) {
        if (view != null) {
            String description = Strings.toString(view.getContentDescription());
            if (Strings.notEmpty(description)) {
                EasyTracker.getInstance(BaseFragmentActivityWithMenu.this)
                        .send(MapBuilder.createEvent(Constants.Analytics.Category.MENU_DRAWER_ACTION,
                                Constants.Analytics.Action.LONG_PRESS, description, null).build());
                Toast.makeText(this, description, Toast.LENGTH_SHORT).show();
                return true;
            }
        }

        return false;
    }

    @OnClick(R.id.ib_clear)
    protected void onClearSubredditFilter() {
        EasyTracker.getInstance(BaseFragmentActivityWithMenu.this)
                .send(MapBuilder
                        .createEvent(Constants.Analytics.Category.MENU_DRAWER_ACTION,
                                Constants.Analytics.Action.CLEAR, Constants.Analytics.Label.SUBREDDIT_FILTER, null)
                        .build());
        if (mSubredditFilter != null) {
            mSubredditFilter.setText(null);
        }
    }

    @OnClick(R.id.ib_sort)
    protected void onSortSubreddits() {
        if (mSubredditSort.equals(RedditContract.Subreddits.SORT_ALPHABETICALLY)) {
            EasyTracker.getInstance(BaseFragmentActivityWithMenu.this)
                    .send(MapBuilder.createEvent(Constants.Analytics.Category.MENU_DRAWER_ACTION,
                            Constants.Analytics.Action.SORT_SUBREDDITS, Constants.Analytics.Label.POPULARITY, null)
                            .build());
            mSubredditSort = RedditContract.Subreddits.SORT_BY_POPULARITY;
            Picasso.with(this).load(R.drawable.ic_action_sort_2_dark).into(mSortSubreddits);
            mSortSubreddits.setContentDescription(getString(R.string.sort_alphabetically));
        } else {
            EasyTracker.getInstance(BaseFragmentActivityWithMenu.this)
                    .send(MapBuilder.createEvent(Constants.Analytics.Category.MENU_DRAWER_ACTION,
                            Constants.Analytics.Action.SORT_SUBREDDITS, Constants.Analytics.Label.ALPHABETICALLY,
                            null).build());
            mSubredditSort = RedditContract.Subreddits.SORT_ALPHABETICALLY;
            Picasso.with(this).load(R.drawable.ic_action_sort_1_dark).into(mSortSubreddits);
            mSortSubreddits.setContentDescription(getString(R.string.sort_by_popularity));
        }

        getLoaderManager().restartLoader(Constants.Loader.LOADER_SUBREDDITS, null, this);
    }

    @OnClick(R.id.ib_refresh)
    protected void onRefreshSubreddits() {
        if (RedditLoginInformation.isLoggedIn()) {
            EasyTracker.getInstance(BaseFragmentActivityWithMenu.this)
                    .send(MapBuilder.createEvent(Constants.Analytics.Category.MENU_DRAWER_ACTION,
                            Constants.Analytics.Action.REFRESH_SUBREDDITS, Constants.Analytics.Label.LOGGED_IN,
                            null).build());
            // Since the user is logged in we can just run the task to update their subreddits.
            SubredditUtil.SetDefaultSubredditsTask defaultSubredditsTask = new SubredditUtil.SetDefaultSubredditsTask(
                    this, true);
            defaultSubredditsTask.execute();
        } else {
            EasyTracker.getInstance(BaseFragmentActivityWithMenu.this)
                    .send(MapBuilder.createEvent(Constants.Analytics.Category.MENU_DRAWER_ACTION,
                            Constants.Analytics.Action.SHOW_SET_DEFAULTS, null, null).build());
            // If they aren't logged in, we want to make sure that they understand this will set the subreddits back to default.
            SetDefaultSubredditsDialogFragment fragment = SetDefaultSubredditsDialogFragment.newInstance();
            fragment.show(getFragmentManager(), Constants.Dialog.DIALOG_DEFAULT_SUBREDDITS);
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case android.R.id.home:
            if (mMenuDrawer != null) {
                mMenuDrawer.toggleMenu();
            }
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    public void setMenuDrawerContentView(int layoutResId) {
        initializeMenuDrawer();
        mMenuDrawer.setContentView(layoutResId);
        ButterKnife.inject(this);
        setupMenuDrawer();
    }

    private void initializeMenuDrawer() {
        mMenuDrawer = MenuDrawer.attach(this, MenuDrawer.Type.OVERLAY, Position.LEFT, MenuDrawer.MENU_DRAG_WINDOW);
        mMenuDrawer.setMenuView(R.layout.subreddit_menudrawer);
        mMenuDrawer.getMenuContainer().setBackgroundColor(0xf51b1b1b);
        mMenuDrawer.setSlideDrawable(R.drawable.ic_drawer);
        mMenuDrawer.setDrawerIndicatorEnabled(true);
        mMenuDrawer.setMenuSize(getResources().getDimensionPixelSize(R.dimen.menu_size));
    }

    private void setupMenuDrawer() {
        initializeSubredditFilter();
        initializeSubredditList();
        initializeLoaders();
    }

    private void initializeSubredditFilter() {
        if (mSubredditFilter != null) {
            mSubredditFilter.addTextChangedListener(mSubredditFilterWatcher);
        }
    }

    private void initializeSubredditList() {
        mSubredditAdapter = getSubredditMenuAdapter();
        if (mSubredditList != null) {
            mSubredditList.setAdapter(mSubredditAdapter);
            mSubredditList.setOnItemClickListener(mSubredditClickListener);
        }
    }

    private void initializeLoaders() {
        getLoaderManager().initLoader(Constants.Loader.LOADER_SUBREDDITS, null, this);
    }

    private SubredditMenuDrawerCursorAdapter getSubredditMenuAdapter() {
        if (mSubredditAdapter == null) {
            mSubredditAdapter = new SubredditMenuDrawerCursorAdapter(this, mSubredditActionListener);
        }

        return mSubredditAdapter;
    }

    protected void displaySubredditInfo(SubredditData subredditData) {
        AboutSubredditDialogFragment fragment = AboutSubredditDialogFragment.newInstance(subredditData);
        fragment.show(getFragmentManager(), Constants.Dialog.DIALOG_ABOUT_SUBREDDIT);
    }

    protected void forceRefreshCurrentSubreddit() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                RedditService.getPosts(BaseFragmentActivityWithMenu.this, mSelectedSubreddit, mAge, mCategory);
            }
        }).start();
    }

    protected void filterSubreddits(String filterText) {
        LoaderManager loaderManager = getLoaderManager();
        Bundle filterBundle = new Bundle();
        filterBundle.putString(Constants.Extra.EXTRA_QUERY, filterText);
        loaderManager.restartLoader(Constants.Loader.LOADER_SUBREDDITS, filterBundle, this);
    }

    protected abstract void subscribeToSubreddit(String subredditName);

    protected void showLogin() {
        // Only needs to be shown if they aren't currently logged in.
        if (!RedditLoginInformation.isLoggedIn()) {
            LoginDialogFragment loginFragment = LoginDialogFragment.newInstance();
            loginFragment.show(getFragmentManager(), Constants.Dialog.DIALOG_LOGIN);
        }
    }

    protected abstract void unsubscribeToSubreddit(String subredditName);

    protected void deleteSubreddit(String subredditName) {
        // If the user isn't logged in we don't care about subscribing/unsubscribing
        if (!RedditLoginInformation.isLoggedIn()) {
            RedditService.unsubscribe(this, subredditName);
        }
        ContentResolver resolver = getContentResolver();
        resolver.delete(RedditContract.Subreddits.CONTENT_URI, RedditContract.SubredditColumns.NAME + " = ?",
                new String[] { subredditName });
    }

    /**
     * Take care of popping the fragment back stack or finishing the activity
     * as appropriate.
     */
    @Override
    public void onBackPressed() {
        long currentTime = new Date().getTime();

        // If the back button has been double clicked, finish the activity.
        if (currentTime - Constants.DOUBLE_CLICK_TIMEOUT <= mLastTimeBackPressed) {
            ExitDialogFragment.newInstance().show(getFragmentManager(), Constants.Dialog.DIALOG_LOGIN);
        } else {
            mLastTimeBackPressed = currentTime;
        }

        // If the menu drawer is open, close it. Otherwise go about the normal business.
        if (mMenuDrawer != null && mMenuDrawer.isMenuVisible()) {
            mMenuDrawer.closeMenu();
            return;
        } else if (!getFragmentManager().popBackStackImmediate()) {
            // Otherwise, let's pop the back stack. If that returns false then there is nothing left in the backstack so let's see if they want to exit.
            ExitDialogFragment.newInstance().show(getFragmentManager(), Constants.Dialog.DIALOG_LOGIN);
        }
    }

    /**
     * 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.
     */
    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        switch (id) {
        case Constants.Loader.LOADER_SUBREDDITS:
            String selection = null;
            String[] selectionArgs = null;

            if (args != null && args.containsKey(Constants.Extra.EXTRA_QUERY)) {
                String query = args.getString(Constants.Extra.EXTRA_QUERY);

                if (Strings.notEmpty(query)) {
                    selection = RedditContract.SubredditColumns.DISPLAY_NAME + " LIKE ?";
                    selectionArgs = new String[] { "%" + query + "%" };
                }
            }
            return new CursorLoader(this, RedditContract.Subreddits.CONTENT_URI,
                    RedditContract.Subreddits.SUBREDDITS_PROJECTION, selection, selectionArgs, mSubredditSort);
        }

        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<Cursor> loader, Cursor data) {
        switch (loader.getId()) {
        case Constants.Loader.LOADER_SUBREDDITS:
            mSubredditAdapter.swapCursor(data);
            break;
        }
    }

    /**
     * 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<Cursor> loader) {
        switch (loader.getId()) {
        case Constants.Loader.LOADER_SUBREDDITS:
            mSubredditAdapter.swapCursor(null);
            break;
        }
    }

    @Override
    public void onSetDefaultSubreddits() {
        EasyTracker.getInstance(BaseFragmentActivityWithMenu.this)
                .send(MapBuilder.createEvent(Constants.Analytics.Category.MENU_DRAWER_ACTION,
                        Constants.Analytics.Action.REFRESH_SUBREDDITS, Constants.Analytics.Label.NOT_LOGGED_IN,
                        null).build());
        SubredditUtil.SetDefaultSubredditsTask defaultSubredditsTask = new SubredditUtil.SetDefaultSubredditsTask(
                this, true);
        defaultSubredditsTask.execute();
    }

    @Override
    public void onAddSubreddit(String subreddit) {
        if (Strings.notEmpty(subreddit)) {
            EasyTracker.getInstance(BaseFragmentActivityWithMenu.this)
                    .send(MapBuilder.createEvent(Constants.Analytics.Category.MENU_DRAWER_ACTION,
                            Constants.Analytics.Action.ADD_SUBREDDIT, subreddit, null).build());
            RedditService.aboutSubreddit(this, subreddit);
            loadSubredditFromMenu(subreddit);
            closeMenuDrawerIfNeeded();
        }
    }

    protected abstract void loadSubredditFromMenu(String subreddit);

    protected void closeMenuDrawerIfNeeded() {
        if (mMenuDrawer != null && mMenuDrawer.isMenuVisible()) {
            mMenuDrawer.closeMenu();
        }
    }

    @Override
    public Age getAge() {
        return mAge;
    }

    @Override
    public void setAge(Age age) {
        mAge = age;
    }

    @Override
    public Category getCategory() {
        return mCategory;
    }

    @Override
    public void setCategory(Category category) {
        mCategory = category;
    }

    @Override
    public String getSubreddit() {
        return mSelectedSubreddit;
    }

    @Override
    public void setSubreddit(String subreddit) {
        mSelectedSubreddit = subreddit;
    }
}