/* * Copyright 2014 Google Inc., University of South Florida (sjbarbeau@gmail.com) All rights reserved. * * 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. * * Portions of code taken from the Google I/0 2014 (https://github.com/google/iosched) * and a generated NavigationDrawer app from Android Studio, modified for OneBusAway by USF */ package org.onebusaway.android.ui; import org.onebusaway.android.R; import org.onebusaway.android.app.Application; import org.onebusaway.android.util.UIUtils; import org.onebusaway.android.view.ScrimInsetsScrollView; import android.content.Context; import android.content.SharedPreferences; import android.content.res.Configuration; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v4.app.Fragment; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import java.util.ArrayList; /** * Fragment used for managing interactions for and presentation of a navigation drawer. * See the <a href="https://developer.android.com/design/patterns/navigation-drawer.html#Interaction"> * design guidelines</a> for a complete explanation of the behaviors implemented here. */ public class NavigationDrawerFragment extends Fragment { public static final String TAG = "NavDrawerFragment"; /** * Remember the position of the selected item. */ private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position"; // symbols for navdrawer items (indices must correspond to array below). This is // not a list of items that are necessarily *present* in the Nav Drawer; rather, // it's a list of all possible items. protected static final int NAVDRAWER_ITEM_NEARBY = 0; protected static final int NAVDRAWER_ITEM_STARRED_STOPS = 1; protected static final int NAVDRAWER_ITEM_MY_REMINDERS = 2; protected static final int NAVDRAWER_ITEM_SETTINGS = 3; protected static final int NAVDRAWER_ITEM_HELP = 4; protected static final int NAVDRAWER_ITEM_SEND_FEEDBACK = 5; protected static final int NAVDRAWER_ITEM_PLAN_TRIP = 6; protected static final int NAVDRAWER_ITEM_INVALID = -1; protected static final int NAVDRAWER_ITEM_SEPARATOR = -2; protected static final int NAVDRAWER_ITEM_SEPARATOR_SPECIAL = -3; // Currently selected navigation drawer item (must be value of one of the constants above) private int mCurrentSelectedPosition = 0; // titles for navdrawer items (indices must correspond to the above) private static final int[] NAVDRAWER_TITLE_RES_ID = new int[]{ R.string.navdrawer_item_nearby, R.string.navdrawer_item_starred_stops, R.string.navdrawer_item_my_reminders, R.string.navdrawer_item_settings, R.string.navdrawer_item_help, R.string.navdrawer_item_send_feedback, R.string.navdrawer_item_plan_trip }; // icons for navdrawer items (indices must correspond to above array) private static final int[] NAVDRAWER_ICON_RES_ID = new int[]{ R.drawable.ic_drawer_maps_place, // Nearby R.drawable.ic_drawer_star, // Starred Stops R.drawable.ic_drawer_alarm, // My reminders 0, // Settings 0, // Help 0, // Send feedback R.drawable.ic_maps_directions // Plan a trip }; // list of navdrawer items that were actually added to the navdrawer, in order private ArrayList<Integer> mNavDrawerItems = new ArrayList<Integer>(); // views that correspond to each navdrawer item, null if not yet created private View[] mNavDrawerItemViews = null; /** * A pointer to the current callbacks instance (the Activity). */ private NavigationDrawerCallbacks mCallbacks; /** * Helper component that ties the action bar to the navigation drawer. */ private ActionBarDrawerToggle mDrawerToggle; // Navigation drawer: private DrawerLayout mDrawerLayout; private View mDrawerItemsListContainer; private View mFragmentContainerView; public NavigationDrawerFragment() { } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Read in the flag indicating whether or not the user has demonstrated awareness of the // drawer. See PREF_USER_LEARNED_DRAWER for details. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity()); if (savedInstanceState != null) { mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION); Log.d(TAG, "Using position from savedInstanceState = " + mCurrentSelectedPosition); } else { // Try to get the saved position from preferences mCurrentSelectedPosition = sp.getInt(STATE_SELECTED_POSITION, NAVDRAWER_ITEM_NEARBY); Log.d(TAG, "Using position from preferences = " + mCurrentSelectedPosition); } // Select either the default item (0) or the last selected item. selectItem(mCurrentSelectedPosition); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // Indicate that this fragment would like to influence the set of actions in the action bar. setHasOptionsMenu(true); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mDrawerItemsListContainer = inflater .inflate(R.layout.navdrawer_list, container, false); return mDrawerItemsListContainer; } /** * Users of this fragment must call this method to set up the navigation drawer interactions. * * @param fragmentId The android:id of this fragment in its activity's layout. * @param drawerLayout The DrawerLayout containing this fragment's UI. */ public void setUp(int fragmentId, DrawerLayout drawerLayout) { int selfItem = mCurrentSelectedPosition; mFragmentContainerView = getActivity().findViewById(fragmentId); mDrawerLayout = drawerLayout; if (mDrawerLayout == null) { return; } // set a custom shadow that overlays the main content when the drawer opens mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); ScrimInsetsScrollView navDrawer = (ScrimInsetsScrollView) mDrawerLayout.findViewById(R.id.navdrawer); if (selfItem == NAVDRAWER_ITEM_INVALID) { // do not show a nav drawer if (navDrawer != null) { ((ViewGroup) navDrawer.getParent()).removeView(navDrawer); } mDrawerLayout = null; return; } // populate the nav drawer with the correct items populateNavDrawer(); ActionBar actionBar = getActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeButtonEnabled(true); // ActionBarDrawerToggle ties together the the proper interactions // between the navigation drawer and the action bar app icon. mDrawerToggle = new android.support.v7.app.ActionBarDrawerToggle( getActivity(), /* host Activity */ mDrawerLayout, /* DrawerLayout object */ R.string.navigation_drawer_open, /* "open drawer" description for accessibility */ R.string.navigation_drawer_close /* "close drawer" description for accessibility */ ) { @Override public void onDrawerClosed(View drawerView) { super.onDrawerClosed(drawerView); if (!isAdded()) { return; } getActivity().supportInvalidateOptionsMenu(); // calls onPrepareOptionsMenu() } @Override public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); if (!isAdded()) { return; } getActivity().supportInvalidateOptionsMenu(); // calls onPrepareOptionsMenu() } }; // Defer code dependent on restoration of previous instance state. mDrawerLayout.post(new Runnable() { @Override public void run() { mDrawerToggle.syncState(); } }); mDrawerLayout.setDrawerListener(mDrawerToggle); } /** * Sets the currently selected navigation drawer item, based on the provided position * parameter, * which must be one of the NAVDRAWER_ITEM_* contants in this class. * * @param position the item to select in the navigation drawer - must be one of the * NAVDRAWER_ITEM_* contants in this class */ public void selectItem(int position) { setSelectedNavDrawerItem(position); if (mDrawerLayout != null) { mDrawerLayout.closeDrawer(mFragmentContainerView); } if (mCallbacks != null) { mCallbacks.onNavigationDrawerItemSelected(position); } } /** * 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(int itemId) { if (!isNewActivityItem(itemId)) { // We only change the selected item if it doesn't launch a new activity mCurrentSelectedPosition = itemId; // Save the selected position as a preference SharedPreferences sp = PreferenceManager .getDefaultSharedPreferences(getActivity()); sp.edit().putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition).apply(); } if (mNavDrawerItemViews != null) { for (int i = 0; i < mNavDrawerItemViews.length; i++) { if (i < mNavDrawerItems.size()) { int thisItemId = mNavDrawerItems.get(i); formatNavDrawerItem(mNavDrawerItemViews[i], thisItemId, itemId == thisItemId); } } } } @Override public void onAttach(Context context) { super.onAttach(context); try { mCallbacks = (NavigationDrawerCallbacks) context; } catch (ClassCastException e) { throw new ClassCastException("Activity must implement NavigationDrawerCallbacks."); } } @Override public void onDetach() { super.onDetach(); mCallbacks = null; } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.d(TAG, "Saving position = " + mCurrentSelectedPosition); outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Forward the new configuration the drawer toggle component. mDrawerToggle.onConfigurationChanged(newConfig); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (mDrawerToggle.onOptionsItemSelected(item)) { return true; } return super.onOptionsItemSelected(item); } private ActionBar getActionBar() { return ((AppCompatActivity) getActivity()).getSupportActionBar(); } /** * Callbacks interface that all activities using this fragment must implement. */ public interface NavigationDrawerCallbacks { /** * Called when an item in the navigation drawer is selected. */ void onNavigationDrawerItemSelected(int position); } /** Populates the navigation drawer with the appropriate items. */ public void populateNavDrawer() { mNavDrawerItems.clear(); mNavDrawerItems.add(NAVDRAWER_ITEM_NEARBY); mNavDrawerItems.add(NAVDRAWER_ITEM_STARRED_STOPS); mNavDrawerItems.add(NAVDRAWER_ITEM_MY_REMINDERS); if ((Application.get().getCurrentRegion() != null && !TextUtils.isEmpty(Application.get().getCurrentRegion().getOtpBaseUrl())) || !TextUtils.isEmpty(Application.get().getCustomOtpApiUrl())) { mNavDrawerItems.add(NAVDRAWER_ITEM_PLAN_TRIP); } mNavDrawerItems.add(NAVDRAWER_ITEM_SEPARATOR); mNavDrawerItems.add(NAVDRAWER_ITEM_SETTINGS); mNavDrawerItems.add(NAVDRAWER_ITEM_HELP); mNavDrawerItems.add(NAVDRAWER_ITEM_SEND_FEEDBACK); createNavDrawerItems(); } private void createNavDrawerItems() { if (mDrawerItemsListContainer == null || getActivity() == null) { return; } mNavDrawerItemViews = new View[mNavDrawerItems.size()]; int i = 0; LinearLayout containerLayout = (LinearLayout) mDrawerItemsListContainer. findViewById(R.id.navdrawer_items_list); containerLayout.removeAllViews(); for (int itemId : mNavDrawerItems) { mNavDrawerItemViews[i] = makeNavDrawerItem(itemId, containerLayout); containerLayout.addView(mNavDrawerItemViews[i]); ++i; } } private View makeNavDrawerItem(final int itemId, ViewGroup container) { boolean selected = mCurrentSelectedPosition == itemId; int layoutToInflate; if (itemId == NAVDRAWER_ITEM_SEPARATOR) { layoutToInflate = R.layout.navdrawer_separator; } else if (itemId == NAVDRAWER_ITEM_SEPARATOR_SPECIAL) { layoutToInflate = R.layout.navdrawer_separator; } else { layoutToInflate = R.layout.navdrawer_item; } View view = getActivity().getLayoutInflater().inflate(layoutToInflate, container, false); if (isSeparator(itemId)) { // we are done UIUtils.setAccessibilityIgnore(view); return view; } ImageView iconView = (ImageView) view.findViewById(R.id.icon); TextView titleView = (TextView) view.findViewById(R.id.title); int iconId = itemId >= 0 && itemId < NAVDRAWER_ICON_RES_ID.length ? NAVDRAWER_ICON_RES_ID[itemId] : 0; int titleId = itemId >= 0 && itemId < NAVDRAWER_TITLE_RES_ID.length ? NAVDRAWER_TITLE_RES_ID[itemId] : 0; // set icon and text iconView.setVisibility(iconId > 0 ? View.VISIBLE : View.GONE); if (iconId > 0) { iconView.setImageResource(iconId); } titleView.setText(getString(titleId)); formatNavDrawerItem(view, itemId, selected); view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { selectItem(itemId); } }); return view; } private void formatNavDrawerItem(View view, int itemId, boolean selected) { if (isSeparator(itemId)) { // Don't do any formatting return; } ImageView iconView = (ImageView) view.findViewById(R.id.icon); TextView titleView = (TextView) view.findViewById(R.id.title); /** * Configure its appearance according to whether or not it's selected. Certain items * (e.g., Settings) don't get formatted upon selection, since they open a new activity. */ if (selected) { if (isNewActivityItem(itemId)) { // Don't change any formatting, since this is a category that launches a new activity return; } else { // Show the category as highlighted by changing background, text, and icon color view.setSelected(true); titleView.setTextColor( getResources().getColor(R.color.navdrawer_text_color_selected)); iconView.setColorFilter( getResources().getColor(R.color.navdrawer_icon_tint_selected)); } } else { // Show the category as not highlighted, if its not currently selected if (itemId != mCurrentSelectedPosition) { view.setSelected(false); titleView.setTextColor(getResources().getColor(R.color.navdrawer_text_color)); iconView.setColorFilter(getResources().getColor(R.color.navdrawer_icon_tint)); } } } private boolean isSeparator(int itemId) { return itemId == NAVDRAWER_ITEM_SEPARATOR || itemId == NAVDRAWER_ITEM_SEPARATOR_SPECIAL; } /** * Returns true if this is an item that should not allow selection (e.g., Settings), * because they launch a new Activity and aren't part of this screen, false if its selectable * and changes the current UI via a new fragment * * @return true if this is an item that should not allow selection (e.g., Settings), * because they launch a new Activity and aren't part of this screen, false if its selectable * and changes the current UI via a new fragment */ private boolean isNewActivityItem(int itemId) { return itemId == NAVDRAWER_ITEM_SETTINGS || itemId == NAVDRAWER_ITEM_HELP || itemId == NAVDRAWER_ITEM_SEND_FEEDBACK || itemId == NAVDRAWER_ITEM_PLAN_TRIP; } }