Java tutorial
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"); } } }