Java tutorial
/* * Copyright 2013 The Android Open Source Project * * 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.luboganev.dejalist.ui; import static nl.qbusict.cupboard.CupboardFactory.cupboard; import java.io.File; import android.app.Activity; import android.content.ContentValues; import android.content.Intent; import android.content.res.Configuration; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Parcelable; import android.support.v4.app.ActionBarDrawerToggle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.CursorLoader; import android.support.v4.content.FileProvider; import android.support.v4.content.Loader; import android.support.v4.view.GravityCompat; import android.support.v4.widget.CursorAdapter; import android.support.v4.widget.DrawerLayout; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.widget.AdapterView; import android.widget.ListView; import android.widget.Toast; import butterknife.InjectView; import butterknife.Views; import com.luboganev.dejalist.R; import com.luboganev.dejalist.Utils; import com.luboganev.dejalist.data.BackupIntentService; import com.luboganev.dejalist.data.DejalistContract; import com.luboganev.dejalist.data.DejalistContract.Categories; import com.luboganev.dejalist.data.DejalistContract.Products; import com.luboganev.dejalist.data.ProductImageFileHelper; import com.luboganev.dejalist.data.SelectionBuilder; import com.luboganev.dejalist.data.entities.Category; import com.luboganev.dejalist.data.entities.Product; import com.luboganev.dejalist.ui.CategoryDialogFragment.CategoryEditorCallback; import com.luboganev.dejalist.ui.ConfirmBackResDialogFragment.ConfirmBackResCallback; import com.luboganev.dejalist.ui.SetProductsCategoryDialogFragment.SetProductsCategoryCallback; /** * This example illustrates a common usage of the DrawerLayout widget * in the Android support library. * <p/> * <p>When a navigation (left) drawer is present, the host activity should detect presses of * the action bar's Up affordance as a signal to open and close the navigation drawer. The * ActionBarDrawerToggle facilitates this behavior. * Items within the drawer should fall into one of two categories:</p> * <p/> * <ul> * <li><strong>View switches</strong>. A view switch follows the same basic policies as * list or tab navigation in that a view switch does not create navigation history. * This pattern should only be used at the root activity of a task, leaving some form * of Up navigation active for activities further down the navigation hierarchy.</li> * <li><strong>Selective Up</strong>. The drawer allows the user to choose an alternate * parent for Up navigation. This allows a user to jump across an app's navigation * hierarchy at will. The application should treat this as it treats Up navigation from * a different task, replacing the current task stack using TaskStackBuilder or similar. * This is the only form of navigation drawer that should be used outside of the root * activity of a task.</li> * </ul> * <p/> * <p>Right side drawers should be used for actions, not navigation. This follows the pattern * established by the Action Bar that navigation should be to the left and actions to the right. * An action should be an operation performed on the current contents of the window, * for example enabling or disabling a data overlay on top of the current content.</p> */ public class MainActivity extends FragmentActivity implements LoaderCallbacks<Cursor>, ProductsGalleryController, CategoryEditorCallback, UndoBarController.UndoListener, SetProductsCategoryCallback, ChecklistController, ConfirmBackResCallback { @InjectView(R.id.drawer_layout) DrawerLayout mDrawerLayout; @InjectView(R.id.left_drawer) ListView mDrawerList; private ActionBarDrawerToggle mDrawerToggle; private NavigationCursorAdapter mAdapter; private UndoBarController mUndoBarController; private static final int LOADER_NAVIGATION_ID = 1; private CharSequence mDrawerTitle; private CharSequence mTitle; // private String[] mPlanetTitles; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Views.inject(this); mTitle = mDrawerTitle = getTitle(); // set a custom shadow that overlays the main content when the drawer opens mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); mAdapter = new NavigationCursorAdapter(getApplicationContext(), CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER, onAddCategoryListener); mDrawerList.setChoiceMode(ListView.CHOICE_MODE_SINGLE); mDrawerList.setAdapter(mAdapter); if (savedInstanceState == null) { selectItem(NavigationCursorAdapter.POSITION_CHECKLIST); } else { mStateSelectedNavigationPosition = savedInstanceState.getInt(STATE_SELECTED_NAVIGATION, -1); } if (getSupportLoaderManager().getLoader(LOADER_NAVIGATION_ID) != null) { getSupportLoaderManager().restartLoader(LOADER_NAVIGATION_ID, null, this); } else { getSupportLoaderManager().initLoader(LOADER_NAVIGATION_ID, null, this); } // set up the drawer's list view with items and click listener // mDrawerList.setAdapter(new ArrayAdapter<String>(this, // R.layout.drawer_list_item, mPlanetTitles)); mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); // enable ActionBar app icon to behave as action to toggle nav drawer getActionBar().setDisplayHomeAsUpEnabled(true); getActionBar().setHomeButtonEnabled(true); // ActionBarDrawerToggle ties together the the proper interactions // between the sliding drawer and the action bar app icon mDrawerToggle = new ActionBarDrawerToggle(this, /* host Activity */ mDrawerLayout, /* DrawerLayout object */ R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */ R.string.drawer_open, /* "open drawer" description for accessibility */ R.string.drawer_close /* "close drawer" description for accessibility */ ) { public void onDrawerClosed(View view) { getActionBar().setTitle(mTitle); if (mProductsGalleryActionTaker != null) mProductsGalleryActionTaker.setOptionMenuItemsVisible(true); if (mChecklistActionTaker != null) mChecklistActionTaker.setOptionMenuItemsVisible(true); invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() } public void onDrawerOpened(View drawerView) { getActionBar().setTitle(mDrawerTitle); if (mProductsGalleryActionTaker != null) mProductsGalleryActionTaker.setOptionMenuItemsVisible(false); if (mChecklistActionTaker != null) mChecklistActionTaker.setOptionMenuItemsVisible(false); invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() } }; mDrawerLayout.setDrawerListener(mDrawerToggle); mUndoBarController = new UndoBarController(findViewById(R.id.undobar), this); } private static final String STATE_SELECTED_NAVIGATION = "selected_navigation"; private int mStateSelectedNavigationPosition = -1; @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu_main, menu); return super.onCreateOptionsMenu(menu); } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(STATE_SELECTED_NAVIGATION, mDrawerList.getCheckedItemPosition()); mUndoBarController.onSaveInstanceState(outState); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); mUndoBarController.onRestoreInstanceState(savedInstanceState); } @Override public boolean onOptionsItemSelected(MenuItem item) { // The action bar home/up action should open or close the drawer. // ActionBarDrawerToggle will take care of this. if (mDrawerToggle.onOptionsItemSelected(item)) { return true; } // Handle action buttons switch (item.getItemId()) { //case R.id.menu_main_settings: //return true; case R.id.menu_main_backup: ConfirmBackResDialogFragment.getBackupInstance().show(getSupportFragmentManager(), "backup"); return true; case R.id.menu_main_restore: ConfirmBackResDialogFragment.getRestoreInstance().show(getSupportFragmentManager(), "restore"); return true; case R.id.menu_main_about: startActivity(new Intent(getApplicationContext(), AboutActivity.class)); return true; default: return super.onOptionsItemSelected(item); } } /* The click listner for ListView in the navigation drawer */ private class DrawerItemClickListener implements ListView.OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { selectItem(position); if (mProductsGalleryActionTaker != null) mProductsGalleryActionTaker.closeActionMode(); if (mChecklistActionTaker != null) mChecklistActionTaker.closeActionMode(); } } private OnClickListener onAddCategoryListener = new OnClickListener() { @Override public void onClick(View v) { CategoryDialogFragment dialog = CategoryDialogFragment.getInstance(); dialog.show(getSupportFragmentManager(), "CategoryDialogFragment"); } }; private void selectItem(int position) { Category selectedCategory = cupboard().withCursor((Cursor) mAdapter.getItem(position)).get(Category.class); if (selectedCategory._id == NavigationCursorAdapter.NAV_CHECKLIST_ITEM_ID) { selectedCategory.name = getString(R.string.nav_checklist); // update the main content by replacing fragments Fragment fragment = new ChecklistFragment(); FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction().replace(R.id.content_frame, fragment).commit(); } else if (selectedCategory._id == NavigationCursorAdapter.NAV_ALL_PRODUCTS_ITEM_ID) { selectedCategory.name = getString(R.string.nav_all_products); // update the main content by replacing fragments getSupportFragmentManager().beginTransaction() .replace(R.id.content_frame, ProductsGalleryFragment.getInstanceAllProducts()).commit(); } else if (selectedCategory._id == Products.PRODUCT_CATEGORY_NONE_ID) { selectedCategory.name = getString(R.string.nav_products_no_category); // update the main content by replacing fragments getSupportFragmentManager().beginTransaction() .replace(R.id.content_frame, ProductsGalleryFragment.getInstanceNoCategoryProducts()).commit(); } else { // update the main content by replacing fragments getSupportFragmentManager().beginTransaction().replace(R.id.content_frame, ProductsGalleryFragment.getInstanceCategoryProducts(selectedCategory)).commit(); } // update selected item and title, then close the drawer mDrawerList.setItemChecked(position, true); setTitle(selectedCategory.name); mDrawerLayout.closeDrawer(mDrawerList); } @Override public void setTitle(CharSequence title) { mTitle = title; getActionBar().setTitle(mTitle); } /** * When using the ActionBarDrawerToggle, you must call it during * onPostCreate() and onConfigurationChanged()... */ @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); // Sync the toggle state after onRestoreInstanceState has occurred. mDrawerToggle.syncState(); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Pass any configuration change to the drawer toggle mDrawerToggle.onConfigurationChanged(newConfig); } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { CursorLoader cursorLoader = new CursorLoader(getApplicationContext(), DejalistContract.Categories.CONTENT_URI, null, null, null, null); return cursorLoader; } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { data.setNotificationUri(getContentResolver(), DejalistContract.Categories.CONTENT_URI); mAdapter.changeCursor(data); if (mStateSelectedNavigationPosition >= 0) { mDrawerList.setItemChecked(mStateSelectedNavigationPosition, true); mStateSelectedNavigationPosition = -1; } } @Override public void onLoaderReset(Loader<Cursor> loader) { mAdapter.changeCursor(null); } @Override public void onCategoryEdited(Category category) { if (mProductsGalleryActionTaker != null) mProductsGalleryActionTaker.updateShownCategory(category); } @Override public void onCategoryCreated(Category category) { // Unfortunately cannot switch to it because of the loaders. A solution needs to // be found in a future release. } public static final int REQUEST_CODE_NEW_PRODUCT = 1; public static final int REQUEST_CODE_EDIT_PRODUCT = 2; @Override public void newProduct(Category category) { Intent intent = new Intent(this, ProductActivity.class); if (category != null) intent.putExtra(ProductActivity.EXTRA_CATEGORY_ID, category._id); startActivityForResult(intent, REQUEST_CODE_NEW_PRODUCT); } @Override public void editProduct(Product product) { Intent intent = new Intent(this, ProductActivity.class); if (product != null) intent.putExtra(ProductActivity.EXTRA_PRODUCT, product); startActivityForResult(intent, REQUEST_CODE_EDIT_PRODUCT); } @Override public void shareProduct(Product product) { if (product.uri != null) { Intent shareIntent = new Intent(); String fileName = new File(Uri.parse(product.uri).getPath()).getName(); File sharedFile = ProductImageFileHelper.getFile(this, fileName); Utils.d(this, sharedFile.toString()); Uri contentUri = FileProvider.getUriForFile(getApplicationContext(), "com.luboganev.dejalist.productimagesprovider", sharedFile); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri); shareIntent.setType("image/jpeg"); startActivity(Intent.createChooser(shareIntent, getResources().getText(R.string.dialog_share_product))); } else { Toast.makeText(getApplicationContext(), R.string.toast_no_image_to_share, Toast.LENGTH_SHORT).show(); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_CODE_NEW_PRODUCT || requestCode == REQUEST_CODE_EDIT_PRODUCT) { if (resultCode == Activity.RESULT_OK) { long productCategoryId = data.getLongExtra(ProductActivity.RESULT_EXTRA_PRODUCT_CATEGORY_ID, Products.PRODUCT_CATEGORY_NONE_ID); int checkedPosition = mDrawerList.getCheckedItemPosition(); if (checkedPosition != NavigationCursorAdapter.POSITION_ALL_PRODUCTS && mAdapter.getItemId(checkedPosition) != productCategoryId) { final int count = mAdapter.getCount() - 1; // we do not want the add categories button, only the categories // we start from 2 because position 0 is Checklist and position 1 is All products, // which are both not related to categories at all int pos = 0; while (pos < count) { if (productCategoryId == mAdapter.getItemId(pos)) { selectItem(pos); break; } pos++; } // Unfortunately cannot switch to a newly inserted category // because of the loaders. A solution needs to // be found in a future release. if (pos == count) selectItem(NavigationCursorAdapter.POSITION_ALL_PRODUCTS); } else { if (mProductsGalleryActionTaker != null) mProductsGalleryActionTaker.reloadProducts(); } } } } @Override public void setProductsCategory(long[] productIds) { SetProductsCategoryDialogFragment dialog = SetProductsCategoryDialogFragment.getInstance(productIds); dialog.show(getSupportFragmentManager(), "SetProductsCategoryDialogFragment"); } @Override public void onSetProductsCategory(long[] productIds, long categoryId) { ContentValues values = new ContentValues(); values.put(DejalistContract.Products.PRODUCT_CATEGORY_ID, categoryId); getContentResolver().update(DejalistContract.Products.CONTENT_URI, values, DejalistContract.Products.buildSelectionIdIn(productIds), null); if (mProductsGalleryActionTaker != null) mProductsGalleryActionTaker.reloadProducts(); } private static final String UNDO_EXTRA_DELETED_PRODUCTS = "deleted_products"; @Override public void deleteProducts(long[] productIds) { if (mUndoBarController.isShown()) { getContentResolver().delete(Products.CONTENT_URI, Products.SELECTION_DELETED, null); } ContentValues values = new ContentValues(); values.put(Products.PRODUCT_DELETED, 1); getContentResolver().update(Products.CONTENT_URI, values, Products.buildSelectionIdIn(productIds), null); if (mProductsGalleryActionTaker != null) mProductsGalleryActionTaker.reloadProducts(); Bundle undoExtras = new Bundle(); undoExtras.putLongArray(UNDO_EXTRA_DELETED_PRODUCTS, productIds); mUndoBarController.showUndoBar(false, getString(R.string.undobar_product_deleted_message), undoExtras); } @Override public void onUndo(Parcelable token) { ContentValues values = new ContentValues(); values.put(Products.PRODUCT_DELETED, 0); getContentResolver().update(Products.CONTENT_URI, values, Products.SELECTION_DELETED, null); if (mProductsGalleryActionTaker != null) mProductsGalleryActionTaker.reloadProducts(); } @Override public void onUndoExpired(Parcelable token) { getContentResolver().delete(Products.CONTENT_URI, Products.SELECTION_DELETED, null); } private ProductsGalleryActionTaker mProductsGalleryActionTaker; @Override public void editCategory(Category category) { CategoryDialogFragment dialog = CategoryDialogFragment.getInstance(category); dialog.show(getSupportFragmentManager(), "CategoryDialogFragment"); } @Override public void deleteCategory(Category category) { getContentResolver().delete(Categories.buildCategoryUri(category._id), null, null); // go back to all categories selectItem(NavigationCursorAdapter.POSITION_ALL_PRODUCTS); } @Override public void registerProductsGalleryActionTaker(ProductsGalleryActionTaker actionTaker) { mProductsGalleryActionTaker = actionTaker; } @Override public void unregisterProductsGalleryActionTaker() { mProductsGalleryActionTaker = null; } private ChecklistActionTaker mChecklistActionTaker; @Override public void removeProducts(long[] productIds) { ContentValues values = new ContentValues(); values.put(Products.PRODUCT_INLIST, 0); values.put(Products.PRODUCT_CHECKED, 0); getContentResolver().update(Products.CONTENT_URI, values, Products.buildSelectionIdIn(productIds), null); if (mChecklistActionTaker != null) mChecklistActionTaker.reloadProducts(); } @Override public void clearCheckList(boolean onlyChecked) { ContentValues values = new ContentValues(); values.put(Products.PRODUCT_INLIST, 0); values.put(Products.PRODUCT_CHECKED, 0); if (onlyChecked) { getContentResolver().update(Products.CONTENT_URI, values, new SelectionBuilder().where(Products.SELECTION_IN_LIST, (String[]) null) .where(Products.SELECTION_CHECKED, (String[]) null).getSelection(), null); } else getContentResolver().update(Products.CONTENT_URI, values, null, null); if (mChecklistActionTaker != null) mChecklistActionTaker.reloadProducts(); } @Override public void addCheckListProductsClicked() { if (!mDrawerLayout.isDrawerOpen(mDrawerList)) { mDrawerLayout.openDrawer(mDrawerList); } } @Override public void registerChecklistActionTaker(ChecklistActionTaker actionTaker) { mChecklistActionTaker = actionTaker; } @Override public void unregisterChecklistActionTaker() { mChecklistActionTaker = null; } @Override public void onConfirmBackup() { Toast.makeText(getApplicationContext(), R.string.toast_backup_started, Toast.LENGTH_SHORT).show(); Intent intent = new Intent(getApplicationContext(), BackupIntentService.class); intent.putExtra(BackupIntentService.INTENT_EXTRA_ACTION, BackupIntentService.Action.BACKUP); startService(intent); } @Override public void onConfirmRestore() { Toast.makeText(getApplicationContext(), R.string.toast_restore_started, Toast.LENGTH_SHORT).show(); Intent intent = new Intent(getApplicationContext(), BackupIntentService.class); intent.putExtra(BackupIntentService.INTENT_EXTRA_ACTION, BackupIntentService.Action.RESTORE); startService(intent); } }