com.aboveware.actionbar.support.FragmentLayoutSupport.java Source code

Java tutorial

Introduction

Here is the source code for com.aboveware.actionbar.support.FragmentLayoutSupport.java

Source

/*
 * 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;
}