de.janniskilian.xkcdreader.presentation.components.showcomics.ShowComicsActivity.java Source code

Java tutorial

Introduction

Here is the source code for de.janniskilian.xkcdreader.presentation.components.showcomics.ShowComicsActivity.java

Source

package de.janniskilian.xkcdreader.presentation.components.showcomics;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.gcm.GcmNetworkManager;
import com.google.android.gms.gcm.PeriodicTask;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import android.support.annotation.IdRes;
import android.support.design.widget.AppBarLayout;
import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.AppCompatDelegate;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.GestureDetector;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.Toast;

import com.jakewharton.rxbinding.widget.RxTextView;

import de.janniskilian.xkcdreader.R;
import de.janniskilian.xkcdreader.XKCDReaderApp;
import de.janniskilian.xkcdreader.data.services.notificationService.NotificationService;
import de.janniskilian.xkcdreader.domain.Comic;
import de.janniskilian.xkcdreader.helpers.Utils;
import de.janniskilian.xkcdreader.helpers.ui.decorations.DividerItemDecoration;
import de.janniskilian.xkcdreader.helpers.ui.searchBarAnimations.EndSearchModeAnimationListener;
import de.janniskilian.xkcdreader.helpers.ui.searchBarAnimations.StartSearchModeAnimationListener;

import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.inject.Inject;

import butterknife.BindDrawable;
import butterknife.BindInt;
import butterknife.BindView;
import butterknife.ButterKnife;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;

public class ShowComicsActivity extends AppCompatActivity implements ShowComicsContract.PresenterView,
        ShowComicsContract.AdapterView, ViewPager.OnPageChangeListener, RecyclerView.OnItemTouchListener {

    private static final int REQ_VOICE_INPUT = 0;
    private static final String TASK_NOTIFICATION = "TASK_NOTIFICATION";
    private static final long INTERVAL_TASK_NOTIFICATION = 60 * 60 * 3;
    private static final long FLEX_TASK_NOTIFICATION = 60 * 60;
    private static final String STATE_SEARCH_MODE_ACTIVE = "STATE_SEARCH_MODE_ACTIVE";
    private static final String STATE_SEARCH_INPUT = "STATE_SEARCH_INPUT";

    @Inject
    ShowComicsPresenter presenter;
    @BindView(R.id.toolbar)
    Toolbar toolbar;
    @BindView(R.id.search_toolbar)
    Toolbar searchToolbar;
    @BindView(R.id.view_pager)
    ViewPager viewPager;
    @BindView(R.id.search_results)
    RecyclerView searchResults;
    @BindInt(R.integer.reveal_status_bar_duration)
    int revealStatusBarDuration;
    @BindInt(R.integer.crossfade_viewpager_search_duration)
    int crossfadeViewPagerSearchDuration;
    @BindInt(R.integer.search_input_debounce_duration)
    int searchInputDebounceDuration;
    @BindDrawable(R.drawable.ic_arrow_back_black_24dp)
    Drawable tintedBackArrow;
    private ComicsAdapter comicsAdapter;
    private SearchAdapter searchAdapter;
    private GestureDetector gestureDetector;
    private Point searchButtonCenter;
    private EditText searchInput;
    private Subscription searchInputSub;
    private Subscription searchInputLengthSub;

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

        DaggerShowComicsComponent.builder().appComponent(((XKCDReaderApp) getApplication()).getComponent())
                .showComicsModule(new ShowComicsModule(this)).build().inject(this);

        setContentView(R.layout.activity_show_comics);
        ButterKnife.bind(this);

        // Set the icon for the overview screen.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            final Bitmap icon = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_overview);
            final int color = ContextCompat.getColor(this, R.color.primary);

            setTaskDescription(new ActivityManager.TaskDescription(null, icon, color));
        }

        setSupportActionBar(toolbar);
        // Set the correct margin for the overlaying toolbar.
        final AppBarLayout.LayoutParams layoutParams = new AppBarLayout.LayoutParams(
                searchToolbar.getLayoutParams());
        layoutParams.setMargins(0, -layoutParams.height, 0, 0);
        searchToolbar.setLayoutParams(layoutParams);

        comicsAdapter = new ComicsAdapter(getSupportFragmentManager(), this);
        viewPager.setAdapter(comicsAdapter);
        viewPager.addOnPageChangeListener(this);

        searchAdapter = new SearchAdapter();
        searchResults.setHasFixedSize(true);
        searchResults.setLayoutManager(new LinearLayoutManager(this));
        searchResults.addItemDecoration(new DividerItemDecoration(this));
        searchResults.setAdapter(searchAdapter);
        searchResults.addOnItemTouchListener(this);

        gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return true;
            }
        });

        tintedBackArrow.setColorFilter(ContextCompat.getColor(this, R.color.primary), PorterDuff.Mode.SRC_ATOP);

        presenter.attachView(this);

        if (checkPlayServicesAvailability()) {
            scheduleTasks();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        presenter.onStop();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter.detachView();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean(STATE_SEARCH_MODE_ACTIVE, presenter.isSearchModeActive());
        if (searchInput != null) {
            outState.putString(STATE_SEARCH_INPUT, searchInput.getText().toString());
        }
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        final boolean searchModeActive = savedInstanceState.getBoolean(STATE_SEARCH_MODE_ACTIVE);
        final String searchInput = savedInstanceState.getString(STATE_SEARCH_INPUT);
        presenter.onRestoreState(searchModeActive, searchInput);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        final MenuInflater inflater = getMenuInflater();
        if (presenter.isSearchModeActive()) {
            @IdRes
            final int resId;
            if (presenter.showSearchBarVoiceInput()) {
                inflater.inflate(R.menu.voice_input, menu);
                resId = R.id.action_voice_input;
            } else {
                inflater.inflate(R.menu.clear, menu);
                resId = R.id.action_clear;
            }

            // Tint the icon.
            final Drawable icon = DrawableCompat.wrap(menu.findItem(resId).getIcon()).mutate();
            DrawableCompat.setTint(icon, ContextCompat.getColor(this, R.color.icon));
        } else {
            inflater.inflate(R.menu.show_comics, menu);
        }
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        final int id = item.getItemId();
        if (id == android.R.id.home) {
            presenter.onHomePressed();
            return true;
        } else if (id == R.id.action_search) {
            presenter.onSearchOptionSelected();
            return true;
        } else if (id == R.id.action_info) {
            presenter.onInfoOptionSelected();
            return true;
        } else if (id == R.id.action_transcript) {
            presenter.onTranscriptOptionSelected();
            return true;
        } else if (id == R.id.action_explain) {
            presenter.onExplainOptionSelected();
            return true;
        } else if (id == R.id.action_latest) {
            presenter.onLatestOptionSelected();
            return true;
        } else if (id == R.id.action_random) {
            presenter.onRandomOptionSelected();
            return true;
        } else if (id == R.id.action_about) {
            presenter.onAboutOptionSelected();
            return true;
        } /*else if (id == R.id.action_settings) {
           return true;
          }*/ else if (id == R.id.action_voice_input) {
            presenter.onVoiceInputSelected();
            return true;
        } else if (id == R.id.action_clear) {
            presenter.onClearSelected();
            return true;
        } else if (id == R.id.action_toggle_night) {
            if (Utils.isNightModeActive(this)) {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
            } else {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
            }
            recreate();
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == Activity.RESULT_OK) {
            if (requestCode == REQ_VOICE_INPUT) {
                final String text = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS).get(0);
                presenter.onVoiceInputResult(text);
            }
        }
    }

    @Override
    public void onBackPressed() {
        if (!presenter.onBackPressed()) {
            super.onBackPressed();
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        handleIntent(intent);
    }

    @Override
    protected void onResume() {
        super.onResume();
        handleIntent(getIntent());
    }

    @Override
    public void startActivityForResult(Intent intent, int requestCode) {
        // If Google Play Services is not available (on the emulator), prevent a NPE.
        try {
            super.startActivityForResult(intent, requestCode);
        } catch (Exception ignored) {
        }
    }

    @Override
    public void setActionBarTitle(String title) {
        final ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.setTitle(title);
        }
    }

    @Override
    public void setSelectedPage(int position) {
        viewPager.setCurrentItem(position);
    }

    @Override
    public int getCurrentPosition() {
        return viewPager.getCurrentItem();
    }

    @Override
    public void notifyDataSetChanged() {
        comicsAdapter.notifyDataSetChanged();
    }

    @Override
    public void startSearchMode(boolean animate) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            if (animate) {
                final View actionMenuView = toolbar.getChildAt(1);
                final ViewGroup actionMenuViewGroup = (ViewGroup) actionMenuView;
                final View addButton = actionMenuViewGroup.getChildAt(0);

                searchButtonCenter = new Point(
                        (int) (addButton.getX() + actionMenuView.getX() + addButton.getWidth() / 2),
                        (int) (addButton.getY() + actionMenuView.getY() + addButton.getHeight() / 2));

                final Animator animator = ViewAnimationUtils.createCircularReveal(searchToolbar,
                        searchButtonCenter.x, searchButtonCenter.y, 0, searchToolbar.getWidth());
                animator.setDuration(revealStatusBarDuration);
                animator.addListener(new StartSearchModeAnimationListener(toolbar));
                searchToolbar.setVisibility(View.VISIBLE);
                animator.start();
            } else {
                searchToolbar.setVisibility(View.VISIBLE);
            }

            setSupportActionBar(searchToolbar);

            final ActionBar actionBar = getSupportActionBar();
            if (actionBar != null) {
                actionBar.setHomeButtonEnabled(true);
                actionBar.setDisplayHomeAsUpEnabled(true);
                actionBar.setHomeAsUpIndicator(tintedBackArrow);
                actionBar.setDisplayShowTitleEnabled(false);
                actionBar.setDisplayShowCustomEnabled(true);
                actionBar.setCustomView(R.layout.toolbar_search);
            }
        } else {
            toolbar.setBackgroundColor(ContextCompat.getColor(this, R.color.background));

            final ActionBar actionBar = getSupportActionBar();
            if (actionBar != null) {
                actionBar.setHomeButtonEnabled(true);
                actionBar.setDisplayHomeAsUpEnabled(true);
                actionBar.setHomeAsUpIndicator(tintedBackArrow);
                actionBar.setDisplayShowTitleEnabled(false);
                actionBar.setDisplayShowCustomEnabled(true);
                actionBar.setCustomView(R.layout.toolbar_search);
            }

            invalidateOptionsMenu();
        }

        searchInput = (EditText) findViewById(R.id.search_input);
        searchInput.requestFocus();
        searchInputSub = RxTextView.textChangeEvents(searchInput)
                .debounce(searchInputDebounceDuration, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread()).subscribe(textViewTextChangeEvent -> {
                    presenter.onSearchInputChanged(textViewTextChangeEvent.text().toString());
                });
        searchInputLengthSub = RxTextView.textChangeEvents(searchInput).observeOn(AndroidSchedulers.mainThread())
                .subscribe(textViewTextChangeEvent -> {
                    presenter.onSearchInputLengthChanged(textViewTextChangeEvent.text().toString(),
                            textViewTextChangeEvent.count());
                });
        searchInput.setOnEditorActionListener((textView, actionId, keyEvent) -> {
            if (actionId == EditorInfo.IME_ACTION_SEARCH) {
                Utils.hideKeyboard(ShowComicsActivity.this);
                return true;
            }
            return false;
        });

        Utils.showKeyboard(this);

        searchResults.setVisibility(View.VISIBLE);
        if (animate) {
            searchResults.animate().alpha(1).setDuration(crossfadeViewPagerSearchDuration).setListener(null);

            viewPager.animate().alpha(0).setDuration(crossfadeViewPagerSearchDuration)
                    .setListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            viewPager.setVisibility(View.GONE);
                        }
                    });
        } else {
            searchResults.setAlpha(1);
            viewPager.setAlpha(0);
            viewPager.setVisibility(View.GONE);
        }
    }

    @Override
    public void endSearchMode(boolean animate) {
        Utils.hideKeyboard(this);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            if (animate) {
                // If the device has rotated, we need to calculate a new center for the animation.
                if (searchButtonCenter == null) {
                    final View actionMenuView = searchToolbar.getChildAt(2);
                    final ViewGroup actionMenuViewGroup = (ViewGroup) actionMenuView;
                    final View button = actionMenuViewGroup.getChildAt(0);
                    System.out.println(button.getClass());

                    searchButtonCenter = new Point(
                            (int) (button.getX() + actionMenuView.getX() - button.getWidth() / 2),
                            (int) (button.getY() + actionMenuView.getY() + button.getHeight() / 2));
                }

                final Animator animator = ViewAnimationUtils.createCircularReveal(searchToolbar,
                        searchButtonCenter.x, searchButtonCenter.y, searchToolbar.getWidth(), 0);
                animator.setDuration(revealStatusBarDuration);
                animator.addListener(new EndSearchModeAnimationListener(searchToolbar));
                toolbar.setVisibility(View.VISIBLE);
                animator.start();
            } else {
                searchToolbar.setVisibility(View.INVISIBLE);
            }

            setSupportActionBar(toolbar);
        } else {
            toolbar.setBackgroundColor(ContextCompat.getColor(this, R.color.primary));

            final ActionBar actionBar = getSupportActionBar();
            if (actionBar != null) {
                actionBar.setHomeButtonEnabled(false);
                actionBar.setDisplayHomeAsUpEnabled(false);
                actionBar.setCustomView(R.layout.toolbar_search);
                actionBar.setDisplayShowCustomEnabled(false);
                actionBar.setDisplayShowTitleEnabled(true);
            }
        }

        viewPager.setVisibility(View.VISIBLE);
        if (animate) {
            searchResults.animate().alpha(0).setDuration(crossfadeViewPagerSearchDuration)
                    .setListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            searchResults.setVisibility(View.GONE);
                        }
                    });

            viewPager.animate().alpha(1).setDuration(crossfadeViewPagerSearchDuration).setListener(null);
        } else {
            viewPager.setAlpha(1);
            searchResults.setAlpha(0);
            searchResults.setVisibility(View.GONE);
        }

        searchInputSub.unsubscribe();
        searchInputLengthSub.unsubscribe();
        invalidateOptionsMenu();
    }

    @Override
    public void showInfo(int num, String title, String alt) {
        new AlertDialog.Builder(this, R.style.AlertDialogTheme).setTitle(getString(R.string.info_title, title, num))
                .setMessage(alt).setPositiveButton(R.string.close, null).show();
    }

    @Override
    public void showTranscript(String transcript) {
        new AlertDialog.Builder(this, R.style.AlertDialogTheme).setTitle(R.string.action_transcript)
                .setMessage(transcript).setPositiveButton(R.string.close, null).show();
    }

    @Override
    public void showNoTranscriptAvailable() {
        Toast.makeText(this, R.string.no_transcript, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showExplanation(String url) {
        Utils.openWebsite(this, url);
    }

    @Override
    public void setSearchResult(List<Comic> result) {
        searchAdapter.setData(result);
    }

    @Override
    public void recreateOptionsMenu() {
        invalidateOptionsMenu();
    }

    @Override
    public void setSearchInputText(String text) {
        if (searchInput != null) {
            searchInput.setText(text);
            searchInput.setSelection(text.length());
        }
    }

    @Override
    public void startVoiceInput() {
        try {
            startActivityForResult(Utils.createSpeechRecognitionIntent(), REQ_VOICE_INPUT);
        } catch (final ActivityNotFoundException e) {
            Toast.makeText(this, R.string.speech_recognition_unavailable, Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void showAbout() {
        Utils.openWebsite(this, getString(R.string.licenses_url));
    }

    @Override
    public int getCount() {
        return presenter.getCount();
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
        final View view = rv.findChildViewUnder(e.getX(), e.getY());
        if (view == null || !gestureDetector.onTouchEvent(e)) {
            return false;
        }

        final int position = searchResults.getChildAdapterPosition(view);
        if (position == RecyclerView.NO_POSITION) {
            return false;
        }
        presenter.onSearchItemSelected(searchAdapter.getItem(position));
        return true;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    }

    @Override
    public void onPageSelected(int position) {
        presenter.onPageSelected(position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
    }

    private boolean checkPlayServicesAvailability() {
        final GoogleApiAvailability availability = GoogleApiAvailability.getInstance();
        final int result = availability.isGooglePlayServicesAvailable(this);
        if (result != ConnectionResult.SUCCESS) {
            final Dialog errorDialog = availability.getErrorDialog(this, result, 0);
            if (errorDialog != null) {
                errorDialog.show();
            }
            return false;
        } else {
            return true;
        }
    }

    private void scheduleTasks() {
        final PeriodicTask notificationTask = new PeriodicTask.Builder().setService(NotificationService.class)
                .setTag(TASK_NOTIFICATION).setPeriod(INTERVAL_TASK_NOTIFICATION).setFlex(FLEX_TASK_NOTIFICATION)
                .build();
        GcmNetworkManager.getInstance(this).schedule(notificationTask);
    }

    private void handleIntent(Intent intent) {
        if (intent.hasExtra("num")) {
            presenter.onComicSelected(intent.getIntExtra("num", 1));
            intent.removeExtra("num");
        }
    }
}