Back to project page Resonos-Android-Framework.
The source code is released under:
Apache License
If you think the Android project Resonos-Android-Framework listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package com.resonos.apps.library; //from ww w . j av a 2 s .co m import java.util.HashMap; import java.util.Map; import android.content.Context; import android.content.res.Configuration; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.FragmentTransaction; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; import android.widget.FrameLayout; import com.actionbarsherlock.app.SherlockFragmentActivity; import com.actionbarsherlock.view.Window; import com.resonos.app.library.R; import com.resonos.apps.library.AlertFragment.Result; import com.resonos.apps.library.BaseFragment.FragmentAnimation; import com.resonos.apps.library.util.AppUtils; import com.resonos.apps.library.util.ErrorReporter; import com.resonos.apps.library.util.LifecycleTaskQueue; import com.resonos.apps.library.util.ParameterList; import com.resonos.apps.library.util.ViewServer; //---------------------------------------------------------------------- /** * This is the base activity for anything containing fragments and an actionbar. */ public abstract class FragmentBaseActivity extends SherlockFragmentActivity { // constants public static int AB_HEIGHT; public static final String STATE_APP = "appState"; // objects public App mApp; public Handler mHandler = new Handler(); private LifecycleTaskQueue mTaskQueue = new LifecycleTaskQueue(); // vars private boolean isLowMemory = false; private boolean isCreated = false, isStarted = false, isResumed = false; private boolean isChangingConfig = false; /** Parameters used to create a FragmentBaseActivity */ public enum Param { /** progress bar in actionbar */ PROGRESS, /** progress icon in actionbar */ PROGRESS_INDETERMINATE, /** action bar functions as an overlay, fragments are given spacers to compensate */ ACTION_BAR_OVERLAY, /** use this to designate that an activity is not the root of an application */ NON_ROOT_ACTIVITY, NON_FULL_SCREEN, DONT_KEEP_SCREEN_ON}; private ParameterList<Param> mParams; private Map<String, Object> mCustomRetain; private SizeFrameLayout mContentView; // abstract methods /** * @param savedInstanceState : The saved state, if we are restoring an activity, otherwise null * @return the an instance of a subclass of App. Create it if this is the root activity. * Return the root activity's instance otherwise. */ protected abstract App createApp(Bundle savedInstanceState); /** * @return return the root fragment of this activity */ protected abstract BaseFragment getMainFragment(); /** * @return the resource id of the layout for this app */ protected abstract int getLayoutID(); /** * @return the id of the ViewGroup that fragments will go into */ protected abstract int getFragmentContainerID(); /** * @return the id of the view that will be used as a spacer for fragments * when an action bar overlay is used */ protected abstract int getABSpacerID(); /** * @return whatever parameters you need for this activity * @see FragmentBaseActivity.Param */ protected abstract Param[] getParams(); /** * Enum representing the events we mark in activities as points which * queued tasks can run. */ public enum ActivityEvent {OnCreate, OnStart, OnResume, OnPause, OnStop, OnDestroy}; /** * Add a runnable to a queue to be run one single * occurrence the next time a distinct event occurs. * This task will NOT survive a configuration change * or past the onDestroy method. * @param event : the {@link ActivityEvent} to trigger the runnable. * @param task : the Runnable to run */ public void queueTask(ActivityEvent event, Runnable task) { mTaskQueue.addTask(event, task); } /** * Get the task queue * @return the {@link LifecycleTaskQueue} object */ public LifecycleTaskQueue getTaskQueue() { return mTaskQueue; } /** * Get a custom retained object. This function will return null * values by the time onResume is called. * @param key : app-wide string key * @return the object, or null */ public Object getCustomRetainedObject(String key) { return mCustomRetain == null ? null : mCustomRetain.get(key); } /** * Override this function to have access to retained data * before the super.onCreate function is called. * @param customRetain : the objects, or null if no retained data */ public void onLoadRetainedData(Map<String, Object> customRetain) { // } /** * Extended framelayout so we can know updates to the size of our app */ public class SizeFrameLayout extends FrameLayout { View realContent; /** * Create the extended frame layout * @param context : activity context * @param resID : the layout resource id */ public SizeFrameLayout(Context context, int resID) { super(context); realContent = LayoutInflater.from(context).inflate(resID, null); this.addView(realContent, new FrameLayout.LayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); } @Override public void onSizeChanged(int w, int h, int oldw, int oldh) { if (w > 0 && h > 0) { mApp.readScreenConfiguration(w, h); FragmentBaseActivity.this.onSizeChanged(w, h); } } } @Override protected void onCreate(Bundle savedInstanceState) { mParams = new ParameterList<Param>(getParams()); // Hide the window title. if (!mParams.has(Param.NON_FULL_SCREEN)) getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); if (!mParams.has(Param.DONT_KEEP_SCREEN_ON)) getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); boolean overlay = false; if (mParams.has(Param.PROGRESS)) requestWindowFeature(Window.FEATURE_PROGRESS); if (mParams.has(Param.PROGRESS_INDETERMINATE)) requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); if (mParams.has(Param.ACTION_BAR_OVERLAY)) { overlay = true; requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY); requestWindowFeature(Window.FEATURE_ACTION_MODE_OVERLAY); } getWindow().setFormat(PixelFormat.RGBA_8888); // locks in state getWindow().getDecorView(); Bundle appState = (savedInstanceState == null) ? null : savedInstanceState.getBundle(STATE_APP); mApp = createApp(appState); isChangingConfig = false; // retain instance @SuppressWarnings("unchecked") Map<String,Object> customRetain = (Map<String,Object>)getLastCustomNonConfigurationInstance(); if (customRetain != null) mCustomRetain = customRetain; onLoadRetainedData(customRetain); super.onCreate(savedInstanceState); isCreated = true; // set some global vars AB_HEIGHT = getActionBarHeight(); mContentView = new SizeFrameLayout(this, getLayoutID()); setContentView(mContentView); if (savedInstanceState == null) { toFirstFragment(getMainFragment()); } View spacer = findViewById(getABSpacerID()); spacer.getLayoutParams().height = overlay ? AB_HEIGHT : 0; spacer.requestLayout(); // for hierarchy viewer if (mApp.isDebugging()) ViewServer.get(this).addWindow(this); mTaskQueue.runEvent(ActivityEvent.OnCreate); if (mParams.has(Param.PROGRESS_INDETERMINATE)) this.setSupportProgressBarIndeterminateVisibility(false); } /** * Get the default actionbar height. This may not reflect the actual actionbar height * @return default abar height */ public int getActionBarHeight() { return getResources().getDimensionPixelSize(R.dimen.abs__action_bar_default_height); } /** * Override this to get notifications about size changes to the app * @param w : width * @param h : height */ public void onSizeChanged(int w, int h) { // } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBundle(STATE_APP, mApp.onSaveInstanceState()); } @Override public final Object onRetainCustomNonConfigurationInstance() { isChangingConfig = true; Map<String,Object> customRetain = new HashMap<String,Object>(); onRetainCustomObjects(customRetain); BaseFragment fCur = getCurFragment(); if (fCur != null) fCur.onRetainCustomObjects(customRetain); return customRetain; } /** * Override this to retain custom objects across instances rather than the whole fragment, * as the API allows * @param customRetain : the map to put objects in */ public void onRetainCustomObjects(Map<String, Object> customRetain) { // } /** * Sets whether the indeterminate progress bar is visible, * clearing any waiting message * @param visible */ public void setActionBarProgressVisible(boolean visible) { setSupportProgressBarIndeterminateVisibility(visible); if (!visible) getSupportActionBar().setTitle( (getCurFragment() == null || getCurFragment().getTitle() == null) ? mApp.getAppName() : getCurFragment().getTitle()); } /** * Sets whether the indeterminate progress bar is visible, * displaying a message * @param visible * @param msg : the waiting message */ public void setActionBarProgressVisible(boolean visible, int msg) { setActionBarProgressVisible(visible, getString(msg)); } /** * Sets whether the indeterminate progress bar is visible, * displaying a message * @param visible * @param string : the waiting message */ public void setActionBarProgressVisible(boolean visible, String string) { setActionBarProgressVisible(visible); if (visible) getSupportActionBar().setTitle(string); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mApp.readScreenConfiguration(); } @Override protected void onStart() { super.onStart(); isStarted = true; if (!mParams.has(Param.NON_ROOT_ACTIVITY)); mApp.onStart(); mTaskQueue.runEvent(ActivityEvent.OnStart); } @Override protected void onResume() { super.onResume(); mCustomRetain = null; isResumed = true; if (mApp.isDebugging()) ViewServer.get(this).setFocusedWindow(this); mTaskQueue.runEvent(ActivityEvent.OnResume); } /** * @return Are we between onCreate and onDestroy? */ public boolean isCreated() { return isCreated; } /** * @return Are we between onStart and onStop? */ public boolean isStarted() { return isStarted; } /** * @return Are we NOT between onResume and onPause? */ public boolean isPaused() { return !isResumed; } /** * @return Has a low memory warning been encountered? * It is possible there is no longer low memory. The flag will stay set forever. */ public boolean isLowMemory() { return isLowMemory; } @Override protected void onPause() { super.onPause(); isResumed = false; // for hierarchy viewer if (mApp.isDebugging()) ViewServer.get(this).setFocusedWindow(this); mTaskQueue.runEvent(ActivityEvent.OnPause); } @Override protected void onStop() { super.onStop(); isStarted = false; if (!mParams.has(Param.NON_ROOT_ACTIVITY)); mApp.onStop(); mTaskQueue.runEvent(ActivityEvent.OnStop); } @Override protected void onDestroy() { super.onDestroy(); isCreated = false; // for hierarchy viewer if (mApp.isDebugging()) ViewServer.get(this).removeWindow(this); mTaskQueue.runEvent(ActivityEvent.OnDestroy); mTaskQueue.empty(); } /** * Determine if a call to onDestroy is due to a changing configuration. * This function is undefined at any other time. This is required because * this similar API function was not added until API 11 * @return true if the activity is about to be restarted */ public boolean isChangingConfig() { return isChangingConfig; } @Override public void onLowMemory() { isLowMemory = true; super.onLowMemory(); } /** * Switch main fragment to a new one as a child of the current fragment, adding the last to the backstack * @param new fragment */ public void toChildFragment(BaseFragment f) { toChildFragment(f, false); } /** * Switch main fragment to a new one as a child of the current fragment * @param new fragment * @param noHistory : true to not add this change to the backstack */ public void toChildFragment(BaseFragment f, boolean noHistory) { FragmentTransaction xact = getSupportFragmentManager().beginTransaction(); // a child fragment, so we need to override parent animation params xact.setCustomAnimations(f.getAnimation(FragmentAnimation.ENTER_FORWARD, f), f.getAnimation(FragmentAnimation.EXIT_FORWARD, f), f.getAnimation(FragmentAnimation.ENTER_BACKWARD, f), f.getAnimation(FragmentAnimation.EXIT_BACKWARD, f)); xact.replace(getFragmentContainerID(), f, f.getClass().getSimpleName()); xact.addToBackStack(f.getClass().getSimpleName()); xact.commit(); } /** * Switch main fragment to a new one as a child of the current fragment's parent * @param f : the new fragment * @param noHistory : true to not add this change to the backstack */ public void toSiblingFragment(BaseFragment f, boolean noHistory) { FragmentTransaction xact = getSupportFragmentManager().beginTransaction(); BaseFragment fCur = getCurFragment(); // a sibling fragment so animate both xact.setCustomAnimations(f.getAnimation(FragmentAnimation.ENTER_FORWARD, fCur), fCur.getAnimation(FragmentAnimation.EXIT_FORWARD, f), fCur.getAnimation(FragmentAnimation.ENTER_BACKWARD, f), f.getAnimation(FragmentAnimation.EXIT_BACKWARD, fCur)); xact.replace(getFragmentContainerID(), f, f.getClass().getSimpleName()); if (!noHistory) xact.addToBackStack(f.getClass().getSimpleName()); xact.commit(); } /** * Places the first main fragment in the app. * @param f : the main fragment */ private void toFirstFragment(BaseFragment f) { FragmentTransaction xact = getSupportFragmentManager().beginTransaction(); xact.add(getFragmentContainerID(), f, f.getClass().getSimpleName()); xact.addToBackStack(f.getClass().getSimpleName()); f.setTransactionID(xact.commit()); } /** * Traverse up the back stack searching for the target fragment * @param target fragment */ public void returnToFragment(BaseFragment f) { if (getCurFragment() == f) // already here! return; int trID = f.getTransactionID(); if (trID != -1) getSupportFragmentManager().popBackStackImmediate(trID, 0); } /** * Traverse up the back stack searching for the target fragment, * but not immediately going back (in case calling this inside a transaction) * @param target fragment */ public void returnToFragmentSafe(BaseFragment f) { if (getCurFragment() == f) // already here! return; int trID = f.getTransactionID(); if (trID != -1) getSupportFragmentManager().popBackStack(trID, 0); } /** * Traverse up the back stack one step */ public void backOneFragment() { getSupportFragmentManager().popBackStackImmediate(); } /** * Traverse up the back stack one step, but not immediately (in case calling this inside a transaction) */ public void backOneFragmentSafe() { getSupportFragmentManager().popBackStack(); } /** * @return the current fragment in the main fragment container */ public BaseFragment getCurFragment() { return (BaseFragment)getSupportFragmentManager() .findFragmentById(getFragmentContainerID()); } @Override public void onBackPressed() { // custom app-level back button stuff if (onBackPressedCustom()) return; BaseFragment cur = getCurFragment(); if (cur == null) { // shouldn't ever happen, just a safeguard finish(); return; } // custom fragment-level back button stuff if (!cur.isPaused()) if (cur.onBackPressed()) return; if (cur == getMainFragment()) finish(); else { try { super.onBackPressed(); } catch (IllegalStateException ex) { // in an app that uses this, we're getting an extremely rare crash when popping the backstack // this may give us more details String curDesc = cur == null ? "null" : cur.toString(); ErrorReporter.getInstance().report("ErrorPoppingStack", ex, "fragment: " + curDesc); throw new RuntimeException(ex); } } } /** * Custom actions to take if the back button is pressed * @return true to capture the back button and override default behavior */ protected abstract boolean onBackPressedCustom(); /** * Calculates the x position of a button on the action bar, assuming the buttons are all icons * We do this because there is no public API in the action bar to get the position of buttons * @param the index of button, counting from the right, 0 is the first * @return the x screen position of the center of the button, in pixels */ public int getActionBarButtonX(int i) { float n = (48 * (i + 0.5f) + 2) * App.DENSITY * ((App.SCREEN_SIZE == Configuration.SCREENLAYOUT_SIZE_XLARGE) ? 1.666666667f : 1); if (android.os.Build.VERSION.SDK_INT >= 14) n = n*1.1566667f; return (int)(-0.5f + App.SCREEN_WIDTH - n); } /** * Calculates the y position of a button on the action bar, * assuming the action bar is at the top of the screen. * We do this because there is no public API in the action bar to get the position of buttons * @return the yx screen position of the bottom of the button, in pixels */ public int getActionBarButtonY() { Rect r = new Rect(); android.view.Window window = getWindow(); window.getDecorView().getWindowVisibleDisplayFrame(r); int statusBarHeight = r.top; return statusBarHeight + getActionBarHeight(); } /** * Called when a dialog has returned * @param id : the id the dialog was created with * @param button : the button, DialogInterface.BUTTON_x values */ protected void onDialogResult(int id, Result button) { switch (id) { case App.DIALOG_USER_RATE: if (button == Result.YES) AppUtils.launchMarketThisApp(mApp); else if (button == Result.MAYBE) AppUtils.remindMeLater(this); return; } if (getCurFragment() != null) { getCurFragment().onDialogResult(id, button); } } /** * Called when a dialog needs a custom view during creation * @param id : the id the dialog was created with */ protected View onDialogCreateCustomView(int id) { if (getCurFragment() != null) { return getCurFragment().onDialogCreateCustomView(id); } return null; } }