Java tutorial
/* * Copyright (C) 2010 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.aboveware.actionbar.support; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.support.v4.app.ListFragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ExpandableListView; import android.widget.ListView; import com.aboveware.abovegame.R; import com.aboveware.actionbar.common.ActionBarHelper; import com.aboveware.actionbar.common.Document; import com.aboveware.actionbar.common.ExpandableDocument; import com.aboveware.actionbar.common.ExpandableListAdapter; import com.aboveware.actionbar.common.ICustomAlertDialog; import com.aboveware.actionbar.common.IDetailsView; import com.aboveware.actionbar.common.IDialog; import com.aboveware.actionbar.common.IDocument; import com.aboveware.actionbar.common.IDocumentInterface; import com.aboveware.actionbar.common.IListAdapter; import com.aboveware.actionbar.support.FragmentLayoutSupport.DetailsActivity.AlertDialogFragment; /** * Demonstration of using fragments to implement different activity layouts. * This sample provides a different layout (and activity flow) when run in * landscape. */ public abstract class FragmentLayoutSupport extends FragmentActivity implements IDialog { private static final String SHOW_ALERT_DIALOG = "showAlertDialog"; private static final String SHOW_PROGRESS_DIALOG = "showProgressDialog"; static AlertDialogFragment dialogFragment = null; private FragmentManager getFragmentmanager() { return getSupportFragmentManager(); } @Override public void showAlertDialog(Bundle args) { dialogFragment = DetailsActivity.AlertDialogFragment.newInstance(args); dialogFragment.show(getFragmentmanager(), SHOW_ALERT_DIALOG); } @Override public void showCustomAlertDialog(Bundle args) { args.putBoolean(CUSTOMIZED, true); dialogFragment = DetailsActivity.AlertDialogFragment.newInstance(args); dialogFragment.show(getFragmentmanager(), SHOW_ALERT_DIALOG); } @Override public void showProgressDialog(Bundle args) { dialogFragment = DetailsActivity.AlertDialogFragment.newInstance(args); dialogFragment.show(getFragmentmanager(), SHOW_PROGRESS_DIALOG); } @Override public boolean isCancelled() { if (dialogFragment != null) { return dialogFragment.isCancelled(); } return false; } @Override public void dismissDialog() { if (dialogFragment != null) { dialogFragment.dismiss(); } dialogFragment = null; } /** * This is a secondary activity, to show what the user has selected when the * screen is not large enough to show it all in one activity. */ public static class DetailsActivity extends ActionBarActivitySupport implements IDocumentInterface, IDialog { private FragmentManager getFragmentmanager() { return getSupportFragmentManager(); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { document.onActivityResult(requestCode, resultCode, data); } @Override public boolean isCancelled() { if (dialogFragment != null) { return dialogFragment.isCancelled(); } return false; } @Override public void dismissDialog() { if (dialogFragment != null) { dialogFragment.dismiss(); } dialogFragment = null; } @Override public void showAlertDialog(Bundle args) { dialogFragment = AlertDialogFragment.newInstance(args); dialogFragment.show(getFragmentmanager(), SHOW_ALERT_DIALOG); } @Override public void showCustomAlertDialog(Bundle args) { args.putBoolean(CUSTOMIZED, true); dialogFragment = DetailsActivity.AlertDialogFragment.newInstance(args); dialogFragment.show(getFragmentmanager(), SHOW_ALERT_DIALOG); } @Override public void showProgressDialog(Bundle args) { dialogFragment = AlertDialogFragment.newInstance(args); dialogFragment.show(getFragmentmanager(), SHOW_PROGRESS_DIALOG); } public static class AlertDialogFragment extends DialogFragment { private boolean cancelled = false; private ICustomAlertDialog customAlertDialog = null; public boolean isCancelled() { return cancelled; } public static AlertDialogFragment newInstance(Bundle args) { AlertDialogFragment frag = new AlertDialogFragment(); frag.setArguments(args); return frag; } @Override public void show(android.support.v4.app.FragmentManager manager, String tag) { FragmentTransaction fragmentTransaction = manager.beginTransaction(); fragmentTransaction.add(this, tag); fragmentTransaction.commitAllowingStateLoss(); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { if (getArguments().getBoolean(CUSTOMIZED, false) && getActivity() instanceof IDocumentInterface) { IDocumentInterface documentInterface = (IDocumentInterface) getActivity(); Document document = documentInterface.getInterface().getDocument(); customAlertDialog = (ICustomAlertDialog) document; } // re-assign this as a new one is created if we're tilting the device dialogFragment = this; String title = getArguments().getString(TITLE); String message = getArguments().getString(MESSAGE); if (SHOW_ALERT_DIALOG.equalsIgnoreCase(getTag())) { int icon = getArguments().getInt(ICON); final int positiveButton = getArguments().getInt(POSITIVE_BUTTON); final int negativeButton = getArguments().getInt(NEGATIVE_BUTTON); final int id = getArguments().getInt(ID); AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity()).setIcon(icon) .setTitle(title); if (positiveButton != 0) { alertDialogBuilder.setPositiveButton(positiveButton, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { IDocument doc = document; if (doc == null) { Activity activity = getActivity(); if (activity instanceof IDocument) { doc = (IDocument) activity; } } if (doc != null) { doc.doDialogPositiveClick(id); } } }); } if (negativeButton != 0) { alertDialogBuilder.setNegativeButton(negativeButton, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { IDocument doc = document; if (doc == null) { Activity activity = getActivity(); if (activity instanceof IDocument) { doc = (IDocument) activity; } } if (doc != null) { doc.doDialogNegativeClick(id); } } }); } if (message != null && !message.isEmpty()) { alertDialogBuilder.setMessage(message); } if (customAlertDialog != null) { View view = customAlertDialog.getCustomDialogView(getActivity().getLayoutInflater(), getArguments()); if (view != null) { alertDialogBuilder.setView(view); } } return alertDialogBuilder.create(); } String cancel = getArguments().getString(CANCEL); boolean indeterminate = getArguments().getBoolean(INDETERMINANT); boolean cancelabel = getArguments().getBoolean(CANCELABLE); ProgressDialog progressDialog = new ProgressDialogEx(getActivity(), document, this); progressDialog.setTitle(title); progressDialog.setMessage(message); progressDialog.setCancelable(cancelabel); progressDialog.setButton(ProgressDialog.BUTTON_NEGATIVE, cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { IDocument doc = document; if (doc == null) { Activity activity = getActivity(); if (activity instanceof IDocument) { doc = (IDocument) activity; } } if (doc != null && which == ProgressDialog.BUTTON_NEGATIVE) { cancelled = true; doc.doProgressDialogCancel(); } } }); progressDialog.setProgressStyle( indeterminate ? ProgressDialog.STYLE_SPINNER : ProgressDialog.STYLE_HORIZONTAL); return progressDialog; } /** * This is needed as there where no other way to catch the back-button * while the dialog is showing off. */ class ProgressDialogEx extends ProgressDialog { private IDocument document; private AlertDialogFragment alertDialogFragment; public ProgressDialogEx(Context context, IDocument document, AlertDialogFragment alertDialogFragment) { super(context); this.document = document; this.alertDialogFragment = alertDialogFragment; } @Override public void onBackPressed() { super.onBackPressed(); if (document != null) { document.doProgressDialogCancel(); alertDialogFragment.cancelled = true; } } } } private ActionBarHelper documentActionBarHelper; private Activity documentActivity; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(document, savedInstanceState); if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { // If the screen is now in landscape mode, we can show the // dialog in-line with the list so we don't need this activity. finish(); return; } // One fragment display -> assign the details actionbar documentActivity = document.replaceActivity(this); documentActionBarHelper = document.replaceActionBarHelper(getActionBarHelper()); getActionBarHelper().setDisplayHomeAsUpEnabled(true); if (savedInstanceState == null) { // During initial setup, plug in the details fragment. DetailsFragment details = new DetailsFragment(); details.setArguments(getIntent().getExtras()); getFragmentmanager().beginTransaction().add(android.R.id.content, details).commit(); } } /* * (non-Javadoc) * * @see android.support.v4.app.FragmentActivity#onDestroy() */ @Override protected void onDestroy() { super.onDestroy(); document.replaceActivity(documentActivity); document.replaceActionBarHelper(documentActionBarHelper); } @Override public IDocument getInterface() { return document; } } /** * This is the secondary fragment, displaying the details of a particular * item. */ public static class DetailsFragment extends Fragment { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { document.onActivityResult(requestCode, resultCode, data); } /** * Create a new instance of DetailsFragment, initialized to show the text at * 'group' 'index'. */ public static DetailsFragment newInstance(int group, int index) { DetailsFragment f = new DetailsFragment(); // Supply index input as an argument. Bundle args = new Bundle(); args.putInt(ExpandableDocument.CURRENT_GROUP_POSITION, group); args.putInt(ExpandableDocument.CURRENT_CHILD_POSITION, index); f.setArguments(args); return f; } public int getShownIndex() { return getArguments().getInt(ExpandableDocument.CURRENT_CHILD_POSITION, 0); } public int getShownGroup() { return getArguments().getInt(ExpandableDocument.CURRENT_GROUP_POSITION, 0); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (container == null) { // We have different layouts, and in one of them this // fragment's containing frame doesn't exist. The fragment // may still be created from its saved state, but there is // no reason to try to create its view hierarchy because it // won't be displayed. Note this is not needed -- we could // just run the code below, where we would create and return // the view hierarchy; it would just never be used. return null; } // If we're actually a fragment in the activity holding the two fragments, // this will work If we're here as a new activity, i.e. pre-ics not // orientation horizontal, we don't know who's responsible for starting // the list-fragment. Activity activity = getActivity(); if (activity instanceof IDetailsView) { IDetailsView detailsView = (IDetailsView) activity; return detailsView.getDetailsView(getArguments()); } if (null != detailsView) { return detailsView.getDetailsView(getArguments()); } return null; } } /** * This is the "top-level" fragment, showing a list of items that the user can * pick. Upon picking an item, it takes care of displaying the data to the * user as appropriate based on the current UI layout. */ public static class TitlesExpandableFragment extends ExpandableListFragmentSupport { int groupPosition = ExpandableListAdapter.UNSELECT; int childPosition = ExpandableListAdapter.UNSELECT; boolean mDualPane; @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { document.onActivityResult(requestCode, resultCode, data); } @Override public void onDestroy() { super.onDestroy(); Activity activity = getActivity(); if (activity instanceof IDocument) { document = (IDocument) activity; } } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); Activity activity = getActivity(); if (activity instanceof IListAdapter) { IListAdapter mainActivity = (IListAdapter) activity; setListAdapter(mainActivity.getExpandableListAdapter()); } if (activity instanceof IDocument) { document = (IDocument) activity; } // Check to see if we have a frame in which to embed the details // fragment directly in the containing UI. View detailsFrame = getActivity().findViewById(R.id.details); mDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE; childPosition = document.getCurrentChildPosition(); groupPosition = document.getCurrentGroupPosition(); showSelected(groupPosition, childPosition); if (mDualPane) { // In dual-pane mode, the list view highlights the selected item. getExpandableListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); // Make sure our UI is in the correct state. showDetails(groupPosition, childPosition); } } @Override public void onGroupCollapse(int arg0) { super.onGroupCollapse(arg0); if (mDualPane) { showDetails(ExpandableListAdapter.UNSELECT, ExpandableListAdapter.UNSELECT); } } /* * (non-Javadoc) * * @see * com.aboveware.actionbar.support.ExpandableListFragmentSupport#onChildClick * (android.widget.ExpandableListView, android.view.View, int, int, long) */ @Override public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { if (!super.onChildClick(parent, v, groupPosition, childPosition, id)) { showDetails(groupPosition, childPosition); } return true; } /** * Helper function to show the details of a selected item, either by * displaying a fragment in-place in the current UI, or starting a whole new * activity in which it is displayed. */ public void showDetails(int group, int index) { groupPosition = group; childPosition = index; if (mDualPane) { // We can display everything in-place with fragments, so update // the list to highlight the selected item and show the data. getExpandableListView().setItemChecked(index, true); // Check what fragment is currently shown, replace if needed. DetailsFragment details = (DetailsFragment) getFragmentManager().findFragmentById(R.id.details); // if (details == null || details.getShownIndex() != index || // details.getShownGroup() != groupPosition) { // Make new fragment to show this selection. details = DetailsFragment.newInstance(groupPosition, childPosition); // Execute a transaction, replacing any existing fragment // with this one inside the frame. FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.replace(R.id.details, details); ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); ft.commit(); // } } else { // Otherwise we need to launch a new activity to display // the dialog fragment with selected text. // When the details fragment is later created we need to know from // which list fragment. Activity activity = getActivity(); if (activity instanceof IDetailsView) { detailsView = (IDetailsView) activity; } if (activity instanceof IDocument) { document = (IDocument) activity; } Intent intent = new Intent(); intent.setClass(getActivity(), DetailsActivity.class); intent.putExtra(ExpandableDocument.CURRENT_GROUP_POSITION, groupPosition); intent.putExtra(ExpandableDocument.CURRENT_CHILD_POSITION, childPosition); startActivity(intent); } } @Override public void setSelected(int groupPosition, int childPosition) { showDetails(groupPosition, childPosition); } @Override public void showSelected(int groupPosition, int childPosition) { if (groupPosition != ExpandableListAdapter.UNSELECT && childPosition != ExpandableListAdapter.UNSELECT) { getExpandableListView().expandGroup(groupPosition); getExpandableListView().setSelectedChild(groupPosition, childPosition, true); long packedPosition = ExpandableListView.getPackedPositionForChild(groupPosition, childPosition); getExpandableListView() .smoothScrollToPosition(getExpandableListView().getFlatListPosition(packedPosition)); } } } public static class TitlesFragment extends ListFragment { int childPosition = ExpandableListAdapter.UNSELECT; boolean mDualPane; @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { document.onActivityResult(requestCode, resultCode, data); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // Populate list with our static array of titles. Activity activity = getActivity(); if (activity instanceof IListAdapter) { IListAdapter mainActivity = (IListAdapter) activity; setListAdapter(mainActivity.getListAdapter()); } if (activity instanceof IDocument) { document = (IDocument) activity; } // Check to see if we have a frame in which to embed the details // fragment directly in the containing UI. View detailsFrame = getActivity().findViewById(R.id.details); mDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE; childPosition = document.getCurrentChildPosition(); if (mDualPane) { // In dual-pane mode, the list view highlights the selected item. getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); // Make sure our UI is in the correct state. showDetails(childPosition); } } @Override public void onListItemClick(ListView l, View v, int position, long id) { showDetails(position); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); document.setCurrentChildPosition(childPosition); } /** * Helper function to show the details of a selected item, either by * displaying a fragment in-place in the current UI, or starting a whole new * activity in which it is displayed. */ void showDetails(int index) { childPosition = index; if (mDualPane) { // We can display everything in-place with fragments, so update // the list to highlight the selected item and show the data. getListView().setItemChecked(index, true); // Check what fragment is currently shown, replace if needed. DetailsFragment details = (DetailsFragment) getFragmentManager().findFragmentById(R.id.details); if (details == null || details.getShownIndex() != index) { // Make new fragment to show this selection. details = DetailsFragment.newInstance(0, index); // Execute a transaction, replacing any existing fragment // with this one inside the frame. FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.replace(R.id.details, details); ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); ft.commit(); } } else { // Otherwise we need to launch a new activity to display // the dialog fragment with selected text. // When the details fragment is later created we need to know from // which list fragment. Activity activity = getActivity(); if (activity instanceof IDetailsView) { detailsView = (IDetailsView) activity; } if (activity instanceof IDocument) { document = (IDocument) activity; } Intent intent = new Intent(); intent.setClass(getActivity(), DetailsActivity.class); intent.putExtra(ExpandableDocument.CURRENT_CHILD_POSITION, index); startActivity(intent); } } } /** * This is an ugly one. It's only used when we're an pre-ics, non-horizontal * mode and we've selected a list item. It is just used during the creation of * the details view so it ought to stay valid long enough. TODO - If I ever * come up with a better solution - implement that! */ private static IDetailsView detailsView; private static IDocument document; }