Java tutorial
/* * Copyright 2013 serso aka se.solovyev * * 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 org.solovyev.android.messenger; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.view.ViewGroup; import com.actionbarsherlock.app.ActionBar; import com.actionbarsherlock.view.MenuItem; import com.github.rtyley.android.sherlock.roboguice.activity.RoboSherlockFragmentActivity; import com.google.common.base.Predicate; import com.google.inject.Inject; import org.solovyev.android.fragments.MultiPaneFragmentDef; import org.solovyev.android.messenger.accounts.AccountService; import org.solovyev.android.messenger.chats.ChatService; import org.solovyev.android.messenger.core.R; import org.solovyev.android.messenger.fragments.MessengerMultiPaneFragmentManager; import org.solovyev.android.messenger.fragments.PrimaryFragment; import org.solovyev.android.messenger.messages.EmptyFragment; import org.solovyev.android.messenger.messages.UnreadMessagesCounter; import org.solovyev.android.messenger.notifications.NotificationService; import org.solovyev.android.messenger.users.UserService; import org.solovyev.common.Builder; import org.solovyev.common.JPredicate; import roboguice.event.EventManager; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Stack; import static com.google.common.collect.Iterables.any; import static com.google.common.collect.Iterables.find; import static java.util.Arrays.asList; import static org.solovyev.android.messenger.App.newTag; public abstract class BaseFragmentActivity extends RoboSherlockFragmentActivity implements FragmentManager.OnBackStackChangedListener { /* ********************************************************************** * * CONSTANTS * ********************************************************************** */ protected final String TAG = newTag(this.getClass().getSimpleName()); /* ********************************************************************** * * AUTO INJECTED FIELDS * ********************************************************************** */ @Inject @Nonnull private UserService userService; @Inject @Nonnull private ChatService chatService; @Inject @Nonnull private AccountService accountService; @Inject @Nonnull private MultiPaneManager multiPaneManager; @Inject @Nonnull private MessengerListeners messengerListeners; @Inject @Nonnull private UnreadMessagesCounter unreadMessagesCounter; @Inject @Nonnull private NotificationService notificationService; /* ********************************************************************** * * OWN FIELDS * ********************************************************************** */ private int layoutId; @Nullable private ViewGroup secondPane; @Nullable private ViewGroup thirdPane; @Nonnull private final MessengerMultiPaneFragmentManager multiPaneFragmentManager; @Nonnull private final ActivityUi ui = new ActivityUi(this); /* ********************************************************************** * * CONSTRUCTORS * ********************************************************************** */ public BaseFragmentActivity() { this.layoutId = R.layout.mpp_main; this.multiPaneFragmentManager = new MessengerMultiPaneFragmentManager(this); } /* ********************************************************************** * * GETTERS/SETTERS * ********************************************************************** */ @Nonnull protected UserService getUserService() { return userService; } @Nonnull public ChatService getChatService() { return chatService; } @Nonnull public EventManager getEventManager() { return eventManager; } @Nonnull public AccountService getAccountService() { return accountService; } @Nonnull public MultiPaneManager getMultiPaneManager() { return multiPaneManager; } public boolean isDualPane() { return this.secondPane != null; } public boolean isTriplePane() { return this.thirdPane != null; } @Nonnull public MessengerMultiPaneFragmentManager getMultiPaneFragmentManager() { return multiPaneFragmentManager; } @Nonnull public MessengerListeners getMessengerListeners() { return messengerListeners; } public RoboListeners getListeners() { return ui.getListeners(); } /* ********************************************************************** * * LIFECYCLE * ********************************************************************** */ @Override protected void onCreate(Bundle savedInstanceState) { ui.onBeforeCreate(); super.onCreate(savedInstanceState); ui.onCreate(savedInstanceState); setContentView(layoutId); final ActionBar actionBar = getSupportActionBar(); actionBar.setDisplayUseLogoEnabled(false); actionBar.setDisplayHomeAsUpEnabled(false); actionBar.setDisplayShowHomeEnabled(true); actionBar.setDisplayShowTitleEnabled(true); getSupportFragmentManager().addOnBackStackChangedListener(this); this.secondPane = (ViewGroup) findViewById(R.id.content_second_pane); this.thirdPane = (ViewGroup) findViewById(R.id.content_third_pane); onBackStackChanged(); } protected void initFragments() { if (isDualPane()) { initDualPaneFragments(); } else { initMainPaneFragment(); } } private void initMainPaneFragment() { final FragmentManager fm = getSupportFragmentManager(); final MessengerMultiPaneFragmentManager multiPaneFragmentManager = getMultiPaneFragmentManager(); final Fragment mainFragment = fm.findFragmentById(R.id.content_first_pane); if (mainFragment != null) { final PrimaryFragment primaryFragment = getPrimaryFragment(mainFragment); if (primaryFragment != null) { final Stack<MultiPaneFragmentDef> fragmentDefs = new Stack<MultiPaneFragmentDef>(); while (true) { final Fragment secondFragment = fm.findFragmentById(R.id.content_second_pane); if (secondFragment != null) { // fragment should be copied before popping back stack final MultiPaneFragmentDef fragmentDef = newCopyingFragmentDef(secondFragment, true); if (!primaryFragment.isAddToBackStack()) { if (fm.popBackStackImmediate()) { tryPushSecondFragment(fragmentDefs, fragmentDef, secondFragment); } else { tryPushSecondFragment(fragmentDefs, fragmentDef, secondFragment); // nothing to pop => stop break; } } else { // primary fragment itself is on back stack => we cannot pop back stack as we can pop it. // let's just add latest fragment on the second pane and put it on on the stack tryPushSecondFragment(fragmentDefs, fragmentDef, secondFragment); break; } } else { // no second fragment => stop break; } } while (!fragmentDefs.isEmpty()) { multiPaneFragmentManager.setMainFragment(fragmentDefs.pop()); } } } } private void tryPushSecondFragment(@Nonnull Stack<MultiPaneFragmentDef> fragmentDefs, @Nonnull MultiPaneFragmentDef secondFragmentCopy, @Nonnull Fragment secondFragment) { if (!(secondFragment instanceof EmptyFragment)) { fragmentDefs.push(secondFragmentCopy); } } private void initDualPaneFragments() { final FragmentManager fm = getSupportFragmentManager(); // We need to be sure that the fragment which is shown on the main pane is primary. // If it's not a primary fragment we need to move it to the secondary pane. // To restore the primary fragment we just pop back stack (as primary fragment must be somewhere in back stack) // As we need to restore second fragment's state let's copy arguments and instance state and pass it to the newly created argument. final Stack<MultiPaneFragmentDef> fragmentDefs = new Stack<MultiPaneFragmentDef>(); while (true) { final Fragment mainFragment = fm.findFragmentById(R.id.content_first_pane); if (mainFragment != null) { if (!isPrimaryFragment(mainFragment)) { // NOTE: we must save local copies before popping the back stack as these values might change final MultiPaneFragmentDef fragmentDef = newCopyingFragmentDef(mainFragment, false); if (fm.popBackStackImmediate()) { fragmentDefs.push(fragmentDef); } else { // nothing to pop => stop if (fragmentDefs.isEmpty()) { final ActionBar.Tab selectedTab = getSupportActionBar().getSelectedTab(); if (selectedTab != null) { selectedTab.select(); } } break; } } else { // primary fragment => stop break; } } else { // main pane empty => stop break; } } boolean first = true; while (!fragmentDefs.isEmpty()) { MultiPaneFragmentDef fragmentDef = fragmentDefs.pop(); if (first) { first = false; fragmentDef = copy(fragmentDef, false); } else { fragmentDef = copy(fragmentDef, true); } getMultiPaneFragmentManager().setSecondFragment(fragmentDef); } } @Nonnull private MultiPaneFragmentDef newCopyingFragmentDef(@Nonnull final Fragment fragment, boolean addToBackStack) { final FragmentManager fm = getSupportFragmentManager(); final Fragment.SavedState fragmentSavedState = fm.saveFragmentInstanceState(fragment); final String fragmentTag = fragment.getTag(); final Bundle fragmentArguments = fragment.getArguments(); return MultiPaneFragmentDef.newInstance(fragmentTag, addToBackStack, new Builder<Fragment>() { @Nonnull @Override public Fragment build() { final Fragment newFragment = Fragment.instantiate(BaseFragmentActivity.this, fragment.getClass().getName(), fragmentArguments); newFragment.setInitialSavedState(fragmentSavedState); return newFragment; } }, null); } @Nonnull private MultiPaneFragmentDef copy(@Nonnull final MultiPaneFragmentDef fragmentDef, boolean addToBackStack) { return MultiPaneFragmentDef.newInstance(fragmentDef.getTag(), addToBackStack, new Builder<Fragment>() { @Nonnull @Override public Fragment build() { return fragmentDef.build(); } }, new JPredicate<Fragment>() { @Override public boolean apply(Fragment fragment) { return fragmentDef.canReuse(fragment); } }); } private boolean isPrimaryFragment(@Nonnull final Fragment fragment) { return any(asList(PrimaryFragment.values()), new Predicate<PrimaryFragment>() { @Override public boolean apply(PrimaryFragment pf) { return pf.getFragmentTag().equals(fragment.getTag()); } }); } @Nullable private PrimaryFragment getPrimaryFragment(@Nonnull final Fragment fragment) { return find(asList(PrimaryFragment.values()), new Predicate<PrimaryFragment>() { @Override public boolean apply(PrimaryFragment pf) { return pf.getFragmentTag().equals(fragment.getTag()); } }, null); } @Override protected void onResume() { super.onResume(); ui.onResume(); } @Override protected void onPause() { ui.onPause(); super.onPause(); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: if (!getMultiPaneFragmentManager().goBackImmediately()) { finish(); } return true; default: return super.onOptionsItemSelected(item); } } @Nullable public ActionBar.Tab findTabByTag(@Nonnull String tag) { final ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { for (int i = 0; i < actionBar.getTabCount(); i++) { final ActionBar.Tab tab = actionBar.getTabAt(i); if (tab != null && tag.equals(tab.getTag())) { return tab; } } } return null; } @Override public void onBackStackChanged() { final ActionBar actionBar = getSupportActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); } }