Java tutorial
/* * AndFHEM - Open Source Android application to control a FHEM home automation * server. * * Copyright (c) 2011, Matthias Klass or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Inc. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU GENERAL PUBLIC LICENSE, as published by the Free Software Foundation. * * This program 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 this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package li.klass.fhem.activities.core; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import java.io.Serializable; import java.lang.reflect.Constructor; import li.klass.fhem.AndFHEMApplication; import li.klass.fhem.R; import li.klass.fhem.constants.Actions; import li.klass.fhem.constants.BundleExtraKeys; import li.klass.fhem.fragments.FragmentType; import li.klass.fhem.fragments.core.BaseFragment; import li.klass.fhem.util.Reject; import li.klass.fhem.util.ViewUtil; public class TopLevelFragment extends Fragment implements Serializable { public static final String INITIAL_FRAGMENT_TYPE_KEY = "initialFragmentType"; public static final String LAST_SWITCH_TO_BUNDLE_KEY = "lastBundle"; public static final String NAVIGATION_TAG = "NAVIGATION"; public static final String CONTENT_TAG = "CONTENT"; private transient FragmentType initialFragmentType; private BroadcastReceiver broadcastReceiver; public static final IntentFilter FILTER = new IntentFilter(); private static FragmentType currentTopLevelFragmentType = null; static { FILTER.addAction(Actions.SWITCH_TO_INITIAL_FRAGMENT); FILTER.addAction(Actions.SHOW_FRAGMENT); FILTER.addAction(Actions.TOP_LEVEL_BACK); } private Bundle lastSwitchToBundle; private int contentId; private int navigationId; private static final String TAG = TopLevelFragment.class.getName(); public TopLevelFragment() { setRetainInstance(true); } public TopLevelFragment(FragmentType initialFragmentType) { this(); this.initialFragmentType = initialFragmentType; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (savedInstanceState != null) { if (savedInstanceState.containsKey(INITIAL_FRAGMENT_TYPE_KEY)) { initialFragmentType = FragmentType.valueOf(savedInstanceState.getString(INITIAL_FRAGMENT_TYPE_KEY)); } if (savedInstanceState.containsKey(LAST_SWITCH_TO_BUNDLE_KEY)) { lastSwitchToBundle = savedInstanceState.getBundle(LAST_SWITCH_TO_BUNDLE_KEY); } } View view = inflater.inflate(R.layout.content_view, null); Reject.ifNull(view); View navigationView = view.findViewById(R.id.navigation); View contentView = view.findViewById(R.id.content); contentId = ViewUtil.getPseudoUniqueId(view, container); contentView.setId(contentId); navigationId = ViewUtil.getPseudoUniqueId(view, container); if (navigationView != null) navigationView.setId(navigationId); return view; } @Override public void onSaveInstanceState(Bundle outState) { if (outState != null) { if (initialFragmentType != null) { outState.putString(INITIAL_FRAGMENT_TYPE_KEY, initialFragmentType.name()); } outState.putBundle(LAST_SWITCH_TO_BUNDLE_KEY, lastSwitchToBundle); } super.onSaveInstanceState(outState); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); if (lastSwitchToBundle != null) { switchTo(lastSwitchToBundle); } else { switchTo(initialFragmentType, null); } } public void switchToInitialFragment() { if (getFragmentManager() == null) { AndFHEMApplication.getContext().sendBroadcast(new Intent(Actions.RELOAD)); return; } clearBackStack(); switchTo(initialFragmentType, null); } private void clearBackStack() { int entryCount = getFragmentManager().getBackStackEntryCount(); for (int i = 0; i < entryCount; i++) { getFragmentManager().popBackStack(); } } private void switchTo(Bundle bundle) { FragmentType fragmentType; if (bundle.containsKey(BundleExtraKeys.FRAGMENT)) { fragmentType = (FragmentType) bundle.getSerializable(BundleExtraKeys.FRAGMENT); } else { String fragmentName = bundle.getString(BundleExtraKeys.FRAGMENT_NAME); fragmentType = FragmentType.getFragmentFor(fragmentName); } lastSwitchToBundle = bundle; switchTo(fragmentType, bundle); } public void switchTo(FragmentType fragmentType, Bundle data) { if (fragmentType.isTopLevelFragment()) { clearBackStack(); } BaseFragment contentFragment = createContentFragment(fragmentType, data); BaseFragment navigationFragment = createNavigationFragment(fragmentType, data); setContent(navigationFragment, contentFragment); } public boolean back() { String currentName = getLastTransactionName(); boolean topLevelFragment = FragmentType.getFragmentFor(currentName).isTopLevelFragment(); if (getFragmentManager().getBackStackEntryCount() == 0 || topLevelFragment) { getActivity().finish(); return false; } else { boolean found = false; while (getFragmentManager().getBackStackEntryCount() != 0) { String lastTransactionName = getLastTransactionName(); if (!currentName.equals(lastTransactionName)) { found = true; break; } getFragmentManager().popBackStackImmediate(); } if (!found) { getActivity().finish(); return true; } BaseFragment navigationFragment = (BaseFragment) getFragmentManager().findFragmentByTag(NAVIGATION_TAG); updateNavigationVisibility(navigationFragment); return true; } } private String getLastTransactionName() { return getFragmentManager().getBackStackEntryAt(getFragmentManager().getBackStackEntryCount() - 1) .getName(); } @Override public void onResume() { super.onResume(); } @Override public void onAttach(Activity activity) { super.onAttach(activity); if (broadcastReceiver == null) { broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (!isVisible() || !isAdded() || isRemoving() || isDetached() || isHidden()) return; if (getView().findViewById(contentId) == null) return; String action = intent.getAction(); if (action == null) { return; } if (action.equals(Actions.SWITCH_TO_INITIAL_FRAGMENT)) { String fragmentName = intent.getStringExtra(BundleExtraKeys.FRAGMENT); FragmentType fragment = FragmentType.valueOf(fragmentName); if (fragment != initialFragmentType) { return; } switchToInitialFragment(); currentTopLevelFragmentType = initialFragmentType; } if (currentTopLevelFragmentType != null && currentTopLevelFragmentType != initialFragmentType) { return; } if (action.equals(Actions.SHOW_FRAGMENT)) { Bundle bundle = intent.getExtras(); switchTo(bundle); } else if (action.equals(Actions.TOP_LEVEL_BACK)) { String fragmentName = intent.getStringExtra(BundleExtraKeys.FRAGMENT); FragmentType fragment = FragmentType.valueOf(fragmentName); if (fragment == initialFragmentType) { back(); } } currentTopLevelFragmentType = initialFragmentType; } }; } getActivity().registerReceiver(broadcastReceiver, FILTER); } private BaseFragment createNavigationFragment(FragmentType fragmentType, Bundle data) { View navigationView = getView().findViewById(navigationId); if (navigationView == null) { return null; } try { Class<? extends BaseFragment> navigationClass = fragmentType.getNavigationClass(); if (navigationClass == null) { navigationView.setVisibility(View.GONE); return null; } navigationView.setVisibility(View.VISIBLE); return createFragmentForClass(data, navigationClass); } catch (Exception e) { Log.e(TAG, "cannot instantiate fragment", e); return null; } } private void setContent(BaseFragment navigationFragment, BaseFragment contentFragment) { boolean hasNavigation = updateNavigationVisibility(navigationFragment); FragmentManager fragmentManager = getFragmentManager(); if (fragmentManager == null) { Log.e(TAG, "fragment manager is null in #setContent"); return; } FragmentTransaction transaction = fragmentManager.beginTransaction() .addToBackStack(contentFragment.getClass().getName()); if (hasNavigation) { transaction.replace(navigationId, navigationFragment, NAVIGATION_TAG).replace(contentId, contentFragment, CONTENT_TAG); } else { transaction.replace(contentId, contentFragment); } transaction.commit(); } private boolean updateNavigationVisibility(BaseFragment navigationFragment) { View view = getView(); boolean hasNavigation = false; View navigationView = view.findViewById(navigationId); if (navigationView != null) { if (navigationFragment == null) { navigationView.setVisibility(View.GONE); } else { navigationView.setVisibility(View.VISIBLE); hasNavigation = true; } } return hasNavigation; } private BaseFragment createContentFragment(FragmentType fragmentType, Bundle data) { if (fragmentType == null) { getActivity().sendBroadcast(new Intent(Actions.RELOAD)); return null; } try { Class<? extends BaseFragment> fragmentClass = fragmentType.getContentClass(); return createFragmentForClass(data, fragmentClass); } catch (Exception e) { Log.e(TAG, "cannot instantiate fragment", e); return null; } } private BaseFragment createFragmentForClass(Bundle data, Class<? extends BaseFragment> fragmentClass) throws Exception { if (fragmentClass == null) return null; Constructor<? extends BaseFragment> constructor = fragmentClass.getConstructor(Bundle.class); return constructor.newInstance(data); } @Override public void onDetach() { super.onDetach(); getActivity().unregisterReceiver(broadcastReceiver); } }