Java tutorial
/* * Copyright 2014 Google Inc. 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. */ package pl.edu.agh.schedule.ui; import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; import android.animation.TypeEvaluator; import android.animation.ValueAnimator; import android.content.Intent; import android.content.SyncStatusObserver; import android.content.res.Resources; import android.graphics.Color; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.TaskStackBuilder; import android.support.v4.view.GravityCompat; import android.support.v4.view.ViewCompat; import android.support.v4.widget.DrawerLayout; import android.support.v4.widget.SwipeRefreshLayout; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import java.util.ArrayList; import pl.edu.agh.schedule.R; import pl.edu.agh.schedule.about.AboutActivity; import pl.edu.agh.schedule.myschedule.MyScheduleActivity; import pl.edu.agh.schedule.settings.SettingsActivity; import pl.edu.agh.schedule.sync.SyncHelper; import pl.edu.agh.schedule.ui.widget.MultiSwipeRefreshLayout; import pl.edu.agh.schedule.ui.widget.NavDrawerItemView; import pl.edu.agh.schedule.ui.widget.ScrimInsetsScrollView; import pl.edu.agh.schedule.util.LUtils; import pl.edu.agh.schedule.util.RecentTasksStyler; import pl.edu.agh.schedule.util.UIUtils; import static pl.edu.agh.schedule.util.LogUtils.makeLogTag; /** * A base activity that handles common functionality in the app. This includes the * navigation drawer, Action Bar tweaks, amongst others. */ @SuppressWarnings("deprecation") public abstract class BaseActivity extends AppCompatActivity implements MultiSwipeRefreshLayout.CanChildScrollUpCallback { private static final String TAG = makeLogTag(BaseActivity.class); // Navigation drawer: private DrawerLayout mDrawerLayout; // Helper methods for L APIs private LUtils mLUtils; private ObjectAnimator mStatusBarColorAnimator; private Handler mHandler; // 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_MY_SCHEDULE = 0; protected static final int NAVDRAWER_ITEM_SETTINGS = 1; protected static final int NAVDRAWER_ITEM_ABOUT = 2; protected static final int NAVDRAWER_ITEM_INVALID = -1; protected static final int NAVDRAWER_ITEM_SEPARATOR = -2; // titles for navdrawer items (indices must correspond to the above) private static final int[] NAVDRAWER_TITLE_RES_ID = new int[] { R.string.navdrawer_item_my_schedule, R.string.navdrawer_item_settings, R.string.description_about }; // icons for navdrawer items (indices must correspond to above array) private static final int[] NAVDRAWER_ICON_RES_ID = new int[] { R.drawable.ic_navview_my_schedule, // My Schedule R.drawable.ic_navview_settings, // Settings. R.drawable.ic_info_outline, // About }; // 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; private static final int MAIN_CONTENT_FADEIN_DURATION = 250; // list of navdrawer items that were actually added to the navdrawer, in order private ArrayList<Integer> mNavDrawerItems = new ArrayList<>(); // views that correspond to each navdrawer item, null if not yet created private View[] mNavDrawerItemViews = null; // SwipeRefreshLayout allows the user to swipe the screen down to trigger a manual refresh private SwipeRefreshLayout mSwipeRefreshLayout; // Primary toolbar and drawer toggle private Toolbar mActionBarToolbar; // A Runnable that we should execute when the navigation drawer finishes its closing animation private Runnable mDeferredOnDrawerClosedRunnable; private int mNormalStatusBarColor; private static final TypeEvaluator ARGB_EVALUATOR = new ArgbEvaluator(); private SyncHelper syncHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); RecentTasksStyler.styleRecentTasksEntry(this); mHandler = new Handler(); ActionBar ab = getSupportActionBar(); if (ab != null) { ab.setDisplayHomeAsUpEnabled(true); } mLUtils = LUtils.getInstance(this); mNormalStatusBarColor = getResources().getColor(R.color.theme_primary_dark); if (syncHelper == null) { syncHelper = new SyncHelper(this, mSyncStatusObserver); } } private void trySetupSwipeRefresh() { mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout); if (mSwipeRefreshLayout != null) { mSwipeRefreshLayout.setColorSchemeResources(R.color.flat_button_text); mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { @Override public void onRefresh() { requestDataRefresh(); } }); if (mSwipeRefreshLayout instanceof MultiSwipeRefreshLayout) { MultiSwipeRefreshLayout mswrl = (MultiSwipeRefreshLayout) mSwipeRefreshLayout; mswrl.setCanChildScrollUpCallback(this); } } } /** * Returns the navigation drawer item that corresponds to this Activity. Subclasses * of BaseActivity override this to indicate what nav drawer item corresponds to them * Return NAVDRAWER_ITEM_INVALID to mean that this Activity should not have a Nav Drawer. */ protected int getSelfNavDrawerItem() { return NAVDRAWER_ITEM_INVALID; } /** * Sets up the navigation drawer as appropriate. Note that the nav drawer will be * different depending on whether the attendee indicated that they are attending the * event on-site vs. attending remotely. */ private void setupNavDrawer() { // What nav drawer item should be selected? int selfItem = getSelfNavDrawerItem(); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); if (mDrawerLayout == null) { return; } mDrawerLayout.setStatusBarBackgroundColor(getResources().getColor(R.color.theme_primary_dark)); 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; } if (mActionBarToolbar != null) { mActionBarToolbar.setNavigationIcon(R.drawable.ic_ab_drawer); mActionBarToolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { mDrawerLayout.openDrawer(GravityCompat.START); } }); } 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; } } @Override public void onDrawerSlide(View drawerView, float slideOffset) { // NO OP } @Override public void onDrawerOpened(View drawerView) { // NO OP } @Override public void onDrawerStateChanged(int newState) { // NO OP } }); mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); // populate the nav drawer with the correct items populateNavDrawer(); } @Override public void setContentView(int layoutResID) { super.setContentView(layoutResID); getActionBarToolbar(); } protected boolean isNavDrawerOpen() { return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START); } protected void closeNavDrawer() { if (mDrawerLayout != null) { mDrawerLayout.closeDrawer(GravityCompat.START); } } /** * Defines the Navigation Drawer items to display by updating {@code mNavDrawerItems} then * forces the Navigation Drawer to redraw itself. */ private void populateNavDrawer() { mNavDrawerItems.clear(); mNavDrawerItems.add(NAVDRAWER_ITEM_MY_SCHEDULE); // Other items that are always in the nav drawer. mNavDrawerItems.add(NAVDRAWER_ITEM_SEPARATOR); mNavDrawerItems.add(NAVDRAWER_ITEM_SETTINGS); mNavDrawerItems.add(NAVDRAWER_ITEM_ABOUT); createNavDrawerItems(); } @Override public void onBackPressed() { if (isNavDrawerOpen()) { closeNavDrawer(); } else { super.onBackPressed(); } } private void createNavDrawerItems() { ViewGroup mDrawerItemsListContainer = (ViewGroup) findViewById(R.id.navdrawer_items_list); if (mDrawerItemsListContainer == null) { return; } mNavDrawerItemViews = new View[mNavDrawerItems.size()]; mDrawerItemsListContainer.removeAllViews(); int i = 0; for (int itemId : mNavDrawerItems) { mNavDrawerItemViews[i] = makeNavDrawerItem(itemId, mDrawerItemsListContainer); mDrawerItemsListContainer.addView(mNavDrawerItemViews[i]); ++i; } } /** * 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 (mNavDrawerItemViews != null) { for (int i = 0; i < mNavDrawerItemViews.length; i++) { if (i < mNavDrawerItems.size()) { int thisItemId = mNavDrawerItems.get(i); mNavDrawerItemViews[i].setActivated(itemId == thisItemId); } } } } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); setupNavDrawer(); trySetupSwipeRefresh(); View mainContent = findViewById(R.id.main_content); if (mainContent != null) { mainContent.setAlpha(0); mainContent.animate().alpha(1).setDuration(MAIN_CONTENT_FADEIN_DURATION); } else { Log.w(TAG, "No view with ID main_content to fade in."); } } protected void requestDataRefresh() { Log.d(TAG, "Requesting manual data refresh."); syncHelper.performSync(); } private void goToNavDrawerItem(int item) { switch (item) { case NAVDRAWER_ITEM_MY_SCHEDULE: createBackStack(new Intent(this, MyScheduleActivity.class)); break; case NAVDRAWER_ITEM_SETTINGS: createBackStack(new Intent(this, SettingsActivity.class)); break; case NAVDRAWER_ITEM_ABOUT: createBackStack(new Intent(this, AboutActivity.class)); break; } } /** * Enables back navigation for activities that are launched from the NavBar. See * {@code AndroidManifest.xml} to find out the parent activity names for each activity. * * */ private void createBackStack(Intent intent) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { TaskStackBuilder builder = TaskStackBuilder.create(this); builder.addNextIntentWithParentStack(intent); builder.startActivities(); } else { startActivity(intent); finish(); } } private void onNavDrawerItemClicked(final int itemId) { if (itemId == getSelfNavDrawerItem()) { mDrawerLayout.closeDrawer(GravityCompat.START); return; } if (isSpecialItem(itemId)) { goToNavDrawerItem(itemId); } else { // launch the target Activity after a short delay, to allow the close animation to play mHandler.postDelayed(new Runnable() { @Override public void run() { goToNavDrawerItem(itemId); } }, NAVDRAWER_LAUNCH_DELAY); // change the active item on the list so the user can see the item changed setSelectedNavDrawerItem(itemId); // fade out the main content View mainContent = findViewById(R.id.main_content); if (mainContent != null) { mainContent.animate().alpha(0).setDuration(MAIN_CONTENT_FADEOUT_DURATION); } } mDrawerLayout.closeDrawer(GravityCompat.START); } @Override protected void onResume() { super.onResume(); // Watch for sync state changes mSyncStatusObserver.onStatusChanged(0); } protected Toolbar getActionBarToolbar() { if (mActionBarToolbar == null) { mActionBarToolbar = (Toolbar) findViewById(R.id.toolbar_actionbar); if (mActionBarToolbar != null) { // Depending on which version of Android you are on the Toolbar or the ActionBar may be // active so the a11y description is set here. mActionBarToolbar.setNavigationContentDescription( getResources().getString(R.string.navdrawer_description_a11y)); setSupportActionBar(mActionBarToolbar); } } return mActionBarToolbar; } private View makeNavDrawerItem(final int itemId, ViewGroup container) { if (isSeparator(itemId)) { View separator = getLayoutInflater().inflate(R.layout.navdrawer_separator, container, false); UIUtils.setAccessibilityIgnore(separator); return separator; } NavDrawerItemView item = (NavDrawerItemView) getLayoutInflater().inflate(R.layout.navdrawer_item, container, false); item.setContent(NAVDRAWER_ICON_RES_ID[itemId], NAVDRAWER_TITLE_RES_ID[itemId]); item.setActivated(getSelfNavDrawerItem() == itemId); if (item.isActivated()) { item.setContentDescription(getString(R.string.navdrawer_selected_menu_item_a11y_wrapper, getString(NAVDRAWER_TITLE_RES_ID[itemId]))); } else { item.setContentDescription(getString(R.string.navdrawer_menu_item_a11y_wrapper, getString(NAVDRAWER_TITLE_RES_ID[itemId]))); } item.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onNavDrawerItemClicked(itemId); } }); return item; } private boolean isSpecialItem(int itemId) { return itemId == NAVDRAWER_ITEM_SETTINGS; } private boolean isSeparator(int itemId) { return itemId == NAVDRAWER_ITEM_SEPARATOR; } private SyncStatusObserver mSyncStatusObserver = new SyncStatusObserver() { @Override public void onStatusChanged(int which) { runOnUiThread(new Runnable() { @Override public void run() { onRefreshingStateChanged(false); } }); } }; protected void onRefreshingStateChanged(boolean refreshing) { if (mSwipeRefreshLayout != null) { mSwipeRefreshLayout.setRefreshing(refreshing); } } public LUtils getLUtils() { return mLUtils; } protected void onActionBarAutoShowOrHide(boolean shown) { if (mStatusBarColorAnimator != null) { mStatusBarColorAnimator.cancel(); } mStatusBarColorAnimator = ObjectAnimator .ofInt((mDrawerLayout != null) ? mDrawerLayout : mLUtils, (mDrawerLayout != null) ? "statusBarBackgroundColor" : "statusBarColor", shown ? Color.BLACK : mNormalStatusBarColor, shown ? mNormalStatusBarColor : Color.BLACK) .setDuration(250); if (mDrawerLayout != null) { mStatusBarColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { ViewCompat.postInvalidateOnAnimation(mDrawerLayout); } }); } mStatusBarColorAnimator.setEvaluator(ARGB_EVALUATOR); mStatusBarColorAnimator.start(); } @Override public boolean canSwipeRefreshChildScrollUp() { return false; } /** * Returns true if the theme sets the {@code R.attr.isFloatingWindow} flag to true. */ protected boolean shouldBeFloatingWindow() { Resources.Theme theme = getTheme(); TypedValue floatingWindowFlag = new TypedValue(); // Check isFloatingWindow flag is defined in theme. if (theme == null || !theme.resolveAttribute(R.attr.isFloatingWindow, floatingWindowFlag, true)) { return false; } return (floatingWindowFlag.data != 0); } /** * Configure this Activity as a floating window, with the given {@code width}, {@code height} * and {@code alpha}, and dimming the background with the given {@code dim} value. */ protected void setupFloatingWindow(int width, int height, int alpha, float dim) { WindowManager.LayoutParams params = getWindow().getAttributes(); params.width = getResources().getDimensionPixelSize(width); params.height = getResources().getDimensionPixelSize(height); params.alpha = alpha; params.dimAmount = dim; params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND; getWindow().setAttributes(params); } }