net.lp.actionbarpoirot.actions.UiActionManager.java Source code

Java tutorial

Introduction

Here is the source code for net.lp.actionbarpoirot.actions.UiActionManager.java

Source

/*   
 *     Copyright (C) 2014 pjv (and others)
 * 
 *     This file is part of ActionBarPoirot.
 *
 *   ActionBarPoirot is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   ActionBarPoirot is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with ActionBarPoirot.  If not, see <http://www.gnu.org/licenses/>.
 */
package net.lp.actionbarpoirot.actions;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;

import net.lp.actionbarpoirot.PoirotWindow;
import net.lp.actionbarpoirot.helpers.ActivityHelper.ActivityHelperUser;
import net.lp.actionbarpoirot.util.UiUtilities;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;

/**
 * Handles attachment of {@link UiAction}s to {@link Activity}s.
 * 
 * @author pjv
 * 
 */
public class UiActionManager<C extends FragmentActivity & PoirotWindow & ActivityHelperUser> {

    /**
     * Represents an {@link Activity} that has action items and uses the {@link UiActionManager}. It also has call-throughs "lifecycle" methods for the Fragment to send through (for action items) to the Activity. 
     */
    public static interface ActionManagerUser extends ActivityHelperUser {
        public void onFragmentPrepareOptionsMenu(Menu menu);

        public void onFragmentCreateOptionsMenu(Menu menu);

        public boolean onFragmentContextItemSelectedWithMultipleSelection(MenuItem item,
                LinkedHashSet<Uri> mSelectedSessionPositionsUris);

        public int addAndManageActionItem(UiAction action);

        public int removeClientActionItems(String clientReference);

        public void onFragmentActivityResult(int requestCode, int resultCode, Intent data);

        public UiActionManager getUiActionManager();
    }

    /**
     * Represents an {@link Activity} that has a CAB (or context menu) and uses the {@link UiActionManager}.
     */
    public interface ActionModeUser {
        public boolean onCreateContextMenuInner(Menu menu, Uri uri, ArrayList<Uri> multipleUris);

        public Uri getContextItemUri(int position);

        public UiActionManager getUiActionManager();

        public String getContextualActionBarTitle(int numSelected);
    }

    /**
     * Represents an {@link Activity} that has a CAB with multiple selection possibility.
     */
    public interface MultiChoiceModeUser {
        public boolean onContextItemSelectedWithMultipleSelection(MenuItem item,
                LinkedHashSet<Uri> mSelectedSessionPositionsUris);
    }

    /**
     * Represents an {@link Activity} that has a search bar.
     */
    public interface SearchUser {
        public String getLocalClassName();
    }

    /**
     * Start value for counting the index (order for requestCode).
     */
    public static final int I_START = Menu.FIRST + 3;

    /*
     * Key to save the action's class type in the instance state.
     */
    public static final String KEY_UI_ACTION_CLASS = "ui_action_class";

    /*
     * Key to save the list of actions in the instance state.
     */
    public static final String KEY_UI_ACTION_LIST = "ui_action_bundle_list";

    public static final String TAG = PoirotWindow.TAG + "UiActionManager";

    /**
     * List of current managed actions.
     */
    public ArrayList<UiAction<C>> actions = new ArrayList<UiAction<C>>();

    /**
     * List of previously managed actions, that might still need to be cleaned up.
     */
    public ArrayList<UiAction<C>> oldActions = new ArrayList<UiAction<C>>();

    /**
     * Field to keep track at which index (order for requestCode) we are at.
     */
    public int i = I_START;

    /**
     * Constructor.
     * 
     * Without Up-navigation.
     */
    public UiActionManager() {

    }

    /**
     * Constructor. 
     * 
     * With Up-navigation.
     * 
     * @param clientClassName
     */
    public UiActionManager(String clientClassName) {
        // Always add custom UpNav action using the HoneycombICSUpNavigator for
        // >=11.
        if (UiUtilities.hasAcceptableNativeActionBar()) {
            final NavigateUpUiAction<C> upAction = new NavigateUpUiAction<C>(clientClassName);
            upAction.setOrder(android.R.id.home);
            actions.add(upAction);
        }
    }

    /**
     * Start managing a {@link UiAction}, if appropriate.
     * 
     * It will be added to a list of actions. 
     * 
     * @param action The action to be managed.
     * @return The index (order for requestCode usage) to use for the next {@link UiAction}. Returns 0 if not appropriate.
     */
    public int addAndManage(UiAction<C> action) {
        if (!action.isAppropriate(this)) {
            if (PoirotWindow.DEBUG)
                Log.d(TAG, "refused adding UiAction: " + action.toString());
            return 0;
        }
        int iOld = i;
        int nextOrder = action.setOrder(i);
        i = nextOrder;
        if (PoirotWindow.DEBUG)
            Log.d(TAG, "added UiAction: " + action.toString() + " order=" + i);
        return (actions.add(action) ? iOld : 0);
    }

    /**
     * Destructor. Destroys and clears the managed {@link UiAction}s.
     */
    public void destruct() {
        for (Iterator<UiAction<C>> iterator = actions.iterator(); iterator.hasNext();) {
            UiAction<C> action = iterator.next();
            iterator.remove();
            action.destruct();
        }
        actions.clear();

        for (Iterator<UiAction<C>> iterator = oldActions.iterator(); iterator.hasNext();) {
            UiAction<C> action = iterator.next();
            iterator.remove();
            action.destruct();
        }
        oldActions.clear();
    }

    /**
     * Set all managed {@link UiAction}s as visible.
     * 
     * @param menu Menu (CAB) to set them visible for.
     */
    public void enableAllActionItems(Menu menu) {
        for (UiAction<C> action : actions) {
            action.setActionItemVisible(menu, true);
        }
    }

    /**
     * Set only the managed {@link UiAction}s that fit in with multiple selection CAB as visible. The rest invisible.
     * 
     * @param menu Menu (CAB) to set them visible for.
     */
    public void enableOnlyActionItemsForMultipleSelection(Menu menu) {
        for (UiAction<C> action : actions) {
            if (action.shouldShowWhenMultipleSelected()) {
                action.setActionItemVisible(menu, true);
            } else {
                action.setActionItemVisible(menu, false);
            }
        }
    }

    /**
     * Whether this other {@link UiAction} is already managed.
     * 
     * @param otherAction
     * @return
     */
    public boolean has(UiAction<C> otherAction) {
        // actions then
        for (Iterator<UiAction<C>> iterator = actions.iterator(); iterator.hasNext();) {
            UiAction<C> action = iterator.next();
            if (action.equals(otherAction)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Pass on what should happen in onActivityResult to the matching {@link UiAction}.
     * 
     * "Lifecycle" method onActivityResult from Activity should call-through to here.
     * 
     * @param requestCode
     * @param resultCode
     * @param data
     * @param context
     */
    public void onActivityResult(int requestCode, int resultCode, Intent data, C context) {
        if ((actions.size() > 1 && UiUtilities.hasAcceptableNativeActionBar())
                || (actions.size() > 0 && !UiUtilities.hasAcceptableNativeActionBar())) {// NavigateUpUiAction
            // is almost always
            // there
            for (UiAction<C> action : actions) {
                action.onActivityResult(requestCode, resultCode, data, context);
            }
        } else {
            for (UiAction<C> action : oldActions) {
                action.onActivityResult(requestCode, resultCode, data, context);
            }
        }
    }

    /**
     * Pass on what should happen in onContextItemSelected to the matching {@link UiAction}, in case of multiple selection.
     * 
     * "Lifecycle" method onContextItemSelected from Activity should call-through to here.
     * 
     * @param item
     * @param context
     * @param mSelectedSessionPositionsUris
     * @return
     */
    public boolean onContextItemSelectedWithMultipleSelection(MenuItem item, C context,
            LinkedHashSet<Uri> mSelectedSessionPositionsUris) {
        for (UiAction<C> action : actions) {
            if (action.onContextItemSelectedWithMultipleSelection(item, context, mSelectedSessionPositionsUris))
                return true;
        }
        return false;
    }

    /**
     * Pass on what should happen in onCreateOptionsMenu to the matching {@link UiAction}.
     * 
     * "Lifecycle" method onCreateOptionsMenu from Activity should call-through to here.
     * 
     * @param menu
     * @param context
     */
    public void onCreateOptionsMenu(Menu menu, C context) {
        for (UiAction<C> action : actions) {
            action.onCreateOptionsMenu(menu, context);
        }
    }

    /**
     * For onCreateOptionsMenu, generate the category-alternative intent-based items.
     * 
     * @param menu
     * @param context
     * @param uri
     * @param component
     * @param extraCategories
     */
    public void onCreateAlternativeOptionsMenu(Menu menu, C context, Uri uri, Class<? extends C> component,
            List<String> extraCategories) {
        // Generate any additional actions that can be performed on the
        // overall list. In a normal install, there are no additional
        // actions found here, but this allows other applications to extend
        // our menu with their own actions.
        Intent intent = new Intent(null, uri);
        intent.addCategory(Intent.CATEGORY_ALTERNATIVE);
        if (extraCategories != null) {
            for (String extraCategory : extraCategories) {
                intent.addCategory(extraCategory);
            }
        }
        menu.addIntentOptions(Menu.CATEGORY_ALTERNATIVE, 0, UiAction.ORDER_SIXTH_OR_ALTERNATIVE,
                new ComponentName(context, component), null, intent, 0, null);
    }

    /**
     * Pass on what should happen in onKeyUp to the matching {@link UiAction}.
     * 
     * "Lifecycle" method onKeyUp from Activity should call-through to here.
     * 
     * @param keyCode
     * @param event
     * @param context
     * @return
     */
    public boolean onKeyUp(int keyCode, KeyEvent event, C context) {
        for (UiAction<C> action : actions) {
            if (action.onKeyUp(keyCode, event, context))
                return true;
        }
        return false;
    }

    /**
     * Pass on what should happen in onOptionsItemSelected to the matching {@link UiAction}.
     * 
     * "Lifecycle" method onOptionsItemSelected from Activity should call-through to here.
     * 
     * @param item
     * @param context
     * @return
     */
    public boolean onOptionsItemSelected(MenuItem item, C context) {
        for (UiAction<C> action : actions) {
            if (action.onOptionsItemSelected(item, context))
                return true;
        }
        return false;
    }

    /**
     * Pass on what should happen in onPrepareOptionsMenu to the matching {@link UiAction}.
     * 
     * "Lifecycle" method onPrepareOptionsMenu from Activity should call-through to here.
     * 
     * @param menu
     * @param context
     */
    public void onPrepareOptionsMenu(Menu menu, C context) {
        for (UiAction<C> action : actions) {
            action.onPrepareOptionsMenu(menu, context);
        }
    }

    /**
     * Again add all action items to the action bar. It will clear the action bar first.
     * 
     * Compat and LeftNavBar only. This is for such state-based action bars only, not for an action bar that queries which items it should show, like the native action bar.
     */
    public void readdAllActionItems(C context) {
        context.getActivityHelper().clearCompatActionBarActionItems();
        context.getActivityHelper().clearLeftNavBarActionItems();

        for (UiAction<C> action : actions) {
            // LeftNavBar
            if (UiUtilities.hasHoneycombOrUp() && UiUtilities.isGoogleTV(context)
                    && action.getNavBarZoneType() != UiAction.ZONE_NONE
                    && action.isAppropriate(((UiActionManager.ActionManagerUser) context).getUiActionManager())) {
                action.addActionItemToLeftNavBar(context);
            }

            // CompatActionBar
            if (!UiUtilities.hasHoneycombOrUp()) {
                context.getActivityHelper().addActionItemToCompatActionBar(action);
            }
        }
    }

    /**
     * Remove the {@link UiAction}s with this clientReference. In effect, this allows to remove actions that were added from one Activity or Fragment. Useful when the Fragment is swapped out.
     * 
     * @param clientReference
     * @return
     */
    public int remove(String clientReference) {
        // old actions first
        for (Iterator<UiAction<C>> iterator = oldActions.iterator(); iterator.hasNext();) {
            UiAction<C> action = iterator.next();
            if (action.getClientReference().equals(clientReference)) {
                iterator.remove();
                action.destruct();
            }
        }

        // actions then
        int count = 0;
        for (Iterator<UiAction<C>> iterator = actions.iterator(); iterator.hasNext();) {
            UiAction<C> action = iterator.next();
            if (action.getClientReference().equals(clientReference)) {
                oldActions.add(action);
                iterator.remove();
                if (PoirotWindow.DEBUG)
                    Log.d(TAG, "removed UiAction: " + action.toString() + " clientReference=" + clientReference);
                count++;
            }
        }

        return count;
    }

    /**
     * Start counting the index (order for requestCode) from the start value again.
     * 
     * @return
     */
    public int resetCount() {
        i = I_START;
        return i;
    }

    /**
     * Get actions list for non-configuration instance state.
     * 
     * @return actions
     */
    public ArrayList<UiAction<C>> retainNonConfigurationInstance() {
        return actions;
    }

    //TODO: incorporate onCreateContextMenu() both for single and multiple.
}