package com.datdo.mobilib.base; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; import android.os.Bundle; import android.view.View; import android.view.ViewGroup.LayoutParams; import android.view.ViewTreeObserver; import android.view.WindowManager; import com.datdo.mobilib.event.MblCommonEvents; import com.datdo.mobilib.event.MblEventCenter; import com.datdo.mobilib.event.MblEventListener; import com.datdo.mobilib.event.MblStrongEventListener; import com.datdo.mobilib.util.MblUtils; import com.datdo.mobilib.util.MblViewUtil; /** * <pre> * Plug an object of this class into an activity to make this library works. * </pre> */ public class MblActivityPlugin { // current status private int mOrientation; // wrapper views private MblDecorView mDecorView; // for background/foreground detecting private static long sLastOnPause = 0; private static Runnable sBackgroundStatusCheckTask = new Runnable() { @Override public void run() { MblEventCenter.postEvent(this, MblCommonEvents.GO_TO_BACKGROUND); } }; private static final long DEFAULT_MAX_ALLOWED_TRASITION_BETWEEN_ACTIVITY = 2000; protected long mMaxAllowedTrasitionBetweenActivity = DEFAULT_MAX_ALLOWED_TRASITION_BETWEEN_ACTIVITY; /** * Extends Activity#onCreate(Bundle) * @param activity targeted activity * @param savedInstanceState */ public void onCreate(Activity activity, Bundle savedInstanceState) { Context context = MblUtils.getCurrentContext(); if (context == null || !(context instanceof Activity)) { MblUtils.setCurrentContext(activity); } activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); mOrientation = activity.getResources().getConfiguration().orientation; MblEventCenter.postEvent(this, MblCommonEvents.ACTIVITY_CREATED, activity, savedInstanceState); } /** * Extends Activity#onStart() * @param activity targeted activity */ public void onStart(Activity activity) { MblEventCenter.postEvent(this, MblCommonEvents.ACTIVITY_STARTED, activity); } /** * Extends Activity#onResume() * @param activity targeted activity */ public void onResume(Activity activity) { MblUtils.setCurrentContext(activity); MblUtils.getMainThreadHandler().removeCallbacks(sBackgroundStatusCheckTask); long now = getNow(); if (now - sLastOnPause > mMaxAllowedTrasitionBetweenActivity) { MblEventCenter.postEvent(this, MblCommonEvents.GO_TO_FOREGROUND); } MblEventCenter.postEvent(this, MblCommonEvents.ACTIVITY_RESUMED, activity); } /** * Extends Activity#onPause() */ public void onPause(Activity activity) { MblUtils.hideKeyboard(); sLastOnPause = getNow(); MblUtils.getMainThreadHandler().postDelayed(sBackgroundStatusCheckTask, mMaxAllowedTrasitionBetweenActivity); MblEventCenter.postEvent(this, MblCommonEvents.ACTIVITY_PAUSED, activity); } /** * Extends Activity#onStop() * @param activity targeted activity */ public void onStop(Activity activity) { MblEventCenter.postEvent(this, MblCommonEvents.ACTIVITY_STOPPED, activity); } /** * Extends Activity#onConfigurationChanged(Configuration) * @param activity targeted activity * @param newConfig */ public void onConfigurationChanged(Activity activity, Configuration newConfig) { if (newConfig.orientation != mOrientation) { mOrientation = newConfig.orientation; waitForWindowOrientationReallyChanged(activity, new Runnable() { @Override public void run() { MblEventCenter.postEvent(this, MblCommonEvents.ORIENTATION_CHANGED); } }); } } private void waitForWindowOrientationReallyChanged(final Activity activity, final Runnable callback) { if (MblUtils.isPortraitDisplay() != isPortraitWindow(activity)) { MblUtils.getMainThreadHandler().postDelayed(new Runnable() { @Override public void run() { waitForWindowOrientationReallyChanged(activity, callback); } }, 10); } else { callback.run(); } } public boolean isPortraitWindow(Activity activity) { View root = activity.getWindow().getDecorView(); return root.getWidth() <= root.getHeight(); } private long getNow() { return System.currentTimeMillis(); } /** * Determine whether targeted activity is top activity in current task * @param activity * @return */ public boolean isTopActivity(Activity activity) { return MblUtils.getCurrentContext() == activity; } private View createDecorViewAndAddContent(Activity activity, int layoutResId, LayoutParams params) { View content = MblUtils.getLayoutInflater().inflate(layoutResId, null); return createDecorViewAndAddContent(activity, content, params); } private View createDecorViewAndAddContent(Activity activity, View layout, LayoutParams params) { if (params == null) { params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); } layout.setLayoutParams(params); MblDecorView decorView = new MblDecorView(activity); decorView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); decorView.setBackgroundColor(0x0); decorView.addView(layout); mDecorView = decorView; setViewProcessor(activity); return decorView; } private void setViewProcessor(final Activity activity) { if (MblViewUtil.getGlobalViewProcessor() == null) { return; } final boolean[] scrollDirty = new boolean[] { false }; final Runnable stopper = MblUtils.repeatDelayed(new Runnable() { @Override public void run() { if (scrollDirty[0]) { MblViewUtil.iterateView(mDecorView, MblViewUtil.getGlobalViewProcessor()); scrollDirty[0] = false; } } }, 100); MblEventCenter.addListener(new MblStrongEventListener() { @Override public void onEvent(Object sender, String name, Object... args) { if (args[0] == activity) { stopper.run(); terminate(); } } }, MblCommonEvents.ACTIVITY_DESTROYED); mDecorView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() { @Override public void onScrollChanged() { scrollDirty[0] = true; } }); mDecorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { MblViewUtil.iterateView(mDecorView, MblViewUtil.getGlobalViewProcessor()); } }); // run view processing right after inflating MblViewUtil.iterateView(mDecorView, MblViewUtil.getGlobalViewProcessor()); } /** * Create wrapping content view for {@link Activity#setContentView(int)} * @param activity targeted activity * @param layoutResID * @return wrapping content view */ public View getContentView(Activity activity, int layoutResID) { return createDecorViewAndAddContent(activity, layoutResID, null); } /** * Create wrapping content view for {@link Activity#setContentView(View)} * @param activity targeted activity * @param view * @return wrapping content view */ public View getContentView(Activity activity, View view) { return createDecorViewAndAddContent(activity, view, null); } /** * Create wrapping content view for {@link Activity#setContentView(View, LayoutParams)} * @param activity targeted activity * @param view * @param params * @return wrapping content view */ public View getContentView(Activity activity, View view, LayoutParams params) { return createDecorViewAndAddContent(activity, view, params); } /** * Extends Activity#onDestroy() * @param activity targeted activity */ public void onDestroy(Activity activity) { if (activity instanceof MblEventListener) { MblEventCenter.removeListenerFromAllEvents((MblEventListener) activity); } MblUtils.cleanupView(mDecorView); MblEventCenter.postEvent(this, MblCommonEvents.ACTIVITY_DESTROYED, activity); } public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { MblEventCenter.postEvent(this, MblCommonEvents.ACTIVITY_RESULT, activity, requestCode, resultCode, data); } /** * Get root view of activity * @param activity targeted activity * @return root view */ public MblDecorView getDecorView(Activity activity) { return mDecorView; } /** * <pre> * Change max-allowed-transition-between-activities (abbreviation MATBA). Default value is 2 seconds. * This will effect {@link MblCommonEvents#GO_TO_BACKGROUND} and {@link MblCommonEvents#GO_TO_FOREGROUND} * * The following is how this library detect changes between background (BG) and foreground (FG): * 1) Transition between activity A and activity B: * A#onPaused() at t1 ------> B#onResumed() at t2 * In this case, t2-t1 < MTBA. FG status is not changed * 2) Go to BG * A#onPaused() at t1 ------> Home screen ------> t2 * At t2 where t2-t1 > MTBA, FG status is changed to BG. {@link MblCommonEvents#GO_TO_BACKGROUND} event is fired * 3) Go back to FG * A#onPaused() at t1 ------> Home screen ------> t2 (BG) ------> A#onResumed() at t3 * At t3 where t3-t1 > MTBA, BG status is changed to FG {@link MblCommonEvents#GO_TO_FOREGROUND} event is fired * </pre> * @param activity targeted activity * @param duration */ public void setMaxAllowedTrasitionBetweenActivity(Activity activity, long duration) { mMaxAllowedTrasitionBetweenActivity = duration; } /** * @see #setMaxAllowedTrasitionBetweenActivity(Activity, long) */ public void resetDefaultMaxAllowedTrasitionBetweenActivity(Activity activity) { mMaxAllowedTrasitionBetweenActivity = DEFAULT_MAX_ALLOWED_TRASITION_BETWEEN_ACTIVITY; } }