Java tutorial
package com.stanleyidesis.quotograph.ui.activity; import android.animation.Animator; import android.animation.AnimatorInflater; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.annotation.SuppressLint; import android.app.Activity; import android.content.ClipData; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Point; import android.graphics.Rect; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.design.widget.TabLayout; import android.support.percent.PercentFrameLayout; import android.support.percent.PercentRelativeLayout; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.widget.AppCompatAutoCompleteTextView; import android.support.v7.widget.AppCompatCheckBox; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.KeyEvent; import android.view.View; import android.view.ViewAnimationUtils; import android.view.ViewGroup; import android.view.ViewPropertyAnimator; import android.view.ViewTreeObserver; import android.view.animation.AccelerateDecelerateInterpolator; import android.view.inputmethod.EditorInfo; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.ProgressBar; import android.widget.SeekBar; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; import com.afollestad.materialdialogs.MaterialDialog; import com.google.android.gms.ads.AdListener; import com.google.android.gms.ads.InterstitialAd; import com.google.firebase.remoteconfig.FirebaseRemoteConfig; import com.orm.SugarRecord; import com.orm.query.Select; import com.orm.util.NamingHelper; import com.stanleyidesis.quotograph.AdMobUtils; import com.stanleyidesis.quotograph.AnalyticsUtils; import com.stanleyidesis.quotograph.IabConst; import com.stanleyidesis.quotograph.LWQApplication; import com.stanleyidesis.quotograph.LWQPreferences; import com.stanleyidesis.quotograph.R; import com.stanleyidesis.quotograph.RemoteConfigConst; import com.stanleyidesis.quotograph.api.Callback; import com.stanleyidesis.quotograph.api.LWQError; import com.stanleyidesis.quotograph.api.controller.LWQAlarmController; import com.stanleyidesis.quotograph.api.controller.LWQLoggerHelper; import com.stanleyidesis.quotograph.api.controller.LWQQuoteControllerHelper; import com.stanleyidesis.quotograph.api.controller.LWQWallpaperController; import com.stanleyidesis.quotograph.api.controller.LWQWallpaperControllerHelper; import com.stanleyidesis.quotograph.api.db.Author; import com.stanleyidesis.quotograph.api.db.Category; import com.stanleyidesis.quotograph.api.db.Playlist; import com.stanleyidesis.quotograph.api.db.PlaylistAuthor; import com.stanleyidesis.quotograph.api.db.PlaylistCategory; import com.stanleyidesis.quotograph.api.db.PlaylistQuote; import com.stanleyidesis.quotograph.api.db.Quote; import com.stanleyidesis.quotograph.api.event.IabPurchaseEvent; import com.stanleyidesis.quotograph.api.event.PreferenceUpdateEvent; import com.stanleyidesis.quotograph.api.event.WallpaperEvent; import com.stanleyidesis.quotograph.api.misc.UserSurveyController; import com.stanleyidesis.quotograph.ui.UIUtils; import com.stanleyidesis.quotograph.ui.activity.modules.LWQChooseImageSourceModule; import com.stanleyidesis.quotograph.ui.activity.modules.WhatsNewDialog; import com.stanleyidesis.quotograph.ui.adapter.FontMultiselectAdapter; import com.stanleyidesis.quotograph.ui.adapter.PlaylistAdapter; import com.stanleyidesis.quotograph.ui.adapter.SearchResultsAdapter; import com.stanleyidesis.quotograph.ui.dialog.ThankYouDialog; import com.stanleyidesis.quotograph.ui.dialog.TutorialDialog; import org.greenrobot.eventbus.Subscribe; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.BlockingDeque; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingDeque; import butterknife.Bind; import butterknife.ButterKnife; import butterknife.OnCheckedChanged; import butterknife.OnClick; import it.sephiroth.android.library.tooltip.Tooltip; import static butterknife.ButterKnife.findById; /** * Copyright (c) 2016 Stanley Idesis * <p> * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * <p> * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * <p> * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * <p> * LWQSettingsActivity.java * * @author Stanley Idesis * <p> * From Quotograph * https://github.com/stanidesis/quotograph * <p> * Please report any issues * https://github.com/stanidesis/quotograph/issues * <p> * Date: 07/11/2015 */ public class LWQSettingsActivity extends LWQWallpaperActivity implements ActivityStateFlags, SeekBar.OnSeekBarChangeListener, PlaylistAdapter.Delegate, SearchResultsAdapter.Delegate, MaterialDialog.ListCallback, DialogInterface.OnCancelListener, LWQChooseImageSourceModule.Delegate, Tooltip.Callback, UserSurveyController.Delegate, TutorialDialog.Delegate { static class ActivityState { int page = -1; Map<Integer, Integer> newStateValues = new HashMap<>(); BackgroundWallpaperState backgroundWallpaperState = null; } static class Builder { ActivityState activityState; ActivityState build() { return activityState; } private Builder() { activityState = new ActivityState(); } private Builder(ActivityState copyState) { activityState = new ActivityState(); activityState.page = copyState.page; activityState.newStateValues.putAll(copyState.newStateValues); activityState.backgroundWallpaperState = copyState.backgroundWallpaperState; } Builder setViewPagerPage(int page) { activityState.page = page; return this; } Builder setViewState(int resId, int stateFlags) { activityState.newStateValues.put(resId, stateFlags); return this; } Builder setWallpaperState(BackgroundWallpaperState backgroundWallpaperState) { activityState.backgroundWallpaperState = backgroundWallpaperState; return this; } } Runnable changeStateRunnable = new Runnable() { @Override public void run() { if (stateBlockingDeque.isEmpty()) { return; } ActivityState nextActivityState = null; try { nextActivityState = stateBlockingDeque.take(); } catch (InterruptedException e) { e.printStackTrace(); } if (nextActivityState == null || nextActivityState == activityState) { return; } long longestAnimation = 0; // ViewPager Page Set if (nextActivityState.page > -1) { final int newPage = nextActivityState.page; runOnUiThread(new Runnable() { @Override public void run() { viewPager.setCurrentItem(newPage, true); } }); } // Wallpaper State final BackgroundWallpaperState newBackgroundWallpaperState = nextActivityState.backgroundWallpaperState; if (LWQSettingsActivity.this.backgroundWallpaperState != newBackgroundWallpaperState && newBackgroundWallpaperState != null) { longestAnimation = 300l; // TODO HAX runOnUiThread(new Runnable() { @Override public void run() { animateState(newBackgroundWallpaperState); } }); } // The MAIN loop for (int resourceId : nextActivityState.newStateValues.keySet()) { final View view = findById(LWQSettingsActivity.this, resourceId); if (view == null) { continue; } int currentFlags = (int) view.getTag(R.id.view_tag_flags); int newFlags = nextActivityState.newStateValues.get(resourceId); if (currentFlags == newFlags) { continue; } // Enable/disable int enableDisableFlags = newFlags & (FLAG_ENABLE | FLAG_DISABLE); if ((currentFlags & enableDisableFlags) == 0 && enableDisableFlags > 0) { final boolean enable = (newFlags & FLAG_ENABLE) > 0; runOnUiThread(new Runnable() { @Override public void run() { UIUtils.setViewAndChildrenEnabled(view, enable); } }); currentFlags &= ~(FLAG_ENABLE | FLAG_DISABLE); currentFlags |= enableDisableFlags; } // Reveal/Hide int revealHideFlags = newFlags & (FLAG_REVEAL | FLAG_HIDE); if ((currentFlags & revealHideFlags) == 0 && revealHideFlags > 0) { final boolean dismiss = (newFlags & FLAG_HIDE) > 0; runOnUiThread(new Runnable() { @Override public void run() { ((Runnable) view .getTag(dismiss ? R.id.view_tag_animator_hide : R.id.view_tag_animator_reveal)) .run(); } }); currentFlags &= ~(FLAG_REVEAL | FLAG_HIDE); currentFlags |= revealHideFlags; } // Rotate/No Rotate int rotateNoRotateFlags = newFlags & (FLAG_ROTATE | FLAG_NO_ROTATE); if ((currentFlags & rotateNoRotateFlags) == 0 && rotateNoRotateFlags > 0) { final boolean rotate = (newFlags & FLAG_ROTATE) > 0; runOnUiThread(new Runnable() { @Override public void run() { ((Runnable) view.getTag( rotate ? R.id.view_tag_animator_rotate : R.id.view_tag_animator_unrotate)) .run(); } }); currentFlags &= ~(FLAG_ROTATE | FLAG_NO_ROTATE); currentFlags |= rotateNoRotateFlags; } view.setTag(R.id.view_tag_flags, currentFlags); } activityState = nextActivityState; try { Thread.sleep(longestAnimation); } catch (InterruptedException e) { e.printStackTrace(); } } }; ActivityState stateInitial = new Builder().setWallpaperState(BackgroundWallpaperState.HIDDEN) .setViewPagerPage(0).setViewState(R.id.fab_lwq_plus, FLAG_HIDE | FLAG_DISABLE | FLAG_NO_ROTATE) .setViewState(R.id.group_lwq_settings_content, FLAG_HIDE) .setViewState(R.id.group_lwq_settings_fab_screen, FLAG_HIDE) .setViewState(R.id.pb_lwq_settings, FLAG_HIDE) .setViewState(R.id.btn_wallpaper_actions_skip, FLAG_DISABLE) .setViewState(R.id.btn_wallpaper_actions_save, FLAG_DISABLE) .setViewState(R.id.btn_wallpaper_actions_share, FLAG_DISABLE) .setViewState(R.id.group_lwq_settings_choose_image_source, FLAG_HIDE | FLAG_DISABLE).build(); ActivityState stateSkipWallpaper = new Builder().setWallpaperState(BackgroundWallpaperState.HIDDEN) .setViewState(R.id.pb_lwq_settings, FLAG_REVEAL) .setViewState(R.id.btn_wallpaper_actions_skip, FLAG_ROTATE | FLAG_DISABLE) .setViewState(R.id.btn_wallpaper_actions_save, FLAG_DISABLE).build(); ActivityState stateSaveWallpaper = new Builder(stateSkipWallpaper) .setWallpaperState(BackgroundWallpaperState.REVEALED) .setViewState(R.id.btn_wallpaper_actions_skip, FLAG_DISABLE) .setViewState(R.id.btn_wallpaper_actions_save, FLAG_ROTATE | FLAG_DISABLE).build(); ActivityState stateSaveSkipCompleted = new Builder().setWallpaperState(BackgroundWallpaperState.REVEALED) .setViewState(R.id.btn_wallpaper_actions_skip, FLAG_NO_ROTATE | FLAG_ENABLE) .setViewState(R.id.btn_wallpaper_actions_save, FLAG_NO_ROTATE | FLAG_ENABLE) .setViewState(R.id.pb_lwq_settings, FLAG_HIDE).build(); ActivityState stateSaveSkipCompletedObscured = new Builder(stateSaveSkipCompleted) .setWallpaperState(BackgroundWallpaperState.OBSCURED).build(); ActivityState statePlaylist = new Builder().setViewPagerPage(1) .setWallpaperState(BackgroundWallpaperState.OBSCURED) .setViewState(R.id.group_lwq_settings_content, FLAG_REVEAL) .setViewState(R.id.fab_lwq_plus, FLAG_NO_ROTATE) .setViewState(R.id.group_lwq_settings_fab_screen, FLAG_HIDE) .setViewState(R.id.group_lwq_fab_screen_add_edit_quote, FLAG_HIDE | FLAG_DISABLE) .setViewState(R.id.group_lwq_fab_screen_search, FLAG_HIDE | FLAG_DISABLE) .setViewState(R.id.group_lwq_settings_choose_image_source, FLAG_HIDE | FLAG_DISABLE) .setViewState(R.id.pb_lwq_settings, FLAG_HIDE).build(); ActivityState stateSettings = new Builder(statePlaylist).setViewPagerPage(2).build(); ActivityState stateWallpaperEdit = new Builder().setWallpaperState(BackgroundWallpaperState.REVEALED) .setViewState(R.id.group_lwq_settings_content, FLAG_HIDE).build(); ActivityState stateWallpaper = new Builder(stateWallpaperEdit).setViewPagerPage(0) .setViewState(R.id.btn_wallpaper_actions_skip, FLAG_ENABLE | FLAG_NO_ROTATE) .setViewState(R.id.btn_wallpaper_actions_save, FLAG_ENABLE | FLAG_NO_ROTATE) .setViewState(R.id.btn_wallpaper_actions_share, FLAG_ENABLE) .setViewState(R.id.pb_lwq_settings, FLAG_HIDE).build(); ActivityState stateAddReveal = new Builder().setViewState(R.id.fab_lwq_plus, FLAG_ROTATE | FLAG_ENABLE) .setViewState(R.id.group_lwq_settings_fab_screen, FLAG_REVEAL | FLAG_ENABLE) .setViewState(R.id.group_lwq_fab_screen_add_edit_quote, FLAG_HIDE | FLAG_DISABLE) .setViewState(R.id.group_lwq_fab_screen_search, FLAG_HIDE | FLAG_DISABLE) .setViewState(R.id.group_lwq_settings_choose_image_source, FLAG_HIDE | FLAG_DISABLE).build(); ActivityState stateAddEditQuote = new Builder(stateAddReveal) .setViewState(R.id.group_lwq_fab_screen_add_edit_quote, FLAG_REVEAL | FLAG_ENABLE).build(); ActivityState stateSearch = new Builder(stateAddReveal) .setViewState(R.id.group_lwq_fab_screen_search, FLAG_REVEAL | FLAG_ENABLE) .setViewState(R.id.group_lwq_settings_fab_screen, FLAG_REVEAL | FLAG_ENABLE) .setViewState(R.id.pb_lwq_settings, FLAG_HIDE).build(); ActivityState stateSearchInProgress = new Builder(stateAddReveal) .setViewState(R.id.fab_lwq_plus, FLAG_ROTATE | FLAG_DISABLE) .setViewState(R.id.group_lwq_settings_fab_screen, FLAG_REVEAL | FLAG_DISABLE) .setViewState(R.id.group_lwq_fab_screen_search, FLAG_REVEAL | FLAG_DISABLE) .setViewState(R.id.pb_lwq_settings, FLAG_REVEAL).build(); ActivityState stateChooseImageSources = new Builder(stateSettings) .setViewState(R.id.group_lwq_settings_choose_image_source, FLAG_REVEAL | FLAG_ENABLE).build(); private static final int REQUEST_CODE_SAVE = 0; private static final int REQUEST_CODE_ALBUM = 1; // Current ActivityState ActivityState activityState = null; // State Queue BlockingDeque<ActivityState> stateBlockingDeque = new LinkedBlockingDeque<>(); // Executes state changes ExecutorService changeStateExecutorService = Executors.newSingleThreadScheduledExecutor(); // PlaylistAdapter PlaylistAdapter playlistAdapter; // Editing this Quote Quote editingQuote; int editingQuotePosition; // Seekbar Status boolean isModifyingSeekSetting; // Tooltip Stuff boolean showTutorialTips; Set<TutorialTooltips> visibleTips; // Ads InterstitialAd interstitialAd; boolean firstAdLoaded; int maxAdLoadAttempts; // Content @Bind(R.id.group_lwq_settings_content) View content; // Layout Controls @Bind(R.id.tab_layout_lwq_settings) TabLayout tabLayout; @Bind(R.id.viewpager_lwq_settings) ViewPager viewPager; // Preview Container and Wallpaper Actions @Bind(R.id.group_lwq_settings_wallpaper_preview_wrapper) View wallpaperPreviewWrapper; @Bind(R.id.btn_wallpaper_actions_share) View shareButton; @Bind(R.id.btn_wallpaper_actions_save) View saveButton; @Bind(R.id.btn_wallpaper_actions_skip) View skipButton; // Settings Container @Bind(R.id.group_lwq_settings_settings_wrapper) View settingsWrapper; @Bind(R.id.group_lwq_settings_settings) View settingsContainer; // Playlist Container @Bind(R.id.group_lwq_settings_playlist_wrapper) View playlistWrapper; // ProgressBar @Bind(R.id.pb_lwq_settings) ProgressBar progressBar; // Choose Images @Bind(R.id.group_lwq_settings_choose_image_source) View chooseImagesContainer; // This is a slight departure from the way this class currently handles UI elements. // This module encapsulates some functionality, thereby hiding it from this class. LWQChooseImageSourceModule chooseImageSourceModule; // Choose Fonts MaterialDialog chooseFontsDialog; // FAB @Bind(R.id.group_lwq_settings_fab_screen) View fabContainer; @Bind(R.id.fab_lwq_plus) View fabAdd; @Bind(R.id.view_fab_background) View fabBackground; // FAB Actions @Bind(R.id.fab_lwq_search) View fabSearch; @Bind(R.id.fab_lwq_create_quote) View fabCreate; // Add/Edit Quote @Bind(R.id.group_lwq_fab_screen_add_edit_quote) View addEditQuote; @Bind(R.id.et_fab_screen_quote) EditText editableQuote; @Bind(R.id.actv_fab_screen_author) AppCompatAutoCompleteTextView editableAuthor; // Search @Bind(R.id.group_lwq_fab_screen_search) View searchContainer; @Bind(R.id.actv_fab_screen_search) AppCompatAutoCompleteTextView editableQuery; SearchResultsAdapter searchResultsAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_lwq_settings); ButterKnife.bind(this); content.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { content.getViewTreeObserver().removeGlobalOnLayoutListener(this); View[] buttons = new View[] { shareButton, saveButton, skipButton }; for (View button : buttons) { button.setAlpha(0f); button.setTranslationY(button.getHeight() * 2); button.requestLayout(); } animateWallpaperActions(false); } }); // Fetch remote config LWQApplication.fetchRemoteConfig(); // Setup content setupContent(); // Setup ViewPager and Controls setupViewPagerAndTabs(); // Setup FAB setupFABs(); // Setup Add/Edit setupAddEditQuote(); // Setup search setupSearch(); // Setup playlist setupPlaylist(); // Setup settings setupSettings(); // Setup Wallpaper actions setupWallpaperActions(); // Setup progress bar setupProgressBar(); // Setup image source chooser setupChooseImageSources(); // Ads setupAds(); // Track initial Screen View AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_WALLPAPER_PREVIEW); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); if (LWQWallpaperControllerHelper.get().activeWallpaperLoaded()) { changeState(stateWallpaper); } else { changeState(stateInitial); } if (!LWQPreferences.viewedTutorialDialog()) { TutorialDialog.showDialog(this, this); LWQPreferences.setViewedTutorialDialog(true); } else if (WhatsNewDialog.shouldShowDialog()) { showWhatsNewDialog(); } } @SuppressWarnings("WrongConstant") @SuppressLint("NewApi") @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE_SAVE) { changeState(stateSaveSkipCompleted); return; } else if (requestCode == IabConst.PURCHASE_REQUEST_CODE) { LWQApplication.getIabHelper().handleActivityResult(requestCode, resultCode, data); } else if (requestCode == REQUEST_CODE_ALBUM) { if (resultCode != RESULT_OK) { return; } List<String> resultUris = new ArrayList<>(); // final int takeFlags = data.getFlags() // & Intent.FLAG_GRANT_READ_URI_PERMISSION; if (data.getClipData() != null) { ClipData clipData = data.getClipData(); for (int i = 0; i < clipData.getItemCount(); i++) { Uri imageUri = clipData.getItemAt(i).getUri(); // getContentResolver().takePersistableUriPermission(imageUri, takeFlags); resultUris.add(imageUri.toString()); } } else if (data.getData() != null) { Uri imageUri = data.getData(); // getContentResolver().takePersistableUriPermission(imageUri, takeFlags); resultUris.add(imageUri.toString()); } chooseImageSourceModule.onImagesRecovered(resultUris); } super.onActivityResult(requestCode, resultCode, data); } @Override protected void onResume() { super.onResume(); if (LWQWallpaperControllerHelper.get().getRetrievalState() != LWQWallpaperController.RetrievalState.NONE) { changeState(stateSkipWallpaper); } else if (activityState == stateSearchInProgress) { changeState(stateSearch); AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_ADD_SEARCH); } // Show survey if applicable UserSurveyController.showSurvey(this); } @Override public void onBackPressed() { if (activityState == stateSearch || activityState == stateAddEditQuote || activityState == stateAddReveal) { changeState(statePlaylist); AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_PLAYLIST); } else if (activityState == stateChooseImageSources) { changeState(stateSettings); } else { if (activityState == stateSkipWallpaper || activityState == stateSaveWallpaper) { changeState(stateSaveSkipCompleted); } super.onBackPressed(); } } // Setup private void setupAds() { if (!AdMobUtils.adsEnabled()) { return; } View adsBanner = findViewById(R.id.rl_lwq_settings_remove_ads_banner); adsBanner.setVisibility(View.VISIBLE); ((TextView) adsBanner.findViewById(R.id.tv_lwq_settings_remove_ads_message)) .setText(FirebaseRemoteConfig.getInstance().getString(RemoteConfigConst.REMOVE_ADS_BANNER_MESSAGE)); maxAdLoadAttempts = 10; interstitialAd = new InterstitialAd(this); interstitialAd.setAdUnitId(getString(R.string.admob_prime_interstitial)); interstitialAd.setAdListener(new AdListener() { @Override public void onAdFailedToLoad(int i) { if (--maxAdLoadAttempts < 0) { return; } requestNewInterstitial(); } @Override public void onAdLoaded() { if (!firstAdLoaded) { firstAdLoaded = true; playlistAdapter.populateWithAds(); searchResultsAdapter.setAdsEnabled(true); } } @Override public void onAdLeftApplication() { AnalyticsUtils.trackEvent(AnalyticsUtils.CATEGORY_ADS, AnalyticsUtils.ACTION_CLICKTHROUGH, interstitialAd.getAdUnitId()); } }); // Ew, AdMob, ew... content.postDelayed(new Runnable() { @Override public void run() { if ((Build.VERSION.SDK_INT >= 17 && LWQSettingsActivity.this.isDestroyed()) || LWQSettingsActivity.this.isFinishing()) { return; } requestNewInterstitial(); } }, 1000); } private void requestNewInterstitial() { if (!AdMobUtils.adsEnabled()) { return; } interstitialAd.loadAd(AdMobUtils.buildRequest(LWQSettingsActivity.this)); } void setupContent() { content.setAlpha(0f); content.setTag(R.id.view_tag_flags, FLAG_HIDE); content.setTag(R.id.view_tag_animator_hide, new Runnable() { @Override public void run() { animateContent(true); } }); content.setTag(R.id.view_tag_animator_reveal, new Runnable() { @Override public void run() { animateContent(false); } }); } void setupViewPagerAndTabs() { viewPager.setOffscreenPageLimit(3); viewPager.setAdapter(new PagerAdapter() { @Override public Object instantiateItem(ViewGroup container, int position) { switch (position) { case 0: return wallpaperPreviewWrapper; case 1: return playlistWrapper; default: return settingsWrapper; } } @Override public int getCount() { return 3; } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public CharSequence getPageTitle(int position) { return ""; } }); viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { switch (position) { case 0: if (activityState != stateSkipWallpaper) { setBackgroundAlpha(BackgroundWallpaperState.OBSCURED.screenAlpha * positionOffset); } // Fab Add fabAdd.setScaleX(positionOffset); fabAdd.setScaleY(positionOffset); // Content content.setAlpha(positionOffset); // At .3, share is gone, at .6, save is gone, and at 1, skip is gone shareButton.setTranslationY((1f - (1f - (Math.min(positionOffset, 1 / 3f) / (1 / 3f)))) * shareButton.getHeight() * 3); saveButton.setTranslationY((1f - (1f - (Math.min(positionOffset, 2 / 3f) / (2 / 3f)))) * saveButton.getHeight() * 3); skipButton.setTranslationY((1f - (1f - positionOffset)) * skipButton.getHeight() * 3); break; case 1: fabAdd.setScaleX(1f - positionOffset); fabAdd.setScaleY(1f - positionOffset); } } @Override public void onPageSelected(int position) { // Content content.setAlpha(position == 0 ? 0f : 1f); // Add Fab fabAdd.setScaleX(position == 1 ? 1f : 0f); fabAdd.setScaleY(position == 1 ? 1f : 0f); fabAdd.setEnabled(position == 1); // Tooltips if (position == 1) { // Log screen view AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_PLAYLIST); showTutorialTip(TutorialTooltips.ADD); } else if (position == 2) { // Log screen view AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_SETTINGS); // Delay it just a little because the view messes up otherwise new Handler().postDelayed(new Runnable() { @Override public void run() { showTutorialTip(TutorialTooltips.SETTING); } }, 200); } else { // Log screen view AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_WALLPAPER_PREVIEW); } // Action Buttons View[] buttons = new View[] { shareButton, saveButton, skipButton }; for (View button : buttons) { button.setTranslationY(position == 0 ? 0f : button.getHeight() * 3f); button.setEnabled(position == 0); } if (activityState == stateSkipWallpaper) { return; } // Background setBackgroundAlpha(position == 0 ? BackgroundWallpaperState.REVEALED.screenAlpha : BackgroundWallpaperState.OBSCURED.screenAlpha); } @Override public void onPageScrollStateChanged(int state) { } }); tabLayout.setSelectedTabIndicatorColor(getResources().getColor(R.color.palette_500)); tabLayout.setupWithViewPager(viewPager); tabLayout.getTabAt(0).setIcon(R.drawable.selectable_preview_button); tabLayout.getTabAt(0).setContentDescription("Preview Quotograph"); tabLayout.getTabAt(1).setIcon(R.drawable.selectable_playlist_button); tabLayout.getTabAt(1).setContentDescription("Playlist"); tabLayout.getTabAt(2).setIcon(R.drawable.selectable_settings_button); tabLayout.getTabAt(2).setContentDescription("Settings"); } void setupWallpaperActions() { int navBarHeight = UIUtils.getNavBarHeight(this); if (navBarHeight > 0) { View wallpaperActionsContainer = findById(this, R.id.group_lwq_settings_wallpaper_actions); float navBarHeightPercentage = (float) navBarHeight / (float) UIUtils.getRealScreenSize().y; final PercentFrameLayout.LayoutParams layoutParams = (PercentFrameLayout.LayoutParams) wallpaperActionsContainer .getLayoutParams(); layoutParams.getPercentLayoutInfo().bottomMarginPercent += navBarHeightPercentage; wallpaperActionsContainer.requestLayout(); } shareButton.setTag(R.id.view_tag_flags, FLAG_ENABLE | FLAG_REVEAL); skipButton.setTag(R.id.view_tag_flags, FLAG_ENABLE | FLAG_REVEAL | FLAG_NO_ROTATE); skipButton.setTag(R.id.view_tag_animator_rotate, new Runnable() { @Override public void run() { animateAction(skipButton); } }); skipButton.setTag(R.id.view_tag_animator_unrotate, new Runnable() { @Override public void run() { endActionAnimation(skipButton); } }); saveButton.setTag(R.id.view_tag_flags, FLAG_ENABLE | FLAG_REVEAL | FLAG_NO_ROTATE); saveButton.setTag(R.id.view_tag_animator_rotate, new Runnable() { @Override public void run() { animateAction(saveButton); } }); saveButton.setTag(R.id.view_tag_animator_unrotate, new Runnable() { @Override public void run() { endActionAnimation(saveButton); } }); } void setupFABs() { fabContainer.setTag(R.id.view_tag_flags, FLAG_HIDE | FLAG_ENABLE); fabContainer.setTag(R.id.view_tag_animator_reveal, new Runnable() { @Override public void run() { animateFABActions(false).start(); } }); fabContainer.setTag(R.id.view_tag_animator_hide, new Runnable() { @Override public void run() { animateFABActions(true).start(); } }); int navBarHeight = UIUtils.getNavBarHeight(this); if (navBarHeight > 0) { float navBarHeightPercentage = (float) navBarHeight / (float) UIUtils.getRealScreenSize().y; View fabAddWrapper = findById(this, R.id.fl_lwq_fab_reveal); PercentRelativeLayout.LayoutParams layoutParams = (PercentRelativeLayout.LayoutParams) fabAddWrapper .getLayoutParams(); layoutParams.getPercentLayoutInfo().bottomMarginPercent += navBarHeightPercentage; fabAddWrapper.requestLayout(); } fabAdd.setVisibility(View.GONE); fabAdd.setTag(R.id.view_tag_flags, FLAG_HIDE | FLAG_DISABLE | FLAG_NO_ROTATE); fabAdd.setTag(R.id.view_tag_animator_reveal, new Runnable() { @Override public void run() { animateFAB(fabAdd, false).start(); } }); fabAdd.setTag(R.id.view_tag_animator_hide, new Runnable() { @Override public void run() { animateFAB(fabAdd, true).start(); } }); fabAdd.setTag(R.id.view_tag_animator_rotate, new Runnable() { @Override public void run() { animateFABRotation(true).start(); } }); fabAdd.setTag(R.id.view_tag_animator_unrotate, new Runnable() { @Override public void run() { animateFABRotation(false).start(); } }); fabBackground.setVisibility(View.GONE); fabCreate.setAlpha(0f); fabCreate.setVisibility(View.GONE); fabSearch.setAlpha(0f); fabSearch.setVisibility(View.GONE); animateFAB(fabAdd, false).start(); } void setupAddEditQuote() { addEditQuote.setAlpha(0f); addEditQuote.setVisibility(View.GONE); addEditQuote.setTag(R.id.view_tag_flags, FLAG_HIDE | FLAG_DISABLE); addEditQuote.setTag(R.id.view_tag_animator_hide, new Runnable() { @Override public void run() { animateContainer(addEditQuote, true); } }); addEditQuote.setTag(R.id.view_tag_animator_reveal, new Runnable() { @Override public void run() { animateContainer(addEditQuote, false); } }); new Thread(new Runnable() { @Override public void run() { Activity context = LWQSettingsActivity.this; final List<Author> list = Select.from(Author.class).orderBy(NamingHelper.toSQLNameDefault("name")) .list(); String[] allAuthors = new String[list.size()]; for (int i = 0; i < list.size(); i++) { allAuthors[i] = list.get(i).name; } final ArrayAdapter<String> authorAdapter = new ArrayAdapter<>(context, R.layout.support_simple_spinner_dropdown_item, allAuthors); context.runOnUiThread(new Runnable() { @Override public void run() { editableAuthor.setAdapter(authorAdapter); } }); } }).start(); } void setupSearch() { searchContainer.setAlpha(0f); searchContainer.setVisibility(View.GONE); searchContainer.setTag(R.id.view_tag_flags, FLAG_HIDE | FLAG_DISABLE); searchContainer.setTag(R.id.view_tag_animator_hide, new Runnable() { @Override public void run() { animateContainer(searchContainer, true); } }); searchContainer.setTag(R.id.view_tag_animator_reveal, new Runnable() { @Override public void run() { animateContainer(searchContainer, false); } }); editableQuery.setSupportBackgroundTintList(getResources().getColorStateList(R.color.text_field_state_list)); editableQuery.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) { if (i == EditorInfo.IME_NULL) { performSearch(); } return false; } }); // RecyclerView searchResultsAdapter = new SearchResultsAdapter(); searchResultsAdapter.setDelegate(this); final RecyclerView recyclerView = findById(this, R.id.recycler_fab_screen_search_results); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(searchResultsAdapter); new Thread(new Runnable() { @Override public void run() { final List<Category> categories = Category.listAll(Category.class); final List<Author> authors = Author.listAll(Author.class); String[] allHints = new String[categories.size() + authors.size()]; for (int i = 0; i < categories.size(); i++) { allHints[i] = categories.get(i).name; } for (int i = 0; i < authors.size(); i++) { allHints[categories.size() + i] = authors.get(i).name; } final ArrayAdapter<String> searchAdapter = new ArrayAdapter<>(LWQSettingsActivity.this, R.layout.support_simple_spinner_dropdown_item, allHints); LWQSettingsActivity.this.runOnUiThread(new Runnable() { @Override public void run() { editableQuery.setAdapter(searchAdapter); } }); } }).start(); } void setupPlaylist() { playlistAdapter = new PlaylistAdapter(this); RecyclerView recyclerView = findById(this, R.id.recycler_playlist); recyclerView.setHasFixedSize(false); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(playlistAdapter); recyclerView.setItemAnimator(new DefaultItemAnimator()); } void setupSettings() { final String[] refreshPreferenceOptions = getResources().getStringArray(R.array.refresh_preference_options); ArrayAdapter<String> refreshOptionsAdapter = new ArrayAdapter<>(this, R.layout.spinner_item, refreshPreferenceOptions); refreshOptionsAdapter.setDropDownViewResource(R.layout.spinner_drop_down_item); Spinner refreshSpinner = findById(settingsContainer, R.id.spinner_lwq_settings_refresh); refreshSpinner.setAdapter(refreshOptionsAdapter); refreshSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> adapterView, View view, int index, long l) { // Log refresh rate choice LWQLoggerHelper.get().logRefreshRate((String) adapterView.getAdapter().getItem(index)); final int[] refreshValues = getResources().getIntArray(R.array.refresh_preference_values); LWQPreferences.setRefreshPreference(refreshValues[index]); LWQAlarmController.resetAlarm(); } @Override public void onNothingSelected(AdapterView<?> adapterView) { } }); updateRefreshSpinner(); // Blur if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { findById(settingsContainer, R.id.sb_lwq_settings_blur).setVisibility(View.GONE); } else { SeekBar blurBar = findById(settingsContainer, R.id.sb_lwq_settings_blur); blurBar.setProgress(LWQPreferences.getBlurPreference()); blurBar.setOnSeekBarChangeListener(this); } // Dim SeekBar dimBar = findById(settingsContainer, R.id.sb_lwq_settings_dim); dimBar.setProgress(LWQPreferences.getDimPreference()); dimBar.setOnSeekBarChangeListener(this); AppCompatCheckBox doubleTapCheckbox = findById(this, R.id.check_lwq_settings_double_tap); doubleTapCheckbox.setChecked(LWQPreferences.isDoubleTapEnabled()); // Fonts findById(settingsContainer, R.id.btn_lwq_settings_fonts).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { chooseFontsDialog = new MaterialDialog.Builder(LWQSettingsActivity.this).title("Choose Fonts") .adapter(new FontMultiselectAdapter(LWQSettingsActivity.this), LWQSettingsActivity.this) .alwaysCallMultiChoiceCallback().autoDismiss(false).canceledOnTouchOutside(true) .cancelListener(LWQSettingsActivity.this).build(); chooseFontsDialog.show(); // Log view AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_FONTS); } }); // Images findById(settingsContainer, R.id.btn_lwq_settings_images).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { changeState(stateChooseImageSources); // Log view AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_IMAGES); } }); } void updateRefreshSpinner() { Spinner refreshSpinner = findById(settingsContainer, R.id.spinner_lwq_settings_refresh); final AdapterView.OnItemSelectedListener onItemSelectedListener = refreshSpinner .getOnItemSelectedListener(); refreshSpinner.setOnItemSelectedListener(null); final long refreshPreference = LWQPreferences.getRefreshPreference(); final int[] refreshValues = getResources().getIntArray(R.array.refresh_preference_values); for (int i = 0; i < refreshValues.length; i++) { if (refreshPreference == refreshValues[i]) { refreshSpinner.setSelection(i); break; } } refreshSpinner.setOnItemSelectedListener(onItemSelectedListener); } void setupProgressBar() { progressBar.setAlpha(0f); progressBar.setTag(R.id.view_tag_flags, FLAG_HIDE | FLAG_ENABLE); progressBar.setTag(R.id.view_tag_animator_hide, new Runnable() { @Override public void run() { animateProgressBar(false); } }); progressBar.setTag(R.id.view_tag_animator_reveal, new Runnable() { @Override public void run() { animateProgressBar(true); } }); } void setupChooseImageSources() { chooseImageSourceModule = new LWQChooseImageSourceModule(); chooseImageSourceModule.initialize(this, chooseImagesContainer); chooseImagesContainer.setTag(R.id.view_tag_flags, FLAG_HIDE | FLAG_DISABLE); chooseImagesContainer.setTag(R.id.view_tag_animator_hide, new Runnable() { @Override public void run() { chooseImageSourceModule .changeVisibility(findById(LWQSettingsActivity.this, R.id.btn_lwq_settings_images), false); } }); chooseImagesContainer.setTag(R.id.view_tag_animator_reveal, new Runnable() { @Override public void run() { chooseImageSourceModule .changeVisibility(findById(LWQSettingsActivity.this, R.id.btn_lwq_settings_images), true); } }); } // Click Handling // Hanlde the click for every settings option @OnClick({ R.id.tv_lwq_settings_blur, R.id.tv_lwq_settings_dim, R.id.tv_lwq_settings_double_tap, R.id.tv_lwq_settings_fonts, R.id.tv_lwq_settings_images, R.id.tv_lwq_settings_refresh, R.id.tv_lwq_settings_whats_new }) void showSettingsTooltip(View view) { switch (view.getId()) { case R.id.tv_lwq_settings_blur: showToolTip(SettingsTooltips.BLUR, null); break; case R.id.tv_lwq_settings_dim: showToolTip(SettingsTooltips.DIM, null); break; case R.id.tv_lwq_settings_double_tap: showToolTip(SettingsTooltips.DOUBLE_TAP, null); break; case R.id.tv_lwq_settings_fonts: showToolTip(SettingsTooltips.FONTS, null); break; case R.id.tv_lwq_settings_images: showToolTip(SettingsTooltips.IMAGES, null); break; case R.id.tv_lwq_settings_refresh: showToolTip(SettingsTooltips.REFRESH, null); break; case R.id.tv_lwq_settings_whats_new: showToolTip(SettingsTooltips.WHATS_NEW, null); break; } } @OnClick(R.id.btn_lwq_settings_whats_new) void showWhatsNewDialog() { // Log view AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_WHATS_NEW); // Show dialog WhatsNewDialog dialog = new WhatsNewDialog(); dialog.initialize(this, null); dialog.changeVisibility(null, true); } @OnClick(R.id.btn_fab_screen_search) void performSearch() { UIUtils.dismissKeyboard(this); changeState(stateSearchInProgress); final int itemCount = searchResultsAdapter.getItemCount(); searchResultsAdapter.setSearchResults(new ArrayList<Object>()); searchResultsAdapter.notifyDataSetChanged(); String query = editableQuery.getText().toString().trim(); LWQQuoteControllerHelper.get().fetchQuotes(query, new Callback<List<Object>>() { @Override public void onSuccess(final List<Object> objects) { runOnUiThread(new Runnable() { @Override public void run() { changeState(stateSearch); searchResultsAdapter.setSearchResults(objects); searchResultsAdapter.notifyDataSetChanged(); } }); } @Override public void onError(LWQError error) { runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(LWQSettingsActivity.this, "Search pooped out :(, please try again later.", Toast.LENGTH_LONG).show(); changeState(stateSearch); } }); } }); // Log the search AnalyticsUtils.trackSearch(query); } @OnClick(R.id.fab_lwq_create_quote) void revealAddEditQuote() { if (activityState == stateAddEditQuote) { changeState(stateAddReveal); // Log the view AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_ADD); } else { editableAuthor.setText(""); editableQuote.setText(""); changeState(stateAddEditQuote); // Log the view AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_ADD_QUOTE); } } @OnClick(R.id.btn_fab_screen_save) void saveQuote() { Author author = Author.findAuthor(editableAuthor.getText().toString().trim()); if (author == null) { // Create a new Author author = new Author(editableAuthor.getText().toString().trim(), true); author.save(); } if (editingQuote != null) { editingQuote.author = author; editingQuote.text = editableQuote.getText().toString().trim(); editingQuote.save(); playlistAdapter.notifyItemChanged(editingQuotePosition); editingQuote = null; editingQuotePosition = -1; changeState(statePlaylist); return; } Quote quote = Quote.find(editableQuote.getText().toString().trim(), author); if (quote == null) { quote = new Quote(editableQuote.getText().toString().trim(), author, null); quote.save(); } PlaylistQuote playlistQuote = new PlaylistQuote(Playlist.active(), quote); playlistQuote.save(); playlistAdapter.insertItem(playlistQuote); changeState(statePlaylist); UIUtils.dismissKeyboard(this); // Log quote AnalyticsUtils.trackEvent(AnalyticsUtils.CATEGORY_QUOTE, AnalyticsUtils.ACTION_CREATED, "".equalsIgnoreCase(author.name) ? null : author.name); } @OnClick(R.id.btn_fab_screen_cancel) void dismissAddEditQuote() { editingQuote = null; editingQuotePosition = -1; changeState(stateAddReveal); UIUtils.dismissKeyboard(this); // Log the view AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_ADD); } @OnClick(R.id.fab_lwq_plus) void toggleAddScreen() { if (activityState == stateAddReveal || activityState == stateAddEditQuote || activityState == stateSearch) { UIUtils.dismissKeyboard(this); changeState(statePlaylist); // Log the view AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_PLAYLIST); } else { changeState(stateSearch); // Log the view AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_ADD_SEARCH); } } @OnClick(R.id.fab_lwq_search) void revealSearch() { if (activityState == stateSearch) { changeState(stateAddReveal); UIUtils.dismissKeyboard(this); // Log the view AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_ADD); } else { editableQuery.clearComposingText(); editableQuery.clearFocus(); changeState(stateSearch); // Log the view AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_ADD_SEARCH); } } @OnClick(R.id.btn_wallpaper_actions_skip) void skipWallpaperClick() { AnalyticsUtils.trackEvent(AnalyticsUtils.CATEGORY_WALLPAPER, AnalyticsUtils.ACTION_SKIPPED, AnalyticsUtils.LABEL_IN_APP); LWQWallpaperControllerHelper.get().generateNewWallpaper(); // Show an interstitial, if applicable if (interstitialAd != null && interstitialAd.isLoaded()) { interstitialAd.show(); } } @OnClick(R.id.btn_wallpaper_actions_save) void saveWallpaperClick() { startActivityForResult(new Intent(this, LWQSaveWallpaperActivity.class), REQUEST_CODE_SAVE); changeState(stateSaveWallpaper); } @OnClick(R.id.btn_wallpaper_actions_share) void shareWallpaperClick() { sendBroadcast(new Intent(getString(R.string.action_share))); } @OnClick(R.id.rl_lwq_settings_remove_ads_banner) void purchaseAdRemoval() { AnalyticsUtils.trackEvent(AnalyticsUtils.CATEGORY_ADS, AnalyticsUtils.ACTION_TAP, AnalyticsUtils.LABEL_REMOVE_ADS_BANNER); LWQApplication.purchaseProduct(this, IabConst.Product.REMOVE_ADS); } @OnCheckedChanged(R.id.check_lwq_settings_double_tap) void onDoubleTapCheckChange(boolean checked) { LWQPreferences.setDoubleTapEnabled(checked); } // Animation void endActionAnimation(final View button) { runOnUiThread(new Runnable() { @Override public void run() { if (button.getTag(R.id.view_tag_animator) != null) { ((Animator) button.getTag(R.id.view_tag_animator)).end(); button.setTag(R.id.view_tag_animator, null); } } }); } void animateWallpaperActions(final boolean dismiss) { AnimatorSet animatorSet = new AnimatorSet(); Animator shareButtonAnimator = generateAnimator(shareButton, dismiss, 0); animatorSet.playTogether(shareButtonAnimator, generateAnimator(saveButton, dismiss, 20), generateAnimator(skipButton, dismiss, 35)); animatorSet.start(); } Animator generateAnimator(View target, boolean dismiss, long startDelay) { float[] fadeIn = new float[] { 0f, 1f }; float[] fadeOut = new float[] { 1f, 0f }; final ObjectAnimator propAnimator = ObjectAnimator.ofPropertyValuesHolder(target, PropertyValuesHolder.ofFloat(View.ALPHA, dismiss ? fadeOut : fadeIn), PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, dismiss ? (target.getHeight() * 2f) : 0f)); propAnimator.setStartDelay(startDelay); propAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); propAnimator.setDuration(240); return propAnimator; } void animateAction(final View button) { runOnUiThread(new Runnable() { @Override public void run() { final Animator animator = AnimatorInflater.loadAnimator(LWQSettingsActivity.this, R.animator.progress_rotation); animator.setTarget(button); button.setTag(R.id.view_tag_animator, animator); animator.start(); } }); } void animateProgressBar(final boolean reveal) { runOnUiThread(new Runnable() { @Override public void run() { progressBar.animate().alpha(reveal ? 1f : 0f).setDuration(150) .setInterpolator(new AccelerateDecelerateInterpolator()) .setListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { progressBar.setTag(R.id.view_tag_flags, reveal ? FLAG_REVEAL : FLAG_HIDE); } }).start(); } }); } void animateContent(final boolean dismiss) { content.clearAnimation(); content.animate().alpha(dismiss ? 0f : 1f).setDuration(300) .setInterpolator(new AccelerateDecelerateInterpolator()).start(); } void animateContainer(final View container, final boolean dismiss) { container.setVisibility(View.VISIBLE); Animator animator = dismiss ? AnimatorInflater.loadAnimator(this, R.animator.exit_to_left) : AnimatorInflater.loadAnimator(this, R.animator.enter_from_right); animator.setTarget(container); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (dismiss) { container.setVisibility(View.GONE); UIUtils.dismissKeyboard(LWQSettingsActivity.this); } else { UIUtils.revealKeyboard(container == addEditQuote ? editableQuote : editableQuery); } } }); animator.start(); } ViewPropertyAnimator animateFABRotation(final boolean rotate) { final ViewPropertyAnimator animate = fabAdd.animate(); animate.rotation(rotate ? 45f : 0f).setInterpolator(new AccelerateDecelerateInterpolator()) .setDuration(150); return animate; } Animator animateFAB(final View fabToAnimate, final boolean dismiss) { fabToAnimate.setVisibility(View.VISIBLE); float[] toFrom = dismiss ? new float[] { 1f, .2f } : new float[] { .2f, 1f }; AnimatorSet animatorSet = new AnimatorSet(); animatorSet.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { fabToAnimate.setVisibility(dismiss ? View.GONE : View.VISIBLE); } }); ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(fabToAnimate, "alpha", toFrom); alphaAnimator.setDuration(200); alphaAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); // Create a scale + fade animation ObjectAnimator scaleDown = ObjectAnimator.ofPropertyValuesHolder(fabToAnimate, PropertyValuesHolder.ofFloat("scaleX", toFrom), PropertyValuesHolder.ofFloat("scaleY", toFrom)); scaleDown.setDuration(200); scaleDown.setInterpolator(new AccelerateDecelerateInterpolator()); animatorSet.playTogether(scaleDown, alphaAnimator); return animatorSet; } Animator animateFABActions(final boolean dismiss) { fabBackground.setVisibility(View.VISIBLE); Animator backgroundAnimator; if (Build.VERSION.SDK_INT >= 21) { Rect fabRect = new Rect(); fabAdd.getGlobalVisibleRect(fabRect); final Point realScreenSize = UIUtils.getRealScreenSize(); int radius = Math.max(realScreenSize.x, realScreenSize.y); backgroundAnimator = ViewAnimationUtils.createCircularReveal(fabBackground, fabRect.centerX(), fabRect.centerY(), dismiss ? radius : 0, dismiss ? 0 : radius); } else { backgroundAnimator = ObjectAnimator.ofFloat(fabBackground, "alpha", dismiss ? 1f : 0f, dismiss ? 0f : 1f); } backgroundAnimator.setDuration(300).setInterpolator(new AccelerateDecelerateInterpolator()); backgroundAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (dismiss) { fabBackground.setVisibility(View.GONE); // Tell them to swipe again for settings! showTutorialTip(TutorialTooltips.SWIPE_AGAIN); } else { // Tell them about search! showTutorialTip(TutorialTooltips.SEARCH); } } }); final long shortDelay = 50; final long longDelay = 100; final Animator createAnimator = animateFAB(fabCreate, dismiss); final Animator searchAnimator = animateFAB(fabSearch, dismiss); createAnimator.setStartDelay(dismiss ? longDelay : shortDelay); searchAnimator.setStartDelay(dismiss ? shortDelay : longDelay); AnimatorSet allAnimations = new AnimatorSet(); allAnimations.playTogether(backgroundAnimator, createAnimator, searchAnimator); return allAnimations; } // Tutorial Dialog @Override public void onShowTutorial(TutorialDialog dialog) { showTutorialTips = true; visibleTips = new HashSet<>(); showTutorialTip(TutorialTooltips.SHARE); } @Override public void onDismiss(TutorialDialog dialog) { AnalyticsUtils.trackEvent(AnalyticsUtils.CATEGORY_TOOLTIPS, AnalyticsUtils.ACTION_TAP, AnalyticsUtils.LABEL_SKIP_TUTORIAL); } // Misc void changeState(ActivityState activityState) { try { stateBlockingDeque.put(activityState); changeStateExecutorService.submit(new Runnable() { @Override public void run() { try { changeStateRunnable.run(); } catch (Exception e) { e.printStackTrace(); } } }); } catch (InterruptedException e) { e.printStackTrace(); } } void addRemovePlaylistItem(Object item, boolean add) { String analyticsLabel; if (item instanceof PlaylistCategory) { analyticsLabel = ((PlaylistCategory) item).category.name; } else if (item instanceof PlaylistAuthor) { analyticsLabel = ((PlaylistAuthor) item).author.name; } else if (item instanceof PlaylistQuote) { String quote = ((PlaylistQuote) item).quote.text; analyticsLabel = quote.substring(0, Math.min(35, quote.length())) + "..."; } else { return; } if (add) { playlistAdapter.insertItem(item); } else { if (playlistAdapter.getPlaylistItemCount() == 1) { Toast.makeText(this, "Your playlist may not be empty", Toast.LENGTH_LONG).show(); return; } playlistAdapter.removeItem(item); ((SugarRecord) item).delete(); } // Log it AnalyticsUtils.trackEvent(AnalyticsUtils.CATEGORY_PLAYLIST, add ? AnalyticsUtils.ACTION_ADDED : AnalyticsUtils.ACTION_REMOVED, analyticsLabel); } // Event Handling @Subscribe public void onEvent(final IabPurchaseEvent purchaseEvent) { if (purchaseEvent.didFail()) { return; } if (purchaseEvent.purchased == IabConst.Product.REMOVE_ADS) { // Remove ads interstitialAd = null; runOnUiThread(new Runnable() { @Override public void run() { playlistAdapter.removeAllAds(); searchResultsAdapter.setAdsEnabled(false); findViewById(R.id.rl_lwq_settings_remove_ads_banner).setVisibility(View.GONE); ThankYouDialog.showDialog(LWQSettingsActivity.this); } }); } } @Subscribe public void onEvent(PreferenceUpdateEvent preferenceUpdateEvent) { if (preferenceUpdateEvent.getPreferenceKeyId() == R.string.preference_key_refresh) { runOnUiThread(new Runnable() { @Override public void run() { updateRefreshSpinner(); } }); } } @Subscribe public void onEvent(final WallpaperEvent wallpaperEvent) { if (wallpaperEvent.didFail()) { changeState(viewPager.getCurrentItem() == 0 ? stateSaveSkipCompleted : stateSaveSkipCompletedObscured); if (wallpaperEvent.getErrorMessage() == null) { return; } runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(LWQSettingsActivity.this, wallpaperEvent.getErrorMessage(), Toast.LENGTH_LONG) .show(); } }); } else if (wallpaperEvent.getStatus() == WallpaperEvent.Status.RENDERED_WALLPAPER) { if (!isModifyingSeekSetting) { changeState( viewPager.getCurrentItem() == 0 ? stateSaveSkipCompleted : stateSaveSkipCompletedObscured); } else if (activityState == stateInitial) { changeState(stateWallpaper); } } else { changeState(stateSkipWallpaper); } } // SeekBar Listener @Override public void onProgressChanged(SeekBar seekBar, int value, boolean b) { if (seekBar.getId() == R.id.sb_lwq_settings_blur) { LWQPreferences.setBlurPreference(value); } else { LWQPreferences.setDimPreference(value); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { isModifyingSeekSetting = true; changeState(stateWallpaperEdit); } @Override public void onStopTrackingTouch(SeekBar seekBar) { isModifyingSeekSetting = false; changeState(stateSettings); } // PlaylistAdapter.Delegate @Override public void onPlaylistItemRemove(PlaylistAdapter adapter, int position) { addRemovePlaylistItem(adapter.getItem(position), false); } @Override public void onQuoteEdit(PlaylistAdapter adapter, int position) { final Object item = adapter.getItem(position); if (item instanceof PlaylistQuote) { editingQuotePosition = position; editingQuote = ((PlaylistQuote) item).quote; editableAuthor.setText(editingQuote.author.name); editableQuote.setText(editingQuote.text); if (activityState != stateAddEditQuote) { changeState(stateAddEditQuote); AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_ADD_QUOTE); } } // Log the edit AnalyticsUtils.trackEvent(AnalyticsUtils.CATEGORY_QUOTE, AnalyticsUtils.ACTION_EDITED); } @Override public void onMakeQuotograph(PlaylistAdapter adapter, int position) { final Object item = adapter.getItem(position); if (item instanceof PlaylistCategory) { LWQWallpaperControllerHelper.get().generateNewWallpaper((PlaylistCategory) item); } else if (item instanceof PlaylistAuthor) { LWQWallpaperControllerHelper.get().generateNewWallpaper((PlaylistAuthor) item); } else if (item instanceof PlaylistQuote) { LWQWallpaperControllerHelper.get().generateNewWallpaper((PlaylistQuote) item); } // Track manual creation AnalyticsUtils.trackEvent(AnalyticsUtils.CATEGORY_WALLPAPER, AnalyticsUtils.ACTION_MANUALLY_GEN, AnalyticsUtils.LABEL_PLAYLIST); if (interstitialAd != null && interstitialAd.isLoaded()) { interstitialAd.show(); } } @Override public void onRemove(SearchResultsAdapter adapter, Object playlistItem) { addRemovePlaylistItem(playlistItem, false); } @Override public Object onAdd(SearchResultsAdapter adapter, Object model) { SugarRecord playlistItem = null; if (model instanceof Category) { playlistItem = new PlaylistCategory(Playlist.active(), (Category) model); } else if (model instanceof Author) { playlistItem = new PlaylistAuthor(Playlist.active(), (Author) model); } else if (model instanceof Quote) { playlistItem = new PlaylistQuote(Playlist.active(), (Quote) model); } if (playlistItem != null) { playlistItem.save(); addRemovePlaylistItem(playlistItem, true); } return playlistItem; } // MaterialDialog Delegates @Override public void onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text) { FontMultiselectAdapter adapter = (FontMultiselectAdapter) dialog.getListView().getAdapter(); adapter.addOrRemoveFont(which); } @Override public void onCancel(DialogInterface dialog) { if (!(dialog instanceof MaterialDialog)) { return; } MaterialDialog materialDialog = (MaterialDialog) dialog; if (materialDialog.getListView() != null && materialDialog.getListView().getAdapter() instanceof FontMultiselectAdapter) { FontMultiselectAdapter adapter = (FontMultiselectAdapter) materialDialog.getListView().getAdapter(); adapter.setDefaultsIfNecessary(); } chooseFontsDialog = null; } // LWQChooseImageSourceModule delegate @Override public void addPhotoAlbum(LWQChooseImageSourceModule module) { Intent imagePicker = new Intent(Intent.ACTION_GET_CONTENT); imagePicker.setType("image/*"); imagePicker.addCategory(Intent.CATEGORY_OPENABLE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { imagePicker.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); } startActivityForResult(Intent.createChooser(imagePicker, "Choose photo(s)"), REQUEST_CODE_ALBUM); // Track viewing AnalyticsUtils.trackScreenView(AnalyticsUtils.SCREEN_CUSTOM_PHOTOS); } // UserSurveyController Delegate @Override public void revealSurveyInPlaylist() { runOnUiThread(new Runnable() { @Override public void run() { playlistAdapter.setShowSurvey(true); } }); } // Tooltip Sequence private enum SettingsTooltips { BLUR(R.string.tt_setting_blur, R.id.sb_lwq_settings_blur), DIM(R.string.tt_setting_dim, R.id.sb_lwq_settings_dim), FONTS(R.string.tt_setting_fonts, R.id.btn_lwq_settings_fonts), IMAGES( R.string.tt_setting_images, R.id.btn_lwq_settings_images), REFRESH(R.string.tt_setting_refresh, R.id.spinner_lwq_settings_refresh), DOUBLE_TAP(R.string.tt_setting_double_tap, R.id.check_lwq_settings_double_tap), WHATS_NEW( R.string.tt_setting_whats_new, R.id.btn_lwq_settings_whats_new); int stringId; int anchorId; SettingsTooltips(int stringId, int anchorId) { this.stringId = stringId; this.anchorId = anchorId; } } private enum TutorialTooltips { SHARE(R.string.tt_share_quotograph, R.id.btn_wallpaper_actions_share, Tooltip.ClosePolicy.TOUCH_ANYWHERE_NO_CONSUME), SAVE(R.string.tt_save_quotograph, R.id.btn_wallpaper_actions_save, Tooltip.ClosePolicy.TOUCH_ANYWHERE_NO_CONSUME), SKIP( R.string.tt_skip_quotograph, R.id.btn_wallpaper_actions_skip, Tooltip.ClosePolicy.TOUCH_ANYWHERE_NO_CONSUME), SWIPE( R.string.tt_swipe_left_wallpaper, R.id.viewpager_lwq_settings, Tooltip.ClosePolicy.TOUCH_ANYWHERE_NO_CONSUME, Tooltip.Gravity.CENTER, true), ADD(R.string.tt_add_quotes, R.id.fab_lwq_plus, Tooltip.ClosePolicy.TOUCH_ANYWHERE_NO_CONSUME, Tooltip.Gravity.LEFT, true), SEARCH(R.string.tt_search_for_quotes, R.id.actv_fab_screen_search, Tooltip.ClosePolicy.TOUCH_ANYWHERE_NO_CONSUME, Tooltip.Gravity.LEFT, false), WRITE( R.string.tt_write_quotes, R.id.fab_lwq_create_quote, Tooltip.ClosePolicy.TOUCH_ANYWHERE_NO_CONSUME, Tooltip.Gravity.LEFT, false), EXIT( R.string.tt_exit_add, R.id.fab_lwq_plus, Tooltip.ClosePolicy.TOUCH_ANYWHERE_NO_CONSUME, Tooltip.Gravity.LEFT, true), SWIPE_AGAIN( R.string.tt_swipe_left_playlist, R.id.viewpager_lwq_settings, Tooltip.ClosePolicy.TOUCH_ANYWHERE_NO_CONSUME, Tooltip.Gravity.CENTER, true), SETTING( R.string.tt_setting_info, R.id.tv_lwq_settings_dim, Tooltip.ClosePolicy.TOUCH_ANYWHERE_NO_CONSUME, Tooltip.Gravity.BOTTOM, false); int stringId; boolean stop; int anchorId; Tooltip.ClosePolicy closePolicy; Tooltip.Gravity gravity; TutorialTooltips(int stringId, int anchorId, Tooltip.ClosePolicy closePolicy) { this(stringId, anchorId, closePolicy, Tooltip.Gravity.TOP, false); } TutorialTooltips(int stringId, int anchorId, Tooltip.ClosePolicy closePolicy, Tooltip.Gravity gravity, boolean stop) { this.stringId = stringId; this.anchorId = anchorId; this.closePolicy = closePolicy; this.gravity = gravity; this.stop = stop; } } void showTutorialTip(TutorialTooltips tooltip) { if (!showTutorialTips) { return; } else if (visibleTips.contains(tooltip)) { return; } // Assume they've seen the tutorial after we reveal the share tip if (tooltip == TutorialTooltips.SHARE) { LWQPreferences.setViewedTutorial(true); AnalyticsUtils.trackEvent(AnalyticsUtils.CATEGORY_TOOLTIPS, AnalyticsUtils.ACTION_STARTED); } showToolTip(tooltip, this); } void showToolTip(Object tooltipObj, Tooltip.Callback callback) { int id = 0; int stringId = 0; int anchorId = 0; Tooltip.ClosePolicy closePolicy = Tooltip.ClosePolicy.TOUCH_ANYWHERE_NO_CONSUME; Tooltip.Gravity gravity = Tooltip.Gravity.BOTTOM; if (tooltipObj instanceof TutorialTooltips) { TutorialTooltips tooltip = (TutorialTooltips) tooltipObj; id = tooltip.ordinal(); stringId = tooltip.stringId; anchorId = tooltip.anchorId; closePolicy = tooltip.closePolicy; gravity = tooltip.gravity; } else { SettingsTooltips tooltip = (SettingsTooltips) tooltipObj; stringId = tooltip.stringId; anchorId = tooltip.anchorId; } Tooltip.Builder builder = new Tooltip.Builder(id).activateDelay(500).closePolicy(closePolicy, 0) .fadeDuration(200).fitToScreen(true).floatingAnimation(Tooltip.AnimationBuilder.SLOW).showDelay(500) .text(getResources(), stringId).withCallback(callback); if (anchorId > 0) { builder.anchor(findById(this, anchorId), gravity); } Tooltip.make(this, builder.build()).show(); } @Override public void onTooltipClose(Tooltip.TooltipView tooltipView, boolean b, boolean b1) { TutorialTooltips[] allTips = TutorialTooltips.values(); // Remove from visible visibleTips.remove(allTips[tooltipView.getTooltipId()]); // Reached the end of the tips? if (allTips.length == tooltipView.getTooltipId() + 1) { showTutorialTips = false; AnalyticsUtils.trackEvent(AnalyticsUtils.CATEGORY_TOOLTIPS, AnalyticsUtils.ACTION_COMPLETED); return; } // Should stop? if (allTips[tooltipView.getTooltipId()].stop) return; // Show the next one! showTutorialTip(allTips[tooltipView.getTooltipId() + 1]); } @Override public void onTooltipFailed(Tooltip.TooltipView tooltipView) { } @Override public void onTooltipShown(Tooltip.TooltipView tooltipView) { visibleTips.add(TutorialTooltips.values()[tooltipView.getTooltipId()]); } @Override public void onTooltipHidden(Tooltip.TooltipView tooltipView) { } }