/* * Copyright (c) 2016 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.samples.apps.iosched.navigation; import android.accounts.Account; import android.content.Intent; import android.graphics.PorterDuff; import android.os.Bundle; import android.os.Handler; import android.support.design.widget.NavigationView; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.widget.ImageView; import android.widget.Spinner; import com.google.samples.apps.iosched.R; import com.google.samples.apps.iosched.login.SwitchUserActivity; import com.google.samples.apps.iosched.navigation.NavigationModel.NavigationItemEnum; import com.google.samples.apps.iosched.settings.SettingsUtils; import com.google.samples.apps.iosched.ui.BaseActivity; import com.google.samples.apps.iosched.util.AccountUtils; import com.google.samples.apps.iosched.util.ImageLoader; import com.google.samples.apps.iosched.util.UIUtils; import java.util.Arrays; import java.util.List; import static com.google.samples.apps.iosched.util.LogUtils.LOGE; import static com.google.samples.apps.iosched.util.LogUtils.makeLogTag; /** * This is the implementation of {@link AppNavigationView} using a {@link DrawerLayout}. This * extends {@link AppNavigationViewAbstractImpl} so only UI specific methods are implemented. */ public class AppNavigationViewAsDrawerImpl extends AppNavigationViewAbstractImpl implements NavigationView.OnNavigationItemSelectedListener { private static final String TAG = makeLogTag(AppNavigationViewAsDrawerImpl.class); // Delay to launch nav drawer item, to allow close animation to play private static final int NAVDRAWER_LAUNCH_DELAY = 250; // Fade in and fade out durations for the main content when switching between // different Activities of the app through the Nav Drawer private static final int MAIN_CONTENT_FADEOUT_DURATION = 150; /** * Key to track whether the {@link #mAccountSpinner} drop down view is visible when saving the * state of this view. */ private static final String ACCOUNT_SPINNER_DROP_DOWN_VISIBLE = "account_spinner_drop_down_visible"; private DrawerLayout mDrawerLayout; // A Runnable that we should execute when the navigation drawer finishes its closing animation private Runnable mDeferredOnDrawerClosedRunnable; private NavigationView mNavigationView; private Spinner mAccountSpinner; private boolean mAccountSpinnerDropDownViewVisible; /** * True if the {@link #mAccountSpinner} should be shown in drop down mode when this view is * first loaded. */ private boolean mSetAccountSpinnerInDropViewModeWhenFirstShown; private AccountSpinnerAdapter mAccountSpinnerAdapter; private Handler mHandler; private ImageLoader mImageLoader; private NavigationDrawerStateListener mNavigationDrawerStateListener; public AppNavigationViewAsDrawerImpl(ImageLoader imageLoader, NavigationDrawerStateListener listener) { mImageLoader = imageLoader; mNavigationDrawerStateListener = listener; } @Override public void displayNavigationItems(final NavigationModel.NavigationItemEnum[] items) { createNavDrawerItems(items); setSelectedNavDrawerItem(mSelfItem); setupAccountBox(); } @Override public boolean onNavigationItemSelected(final MenuItem menuItem) { NavigationItemEnum item = NavigationItemEnum.getById(menuItem.getItemId()); onNavDrawerItemClicked(item); return true; } @Override public void setUpView() { mHandler = new Handler(); mDrawerLayout = (DrawerLayout) mActivity.findViewById(R.id.drawer_layout); if (mDrawerLayout == null) { return; } // setup the status bar color to be colorPrimaryDark from the theme mDrawerLayout.setStatusBarBackgroundColor( UIUtils.getThemeColor(mActivity, R.attr.colorPrimaryDark, R.color.theme_primary_dark)); mNavigationView = (NavigationView) mActivity.findViewById(R.id.nav_view); if (mSelfItem == NavigationModel.NavigationItemEnum.INVALID) { // do not show a nav drawer mDrawerLayout = null; return; } mDrawerLayout.setDrawerListener(new DrawerLayout.DrawerListener() { @Override public void onDrawerClosed(View drawerView) { // run deferred action, if we have one if (mDeferredOnDrawerClosedRunnable != null) { mDeferredOnDrawerClosedRunnable.run(); mDeferredOnDrawerClosedRunnable = null; } mNavigationDrawerStateListener.onNavDrawerStateChanged(false, false); } @Override public void onDrawerOpened(View drawerView) { mNavigationDrawerStateListener.onNavDrawerStateChanged(true, false); } @Override public void onDrawerStateChanged(int newState) { mNavigationDrawerStateListener.onNavDrawerStateChanged(isNavDrawerOpen(), newState != DrawerLayout.STATE_IDLE); } @Override public void onDrawerSlide(View drawerView, float slideOffset) { mNavigationDrawerStateListener.onNavDrawerSlide(slideOffset); } }); mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); // When the user runs the app for the first time, we want to land them with the // navigation drawer open. But just the first time. if (!SettingsUtils.isFirstRunProcessComplete(mActivity)) { // first run of the app starts with the nav drawer open SettingsUtils.markFirstRunProcessesDone(mActivity, true); mDrawerLayout.openDrawer(GravityCompat.START); } setupAccountBox(); } @Override public void showNavigation() { mDrawerLayout.openDrawer(GravityCompat.START); } private void createNavDrawerItems(NavigationModel.NavigationItemEnum[] items) { if (mNavigationView != null) { Menu menu = mNavigationView.getMenu(); for (int i = 0; i < items.length; i++) { MenuItem item = menu.findItem(items[i].getId()); if (item != null) { item.setVisible(true); item.setIcon(items[i].getIconResource()); item.setTitle(items[i].getTitleResource()); } else { LOGE(TAG, "Menu Item for navigation item with title " + (items[i].getTitleResource() != 0 ? mActivity.getResources().getString( items[i].getTitleResource()) : "") + "not found"); } } mNavigationView.setNavigationItemSelectedListener(this); } } /** * Sets up the given navdrawer item's appearance to the selected state. Note: this could also be * accomplished (perhaps more cleanly) with state-based layouts. */ private void setSelectedNavDrawerItem(NavigationItemEnum item) { if (mNavigationView != null && item != NavigationItemEnum.INVALID) { mNavigationView.getMenu().findItem(item.getId()).setChecked(true); } } /** * Sets up the account box. The account box is the area at the top of the nav drawer that shows * which account the user is logged in as, and lets them switch accounts. It also shows the * user's Google+ cover photo as background. */ private void setupAccountBox() { final View chosenAccountView = mActivity.findViewById(R.id.chosen_account_view); if (chosenAccountView == null) { //This activity does not have an account box return; } Account chosenAccount = AccountUtils.getActiveAccount(mActivity); if (chosenAccount == null) { // No account logged in; hide account box chosenAccountView.setVisibility(View.GONE); return; } else { chosenAccountView.setVisibility(View.VISIBLE); } ImageView coverImageView = (ImageView) chosenAccountView .findViewById(R.id.profile_cover_image); ImageView profileImageView = (ImageView) chosenAccountView.findViewById(R.id.profile_image); String imageUrl = AccountUtils.getPlusImageUrl(mActivity); if (imageUrl != null) { mImageLoader.loadImage(imageUrl, profileImageView); } String coverImageUrl = AccountUtils.getPlusCoverUrl(mActivity); if (coverImageUrl != null) { mActivity.findViewById(R.id.profile_cover_image_placeholder).setVisibility(View.GONE); coverImageView.setVisibility(View.VISIBLE); coverImageView.setContentDescription(mActivity.getResources().getString( R.string.navview_header_user_image_content_description)); mImageLoader.loadImage(coverImageUrl, coverImageView); coverImageView .setColorFilter(mActivity.getResources().getColor(R.color.light_content_scrim)); } else { mActivity.findViewById(R.id.profile_cover_image_placeholder) .setVisibility(View.VISIBLE); coverImageView.setVisibility(View.GONE); } List<Account> accounts = Arrays.asList(AccountUtils.getActiveAccount(getContext())); populateAccountList(accounts); } /** * @param accounts The list of available accounts, the current one being at position 0. */ private void populateAccountList(final List<Account> accounts) { mAccountSpinner = (Spinner) mActivity.findViewById(R.id.account_spinner); mAccountSpinner.getBackground().setColorFilter( mActivity.getResources().getColor(R.color.body_text_1_inverse), PorterDuff.Mode.SRC_ATOP); mAccountSpinnerAdapter = new AccountSpinnerAdapter(mActivity, R.id.profile_name_text, accounts.toArray(new Account[accounts.size()]), mImageLoader); mAccountSpinner.setAdapter(mAccountSpinnerAdapter); mAccountSpinner.setSelection(0); mAccountSpinner.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(final View v, final MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { Intent switchUser = new Intent(getContext(), SwitchUserActivity.class); mActivity.startActivityForResult(switchUser, BaseActivity.SWITCH_USER_RESULT); return true; } return false; } }); mAccountSpinner.setEnabled(true); } public boolean isNavDrawerOpen() { return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START); } public void closeNavDrawer() { if (mDrawerLayout != null) { mDrawerLayout.closeDrawer(GravityCompat.START); } } public DrawerLayout getDrawerLayout() { return mDrawerLayout; } private void onNavDrawerItemClicked(final NavigationItemEnum item) { if (item == mSelfItem) { mDrawerLayout.closeDrawer(GravityCompat.START); return; } if (isSpecialItem(item)) { itemSelected(item); } else { // Launch the target Activity after a short delay, to allow the close animation to play mHandler.postDelayed(new Runnable() { @Override public void run() { itemSelected(item); } }, NAVDRAWER_LAUNCH_DELAY); // Change the active item on the list so the user can see the item changed setSelectedNavDrawerItem(item); // Fade out the main content View mainContent = mActivity.findViewById(R.id.main_content); if (mainContent != null) { mainContent.animate().alpha(0).setDuration(MAIN_CONTENT_FADEOUT_DURATION); } } // The navigation menu is accessible from some screens, via swiping, without a drawer // layout, eg MapActivity when accessed from SessionDetailsActivity. if (mDrawerLayout != null) { mDrawerLayout.closeDrawer(GravityCompat.START); } } private boolean isSpecialItem(NavigationItemEnum item) { return item == NavigationItemEnum.SETTINGS; } public void markAccountSpinnerAsNotShowingDropDownView() { mAccountSpinnerDropDownViewVisible = false; } public void onSaveInstanceState(Bundle outState) { if (mAccountSpinnerAdapter != null) { outState.putBoolean(ACCOUNT_SPINNER_DROP_DOWN_VISIBLE, mAccountSpinnerDropDownViewVisible); } } public void onRestoreInstanceState(Bundle savedInstanceState) { if (savedInstanceState != null && savedInstanceState.containsKey(ACCOUNT_SPINNER_DROP_DOWN_VISIBLE)) { mSetAccountSpinnerInDropViewModeWhenFirstShown = savedInstanceState.getBoolean(ACCOUNT_SPINNER_DROP_DOWN_VISIBLE); } } public interface NavigationDrawerStateListener { void onNavDrawerStateChanged(boolean isOpen, boolean isAnimating); void onNavDrawerSlide(float slideOffset); } }