import android.support.v4.app.Fragment;
import android.util.Log;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Fragment with a workaround to handle menus in conjunction with MenuManagerActivity
*
* To make it work properly you need to assure that all fragments with option menu and that have nested fragments with option menu extend this fragment.
*
* Apparently when you detach a parent fragment the nested fragment is not detached and actually not even paused, nor the visibility hint is set to false.
* For this reason I had to handle an hierarchy check between fragments making the parent fragment notify children when he is going away and coming back
* and making children ask to be informed when the parent go away.
*
* I think I handled any fancy configuration now, not 100% sure tough. I'll keep improving if I found bugs. The nested fragment problem was an hard one to
* catch and fix, I wrote a good debug log that can be enabled by just setting a variable to true. Don't worry about performance since when compiling with
* that variable to false the code for logging is effectively removed, not even the if is evaluated.
*/
public class MenuDelegatorFragment extends Fragment {
public static final String LTAG = MenuDelegatorFragment.class.getSimpleName();
private boolean mHasMenu;
private final Set mChildrenShowingMenu = new HashSet();
private static final boolean DEBUG = false;
private void debugLog(String what) {
Log.v(LTAG, String.format("%s[%d] (%s): %s", mHasMenu ? "M" : "-", getId(), ((Object) this).getClass().getSimpleName(), what));
}
@Override
public void setHasOptionsMenu(boolean hasMenu) {
if (DEBUG) {
debugLog(String.format("setHasOptionsMenu(%b)", hasMenu));
}
// Note that I'm not calling the super constructor here!
// Cause I'll delegate the menu handling to the MenuManagerActivity instead of letting
// the framework do it!
mHasMenu = hasMenu;
if (mHasMenu && isHierarchyVisible()) {
showMenu();
} else {
hideMenu();
}
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
boolean wasUserVisible = getUserVisibleHint();
super.setUserVisibleHint(isVisibleToUser);
if (wasUserVisible == isVisibleToUser) {
return;
}
if (DEBUG) {
debugLog(String.format("setUserVisibleHint(%b)", isVisibleToUser));
}
if (mHasMenu) {
if (isVisibleToUser && isHierarchyVisible()) {
showMenu();
} else {
hideMenu();
}
}
if (isVisibleToUser) {
notifyChildrenToShowMenu();
} else {
notifyChildrenToHideMenu();
}
}
@Override
public void onPause() {
super.onPause();
if (DEBUG) {
debugLog("onPause()");
}
if (mHasMenu) {
hideMenu();
}
notifyChildrenToHideMenu();
}
@Override
public void onResume() {
if (DEBUG) {
debugLog("onResume()");
}
super.onResume();
if (mHasMenu && isHierarchyVisible()) {
showMenu();
}
notifyChildrenToShowMenu();
}
private boolean isHierarchyVisible() {
boolean visible = getUserVisibleHint() && isResumed();
Fragment parent = getParentFragment();
boolean hierarchyVisible;
if (parent == null) {
hierarchyVisible = visible;
} else if (parent instanceof MenuDelegatorFragment) {
hierarchyVisible = visible && ((MenuDelegatorFragment) parent).isHierarchyVisible();
} else {
Log.w(LTAG, String.format("non-%s in the hierarchy, can't grant menu will work correctly in every situation: %s", LTAG, ((Object) parent).getClass().getSimpleName()));
hierarchyVisible = visible && parent.getUserVisibleHint() && parent.isResumed();
}
if (DEBUG) {
debugLog(String.format("isHierarchyVisible() -> %b", hierarchyVisible));
}
return hierarchyVisible;
}
private void showMenu() {
if (((MenuManagerActivity) getActivity()).registerFragmentForOptionMenu(this)) {
if (DEBUG) {
debugLog("showMenu()");
}
if (getParentFragment() instanceof MenuDelegatorFragment) {
((MenuDelegatorFragment) getParentFragment()).addChildShowingMenu(this);
}
}
}
private void hideMenu() {
if (((MenuManagerActivity) getActivity()).unregisterFragmentForOptionMenu(this)) {
if (DEBUG) {
debugLog("hideMenu()");
}
if (getParentFragment() instanceof MenuDelegatorFragment) {
((MenuDelegatorFragment) getParentFragment()).removeChildShowingMenu(this);
}
}
}
private void parentIsGoingAway() {
if (DEBUG) {
debugLog("parentIsGoingAway()");
}
hideMenu();
}
private void parentIsComingBack() {
if (DEBUG) {
debugLog("parentIsComingBack()");
}
if (mHasMenu && isHierarchyVisible()) {
showMenu();
}
}
private void removeChildShowingMenu(MenuDelegatorFragment child) {
if (mChildrenShowingMenu.remove(child)) {
if (DEBUG) {
debugLog(String.format("removeChildShowingMenu([%s] %s)", child.getId(), ((Object) child).getClass().getSimpleName()));
}
}
}
private void addChildShowingMenu(MenuDelegatorFragment child) {
if (mChildrenShowingMenu.add(child)) {
if (DEBUG) {
debugLog(String.format("addChildShowingMenu([%s] %s)", child.getId(), ((Object) child).getClass().getSimpleName()));
}
}
}
private void notifyChildrenToHideMenu() {
if (mChildrenShowingMenu.size() == 0) {
return;
}
if (DEBUG) {
debugLog("notifyChildrenToHideMenu():" + mChildrenShowingMenu.size());
}
for (MenuDelegatorFragment child : mChildrenShowingMenu) {
child.parentIsGoingAway();
}
mChildrenShowingMenu.clear();
}
private void notifyChildrenToShowMenu() {
List children = getChildFragmentManager().getFragments();
if (children == null || children.size() == 0) {
return;
}
if (DEBUG) {
debugLog("notifyChildrenToShowMenu():" + children.size());
}
for (Fragment child : children) {
if (child instanceof MenuDelegatorFragment) {
((MenuDelegatorFragment) child).parentIsComingBack();
} else if (child.hasOptionsMenu()) {
Log.w(LTAG, String.format("non-%s in the hierarchy, can't grant menu will work correctly in every situation: %s", LTAG, ((Object) child).getClass().getSimpleName()));
}
}
}
}