Java tutorial
/* * Copyright 2014 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.android.apps.muzei; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.annotation.TargetApi; import android.app.WallpaperManager; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.graphics.Rect; import android.graphics.drawable.AnimatedVectorDrawable; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; import android.util.Log; import android.util.SparseIntArray; import android.util.TypedValue; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; import android.widget.ImageView; import android.widget.PopupMenu; import android.widget.TextView; import android.widget.Toast; import com.google.android.apps.muzei.api.Artwork; import com.google.android.apps.muzei.api.MuzeiArtSource; import com.google.android.apps.muzei.api.MuzeiContract; import com.google.android.apps.muzei.api.UserCommand; import com.google.android.apps.muzei.event.ArtDetailOpenedClosedEvent; import com.google.android.apps.muzei.event.ArtworkLoadingStateChangedEvent; import com.google.android.apps.muzei.event.ArtworkSizeChangedEvent; import com.google.android.apps.muzei.event.SwitchingPhotosStateChangedEvent; import com.google.android.apps.muzei.event.WallpaperActiveStateChangedEvent; import com.google.android.apps.muzei.event.WallpaperSizeChangedEvent; import com.google.android.apps.muzei.render.MuzeiRendererFragment; import com.google.android.apps.muzei.settings.SettingsActivity; import com.google.android.apps.muzei.sync.TaskQueueService; import com.google.android.apps.muzei.util.AnimatedMuzeiLoadingSpinnerView; import com.google.android.apps.muzei.util.AnimatedMuzeiLogoFragment; import com.google.android.apps.muzei.util.CheatSheet; import com.google.android.apps.muzei.util.DrawInsetsFrameLayout; import com.google.android.apps.muzei.util.PanScaleProxyView; import com.google.android.apps.muzei.util.ScrimUtil; import com.google.android.apps.muzei.util.TypefaceUtil; import net.nurik.roman.muzei.R; import java.util.List; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; public class MuzeiActivity extends AppCompatActivity { private static final String TAG = "MuzeiActivity"; private static final String PREF_SEEN_TUTORIAL = "seen_tutorial"; // Controller/logic fields private SourceManager mSourceManager; private int mCurrentViewportId = 0; private float mWallpaperAspectRatio; private float mArtworkAspectRatio; public boolean mSupportsNextArtwork = false; private LoaderManager.LoaderCallbacks<Cursor> mSourceLoaderCallbacks = new LoaderManager.LoaderCallbacks<Cursor>() { @Override public Loader<Cursor> onCreateLoader(final int id, final Bundle args) { return new CursorLoader(MuzeiActivity.this, MuzeiContract.Sources.CONTENT_URI, new String[] { MuzeiContract.Sources.COLUMN_NAME_SUPPORTS_NEXT_ARTWORK_COMMAND, MuzeiContract.Sources.COLUMN_NAME_COMMANDS }, MuzeiContract.Sources.COLUMN_NAME_IS_SELECTED + "=1", null, null); } @Override public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) { // Update overflow and next button mOverflowSourceActionMap.clear(); mOverflowMenu.getMenu().clear(); mOverflowMenu.inflate(R.menu.muzei_overflow); if (data.moveToFirst()) { mSupportsNextArtwork = data.getInt(0) != 0; List<UserCommand> commands = MuzeiContract.Sources.parseCommands(data.getString(1)); int numSourceActions = Math.min(SOURCE_ACTION_IDS.length, commands.size()); for (int i = 0; i < numSourceActions; i++) { UserCommand action = commands.get(i); mOverflowSourceActionMap.put(SOURCE_ACTION_IDS[i], action.getId()); mOverflowMenu.getMenu().add(0, SOURCE_ACTION_IDS[i], 0, action.getTitle()); } } mNextButton.setVisibility(mSupportsNextArtwork && !mArtworkLoading ? View.VISIBLE : View.GONE); } @Override public void onLoaderReset(final Loader<Cursor> loader) { } }; private LoaderManager.LoaderCallbacks<Cursor> mArtworkLoaderCallbacks = new LoaderManager.LoaderCallbacks<Cursor>() { @Override public Loader<Cursor> onCreateLoader(final int id, final Bundle args) { return new CursorLoader(MuzeiActivity.this, MuzeiContract.Artwork.CONTENT_URI, null, null, null, null); } @Override public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) { if (!data.moveToFirst()) { return; } Artwork currentArtwork = Artwork.fromCursor(data); String titleFont = "AlegreyaSans-Black.ttf"; String bylineFont = "AlegreyaSans-Medium.ttf"; if (Artwork.FONT_TYPE_ELEGANT.equals(currentArtwork.getMetaFont())) { titleFont = "Alegreya-BlackItalic.ttf"; bylineFont = "Alegreya-Italic.ttf"; } mTitleView.setTypeface(TypefaceUtil.getAndCache(MuzeiActivity.this, titleFont)); mTitleView.setText(currentArtwork.getTitle()); mBylineView.setTypeface(TypefaceUtil.getAndCache(MuzeiActivity.this, bylineFont)); mBylineView.setText(currentArtwork.getByline()); String attribution = currentArtwork.getAttribution(); if (!TextUtils.isEmpty(attribution)) { mAttributionView.setText(attribution); mAttributionView.setVisibility(View.VISIBLE); } else { mAttributionView.setVisibility(View.GONE); } final Intent viewIntent = currentArtwork.getViewIntent(); mMetadataView.setEnabled(viewIntent != null); if (viewIntent != null) { mMetadataView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { viewIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Make sure any data URIs granted to Muzei are passed onto the // started Activity viewIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); try { startActivity(viewIntent); } catch (ActivityNotFoundException | SecurityException e) { Toast.makeText(MuzeiActivity.this, R.string.error_view_details, Toast.LENGTH_SHORT) .show(); Log.e(TAG, "Error viewing artwork details.", e); } } }); } else { mMetadataView.setOnClickListener(null); } } @Override public void onLoaderReset(final Loader<Cursor> loader) { } }; // UI flags private int mUiMode = UI_MODE_ART_DETAIL; private static final int UI_MODE_ART_DETAIL = 0; private static final int UI_MODE_INTRO = 1; // not active wallpaper private static final int UI_MODE_TUTORIAL = 2; // active wallpaper, but first time private static final int LOAD_ERROR_COUNT_EASTER_EGG = 4; private boolean mPaused; private boolean mWindowHasFocus; private boolean mOverflowMenuVisible = false; private boolean mWallpaperActive; private boolean mSeenTutorial; private boolean mGuardViewportChangeListener; private boolean mDeferResetViewport; // UI private Handler mHandler = new Handler(); private DrawInsetsFrameLayout mContainerView; private PopupMenu mOverflowMenu; private SparseIntArray mOverflowSourceActionMap = new SparseIntArray(); private static final int[] SOURCE_ACTION_IDS = { R.id.source_action_1, R.id.source_action_2, R.id.source_action_3, R.id.source_action_4, R.id.source_action_5, R.id.source_action_6, R.id.source_action_7, R.id.source_action_8, R.id.source_action_9, R.id.source_action_10, }; // Normal mode UI private View mChromeContainerView; private View mStatusBarScrimView; private View mMetadataView; private View mLoadingContainerView; private View mLoadErrorContainerView; private View mLoadErrorEasterEggView; private AnimatedMuzeiLoadingSpinnerView mLoadingIndicatorView; private View mNextButton; private TextView mTitleView; private TextView mBylineView; private TextView mAttributionView; private PanScaleProxyView mPanScaleProxyView; private boolean mArtworkLoading = false; private boolean mArtworkLoadingError = false; private boolean mLoadingSpinnerShown = false; private boolean mLoadErrorShown = false; private boolean mNextFakeLoading = false; private int mConsecutiveLoadErrorCount = 0; // Tutorial mode UI private View mTutorialContainerView; // Intro mode UI private ViewGroup mIntroContainerView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.muzei_activity); mContainerView = (DrawInsetsFrameLayout) findViewById(R.id.container); setupArtDetailModeUi(); setupIntroModeUi(); setupTutorialModeUi(); mContainerView.setOnInsetsCallback(new DrawInsetsFrameLayout.OnInsetsCallback() { @Override public void onInsetsChanged(Rect insets) { mChromeContainerView.setPadding(insets.left, insets.top, insets.right, insets.bottom); if (mTutorialContainerView != null) { mTutorialContainerView.setPadding(insets.left, insets.top, insets.right, insets.bottom); } } }); showHideChrome(true); mSourceManager = SourceManager.getInstance(this); EventBus.getDefault().register(this); WallpaperSizeChangedEvent wsce = EventBus.getDefault().getStickyEvent(WallpaperSizeChangedEvent.class); if (wsce != null) { onEventMainThread(wsce); } ArtworkSizeChangedEvent asce = EventBus.getDefault().getStickyEvent(ArtworkSizeChangedEvent.class); if (asce != null) { onEventMainThread(asce); } ArtworkLoadingStateChangedEvent alsce = EventBus.getDefault() .getStickyEvent(ArtworkLoadingStateChangedEvent.class); if (alsce != null) { onEventMainThread(alsce); } ArtDetailViewport fve = EventBus.getDefault().getStickyEvent(ArtDetailViewport.class); if (fve != null) { onEventMainThread(fve); } SwitchingPhotosStateChangedEvent spsce = EventBus.getDefault() .getStickyEvent(SwitchingPhotosStateChangedEvent.class); if (spsce != null) { onEventMainThread(spsce); } getSupportLoaderManager().initLoader(0, null, mSourceLoaderCallbacks); getSupportLoaderManager().initLoader(1, null, mArtworkLoaderCallbacks); } private void setupIntroModeUi() { mIntroContainerView = (ViewGroup) findViewById(R.id.intro_container); findViewById(R.id.activate_muzei_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { try { startActivity(new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER) .putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, new ComponentName(MuzeiActivity.this, MuzeiWallpaperService.class)) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); } catch (ActivityNotFoundException e) { try { startActivity(new Intent(WallpaperManager.ACTION_LIVE_WALLPAPER_CHOOSER) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); } catch (ActivityNotFoundException e2) { Toast.makeText(MuzeiActivity.this, R.string.error_wallpaper_chooser, Toast.LENGTH_LONG) .show(); } } } }); } private void setupTutorialModeUi() { final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); mSeenTutorial = sp.getBoolean(PREF_SEEN_TUTORIAL, false); if (mSeenTutorial) { return; } mTutorialContainerView = LayoutInflater.from(this).inflate(R.layout.muzei_include_tutorial, mContainerView, false); mTutorialContainerView.findViewById(R.id.tutorial_icon_affordance) .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mSeenTutorial = true; updateUiMode(); sp.edit().putBoolean(PREF_SEEN_TUTORIAL, mSeenTutorial).apply(); } }); mContainerView.addView(mTutorialContainerView); } private View getMainContainerForUiMode(int mode) { switch (mode) { case UI_MODE_ART_DETAIL: return mChromeContainerView; case UI_MODE_INTRO: return mIntroContainerView; case UI_MODE_TUTORIAL: return mTutorialContainerView; } return null; } private void updateUiMode() { // TODO: this should really just use fragment transactions and transitions int newUiMode = UI_MODE_INTRO; if (mWallpaperActive) { newUiMode = UI_MODE_TUTORIAL; if (mSeenTutorial) { newUiMode = UI_MODE_ART_DETAIL; } } if (mUiMode == newUiMode) { return; } // Crossfade between main containers final View oldContainerView = getMainContainerForUiMode(mUiMode); final View newContainerView = getMainContainerForUiMode(newUiMode); if (oldContainerView != null) { oldContainerView.animate().alpha(0).setDuration(1000).withEndAction(new Runnable() { @Override public void run() { oldContainerView.setVisibility(View.GONE); } }); } if (newContainerView != null) { if (newContainerView.getAlpha() == 1) { newContainerView.setAlpha(0); } newContainerView.setVisibility(View.VISIBLE); newContainerView.animate().alpha(1).setDuration(1000).withEndAction(null); } // Special work if (newUiMode == UI_MODE_INTRO) { final View activateButton = findViewById(R.id.activate_muzei_button); activateButton.setAlpha(0); final AnimatedMuzeiLogoFragment logoFragment = (AnimatedMuzeiLogoFragment) getFragmentManager() .findFragmentById(R.id.animated_logo_fragment); logoFragment.reset(); logoFragment.setOnFillStartedCallback(new Runnable() { @Override public void run() { activateButton.animate().alpha(1f).setDuration(500); } }); mHandler.postDelayed(new Runnable() { @Override public void run() { logoFragment.start(); } }, 1000); } if (mUiMode == UI_MODE_INTRO || newUiMode == UI_MODE_INTRO) { FragmentManager fm = getSupportFragmentManager(); Fragment demoFragment = fm.findFragmentById(R.id.demo_view_container); if (newUiMode == UI_MODE_INTRO && demoFragment == null) { fm.beginTransaction() .add(R.id.demo_view_container, MuzeiRendererFragment.createInstance(true, true)).commit(); } else if (newUiMode != UI_MODE_INTRO && demoFragment != null) { fm.beginTransaction().remove(demoFragment).commit(); } } if (newUiMode == UI_MODE_TUTORIAL) { float animateDistance = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, getResources().getDisplayMetrics()); View mainTextView = findViewById(R.id.tutorial_main_text); mainTextView.setAlpha(0); mainTextView.setTranslationY(-animateDistance / 5); View subTextView = findViewById(R.id.tutorial_sub_text); subTextView.setAlpha(0); subTextView.setTranslationY(-animateDistance / 5); final View affordanceView = findViewById(R.id.tutorial_icon_affordance); affordanceView.setAlpha(0); affordanceView.setTranslationY(animateDistance); View iconTextView = findViewById(R.id.tutorial_icon_text); iconTextView.setAlpha(0); iconTextView.setTranslationY(animateDistance); AnimatorSet set = new AnimatorSet(); set.setStartDelay(500); set.setDuration(250); set.playTogether(ObjectAnimator.ofFloat(mainTextView, View.ALPHA, 1f), ObjectAnimator.ofFloat(subTextView, View.ALPHA, 1f)); set.start(); set = new AnimatorSet(); set.setStartDelay(2000); // Bug in older versions where set.setInterpolator didn't work Interpolator interpolator = new OvershootInterpolator(); ObjectAnimator a1 = ObjectAnimator.ofFloat(affordanceView, View.TRANSLATION_Y, 0); ObjectAnimator a2 = ObjectAnimator.ofFloat(iconTextView, View.TRANSLATION_Y, 0); ObjectAnimator a3 = ObjectAnimator.ofFloat(mainTextView, View.TRANSLATION_Y, 0); ObjectAnimator a4 = ObjectAnimator.ofFloat(subTextView, View.TRANSLATION_Y, 0); a1.setInterpolator(interpolator); a2.setInterpolator(interpolator); a3.setInterpolator(interpolator); a4.setInterpolator(interpolator); set.setDuration(500).playTogether(ObjectAnimator.ofFloat(affordanceView, View.ALPHA, 1f), ObjectAnimator.ofFloat(iconTextView, View.ALPHA, 1f), a1, a2, a3, a4); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { set.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { ImageView emanateView = (ImageView) findViewById(R.id.tutorial_icon_emanate); AnimatedVectorDrawable avd = (AnimatedVectorDrawable) getResources() .getDrawable(R.drawable.avd_tutorial_icon_emanate, getTheme()); emanateView.setImageDrawable(avd); avd.start(); } }); } set.start(); } mPanScaleProxyView.setVisibility(newUiMode == UI_MODE_ART_DETAIL ? View.VISIBLE : View.GONE); mUiMode = newUiMode; maybeUpdateArtDetailOpenedClosed(); } private void maybeUpdateArtDetailOpenedClosed() { boolean currentlyOpened = false; ArtDetailOpenedClosedEvent adoce = EventBus.getDefault().getStickyEvent(ArtDetailOpenedClosedEvent.class); if (adoce != null) { currentlyOpened = adoce.isArtDetailOpened(); } boolean shouldBeOpened = false; if (mUiMode == UI_MODE_ART_DETAIL // && !mArtworkLoading // uncomment when this wouldn't cause // // a zoom out / in visual glitch && (mWindowHasFocus || mOverflowMenuVisible) && !mPaused) { shouldBeOpened = true; } if (currentlyOpened != shouldBeOpened) { EventBus.getDefault().postSticky(new ArtDetailOpenedClosedEvent(shouldBeOpened)); } } private void setupArtDetailModeUi() { mChromeContainerView = findViewById(R.id.chrome_container); mStatusBarScrimView = findViewById(R.id.statusbar_scrim); mChromeContainerView.setBackground(ScrimUtil.makeCubicGradientScrimDrawable(0xaa000000, 8, Gravity.BOTTOM)); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { mStatusBarScrimView.setVisibility(View.GONE); mStatusBarScrimView = null; } else { mStatusBarScrimView.setBackground(ScrimUtil.makeCubicGradientScrimDrawable(0x44000000, 8, Gravity.TOP)); } mMetadataView = findViewById(R.id.metadata); final float metadataSlideDistance = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, getResources().getDisplayMetrics()); mContainerView.setOnSystemUiVisibilityChangeListener(new View.OnSystemUiVisibilityChangeListener() { @Override public void onSystemUiVisibilityChange(int vis) { final boolean visible = (vis & View.SYSTEM_UI_FLAG_LOW_PROFILE) == 0; boolean showArtDetailChrome = (mUiMode == UI_MODE_ART_DETAIL); mChromeContainerView.setVisibility(showArtDetailChrome ? View.VISIBLE : View.GONE); mChromeContainerView.animate().alpha(visible ? 1f : 0f) .translationY(visible ? 0 : metadataSlideDistance).setDuration(200) .withEndAction(new Runnable() { @Override public void run() { if (!visible) { mChromeContainerView.setVisibility(View.GONE); } } }); if (mStatusBarScrimView != null) { mStatusBarScrimView.setVisibility(showArtDetailChrome ? View.VISIBLE : View.GONE); mStatusBarScrimView.animate().alpha(visible ? 1f : 0f).setDuration(200) .withEndAction(new Runnable() { @Override public void run() { if (!visible) { mStatusBarScrimView.setVisibility(View.GONE); } } }); } } }); mTitleView = (TextView) findViewById(R.id.title); mBylineView = (TextView) findViewById(R.id.byline); mAttributionView = (TextView) findViewById(R.id.attribution); setupOverflowButton(); mNextButton = findViewById(R.id.next_button); mNextButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mSourceManager.sendAction(MuzeiArtSource.BUILTIN_COMMAND_ID_NEXT_ARTWORK); mNextFakeLoading = true; showNextFakeLoading(); } }); CheatSheet.setup(mNextButton); mPanScaleProxyView = (PanScaleProxyView) findViewById(R.id.pan_scale_proxy); mPanScaleProxyView.setMaxZoom(5); mPanScaleProxyView.setOnViewportChangedListener(new PanScaleProxyView.OnViewportChangedListener() { @Override public void onViewportChanged() { if (mGuardViewportChangeListener) { return; } ArtDetailViewport.getInstance().setViewport(mCurrentViewportId, mPanScaleProxyView.getCurrentViewport(), true); } }); mPanScaleProxyView.setOnOtherGestureListener(new PanScaleProxyView.OnOtherGestureListener() { @Override public void onSingleTapUp() { if (mUiMode == UI_MODE_ART_DETAIL) { showHideChrome((mContainerView.getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LOW_PROFILE) != 0); } } }); mLoadingContainerView = findViewById(R.id.image_loading_container); mLoadingIndicatorView = (AnimatedMuzeiLoadingSpinnerView) findViewById(R.id.image_loading_indicator); mLoadErrorContainerView = findViewById(R.id.image_error_container); mLoadErrorEasterEggView = findViewById(R.id.error_easter_egg); findViewById(R.id.image_error_retry_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { showNextFakeLoading(); startService(TaskQueueService.getDownloadCurrentArtworkIntent(MuzeiActivity.this)); } }); } @TargetApi(Build.VERSION_CODES.KITKAT) private void setupOverflowButton() { final View overflowButton = findViewById(R.id.overflow_button); mOverflowMenu = new PopupMenu(this, overflowButton); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { overflowButton.setOnTouchListener(mOverflowMenu.getDragToOpenListener()); } overflowButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mOverflowMenuVisible = true; mOverflowMenu.show(); } }); mOverflowMenu.setOnDismissListener(new PopupMenu.OnDismissListener() { @Override public void onDismiss(PopupMenu popupMenu) { mOverflowMenuVisible = false; } }); mOverflowMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem menuItem) { int id = mOverflowSourceActionMap.get(menuItem.getItemId()); if (id > 0) { mSourceManager.sendAction(id); return true; } switch (menuItem.getItemId()) { case R.id.action_settings: startActivity(new Intent(MuzeiActivity.this, SettingsActivity.class)); return true; } return false; } }); } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_MENU: if (mUiMode == UI_MODE_ART_DETAIL) { mOverflowMenuVisible = true; mOverflowMenu.show(); } return true; } return super.onKeyUp(keyCode, event); } @Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); EventBus.getDefault().unregister(this); } private void showHideChrome(boolean show) { int flags = show ? 0 : View.SYSTEM_UI_FLAG_LOW_PROFILE; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { flags |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; if (!show) { flags |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE; } } mContainerView.setSystemUiVisibility(flags); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); mWindowHasFocus = hasFocus; maybeUpdateArtDetailOpenedClosed(); } @Subscribe public void onEventMainThread(WallpaperSizeChangedEvent wsce) { if (wsce.getHeight() > 0) { mWallpaperAspectRatio = wsce.getWidth() * 1f / wsce.getHeight(); } else { mWallpaperAspectRatio = mPanScaleProxyView.getWidth() * 1f / mPanScaleProxyView.getHeight(); } resetProxyViewport(); } @Subscribe public void onEventMainThread(ArtworkSizeChangedEvent ase) { mArtworkAspectRatio = ase.getWidth() * 1f / ase.getHeight(); resetProxyViewport(); } private void resetProxyViewport() { if (mWallpaperAspectRatio == 0 || mArtworkAspectRatio == 0) { return; } mDeferResetViewport = false; SwitchingPhotosStateChangedEvent spe = EventBus.getDefault() .getStickyEvent(SwitchingPhotosStateChangedEvent.class); if (spe != null && spe.isSwitchingPhotos()) { mDeferResetViewport = true; return; } if (mPanScaleProxyView != null) { mPanScaleProxyView.setRelativeAspectRatio(mArtworkAspectRatio / mWallpaperAspectRatio); } } @Subscribe public void onEventMainThread(ArtDetailViewport e) { if (!e.isFromUser() && mPanScaleProxyView != null) { mGuardViewportChangeListener = true; mPanScaleProxyView.setViewport(e.getViewport(mCurrentViewportId)); mGuardViewportChangeListener = false; } } @Subscribe public void onEventMainThread(SwitchingPhotosStateChangedEvent spe) { mCurrentViewportId = spe.getCurrentId(); mPanScaleProxyView.enablePanScale(!spe.isSwitchingPhotos()); // Process deferred artwork size change when done switching if (!spe.isSwitchingPhotos() && mDeferResetViewport) { resetProxyViewport(); } } @Override protected void onPause() { super.onPause(); mPaused = true; maybeUpdateArtDetailOpenedClosed(); } @Override protected void onStop() { super.onStop(); mOverflowMenu.dismiss(); } @Override protected void onPostResume() { super.onPostResume(); mPaused = false; mConsecutiveLoadErrorCount = 0; // update intro mode UI to latest wallpaper active state WallpaperActiveStateChangedEvent e = EventBus.getDefault() .getStickyEvent(WallpaperActiveStateChangedEvent.class); if (e != null) { onEventMainThread(e); } else { onEventMainThread(new WallpaperActiveStateChangedEvent(false)); } updateUiMode(); mChromeContainerView.setVisibility((mUiMode == UI_MODE_ART_DETAIL) ? View.VISIBLE : View.GONE); if (mStatusBarScrimView != null) { mStatusBarScrimView.setVisibility((mUiMode == UI_MODE_ART_DETAIL) ? View.VISIBLE : View.GONE); } // Note: normally should use window animations for this, but there's a bug // on Samsung devices where the wallpaper is animated along with the window for // windows showing the wallpaper (the wallpaper _should_ be static, not part of // the animation). View decorView = getWindow().getDecorView(); decorView.setAlpha(0f); decorView.animate().cancel(); decorView.animate().setStartDelay(500).alpha(1f).setDuration(300); maybeUpdateArtDetailOpenedClosed(); NewWallpaperNotificationReceiver.markNotificationRead(this); } @Subscribe public void onEventMainThread(final WallpaperActiveStateChangedEvent e) { if (mPaused) { return; } mWallpaperActive = e.isActive(); updateUiMode(); } @Subscribe public void onEventMainThread(ArtworkLoadingStateChangedEvent e) { mArtworkLoading = e.isLoading(); mArtworkLoadingError = e.hadError(); if (!mArtworkLoading) { mNextFakeLoading = false; if (!mArtworkLoadingError) { mConsecutiveLoadErrorCount = 0; } } // Artwork no longer loading, update the visibility of the next button mNextButton.setVisibility(mSupportsNextArtwork && !mArtworkLoading ? View.VISIBLE : View.GONE); if (mUiMode == UI_MODE_ART_DETAIL) { maybeUpdateArtDetailOpenedClosed(); } updateLoadingSpinnerAndErrorVisibility(); } private void showNextFakeLoading() { mNextFakeLoading = true; // Show a loading spinner for up to 10 seconds. When new artwork is loaded, // the loading spinner will go away. See onEventMainThread(ArtworkLoadingStateChangedEvent) mHandler.removeCallbacks(mUnsetNextFakeLoadingRunnable); mHandler.postDelayed(mUnsetNextFakeLoadingRunnable, 10000); updateLoadingSpinnerAndErrorVisibility(); } private Runnable mUnsetNextFakeLoadingRunnable = new Runnable() { @Override public void run() { mNextFakeLoading = false; updateLoadingSpinnerAndErrorVisibility(); } }; private void updateLoadingSpinnerAndErrorVisibility() { boolean showLoadingSpinner = (mUiMode == UI_MODE_ART_DETAIL && (mArtworkLoading || mNextFakeLoading)); boolean showError = !showLoadingSpinner && mArtworkLoadingError; if (showLoadingSpinner != mLoadingSpinnerShown) { mLoadingSpinnerShown = showLoadingSpinner; mHandler.removeCallbacks(mShowLoadingSpinnerRunnable); if (showLoadingSpinner) { mHandler.postDelayed(mShowLoadingSpinnerRunnable, 700); } else { mLoadingContainerView.animate().alpha(0).setDuration(1000).withEndAction(new Runnable() { @Override public void run() { mLoadingContainerView.setVisibility(View.GONE); mLoadingIndicatorView.stop(); } }); } } if (showError != mLoadErrorShown) { mLoadErrorShown = showError; mHandler.removeCallbacks(mShowLoadErrorRunnable); if (showError) { mHandler.postDelayed(mShowLoadErrorRunnable, 700); } else { mLoadErrorContainerView.animate().alpha(0).setDuration(1000).withEndAction(new Runnable() { @Override public void run() { mLoadErrorContainerView.setVisibility(View.GONE); } }); } } } private Runnable mShowLoadingSpinnerRunnable = new Runnable() { @Override public void run() { mLoadingIndicatorView.start(); mLoadingContainerView.setVisibility(View.VISIBLE); mLoadingContainerView.animate().alpha(1).setDuration(300).withEndAction(null); } }; private Runnable mShowLoadErrorRunnable = new Runnable() { @Override public void run() { ++mConsecutiveLoadErrorCount; mLoadErrorEasterEggView.setVisibility( (mConsecutiveLoadErrorCount >= LOAD_ERROR_COUNT_EASTER_EGG) ? View.VISIBLE : View.GONE); mLoadErrorContainerView.setVisibility(View.VISIBLE); mLoadErrorContainerView.animate().alpha(1).setDuration(300).withEndAction(null); } }; }