/* * Copyright (C) 2015 Hippo Seven * * 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 com.hippo.scene; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Parcelable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import android.view.ViewTreeObserver; import android.view.inputmethod.InputMethodManager; import android.widget.FrameLayout; import com.hippo.util.AssertUtils; import com.hippo.util.ViewUtils; /** * {@link com.hippo.scene.Scene} is a {@code Activity} of {@link android.app.Activity}. *
* When start a new {@code Scene}, previous {@code Scene} can stay at screen for
* a while. Or retain for a while before destroy.
*/
public abstract class Scene {
public static final int LAUNCH_MODE_STANDARD = 0;
public static final int LAUNCH_MODE_SINGLE_TOP = 1;
static final int SCENE_STATE_INIT = 0;
static final int SCENE_STATE_PREPARE = 1;
static final int SCENE_STATE_OPEN = 2;
static final int SCENE_STATE_RUN = 3;
static final int SCENE_STATE_CLOSE = 4;
static final int SCENE_STATE_DESTROY = 5;
static final int SCENE_STATE_DIE = 6;
static final int SCENE_STATE_PAUSE = 7;
static final int SCENE_STATE_REBIRTH = 8;
SceneManager mSceneManager;
private @Nullable Curtain mCurtain;
private @Nullable Announcer mAnnouncer;
private SceneView mSceneView;
private int mBackgroundColor = 0xffeeeeee; // TODO Need a better to set background color
private int mState = SCENE_STATE_INIT;
private int mId;
private boolean mHide;
public void setSceneManager(SceneManager sceneManager) {
mSceneManager = sceneManager;
mId = sceneManager.nextId();
}
int getId() {
return mId;
}
void setCurtain(@Nullable Curtain curtain) {
mCurtain = curtain;
}
void setAnnouncer(@Nullable Announcer announcer) {
mAnnouncer = announcer;
}
@Nullable Curtain getCurtain() {
return mCurtain;
}
public @Nullable Announcer getAnnouncer() {
return mAnnouncer;
}
// If there is no StageActivity for SceneManager, yout will get AssertError
public @NonNull StageActivity getStageActivity() {
StageActivity stageActivity = mSceneManager.getStageActivity();
AssertUtils.assertNotNull("StageActivity is null", stageActivity);
return stageActivity;
}
public int getLaunchMode() {
return LAUNCH_MODE_STANDARD;
}
private void checkSceneView() {
AssertUtils.assertNotNull("Must call it after onCreate and before onDestroy", mSceneView);
}
/**
* Must call it after onCreate and before onDestroy
*/
public @NonNull SceneView getSceneView() {
checkSceneView();
return mSceneView;
}
public int getSceneCount() {
return mSceneManager.getSceneCount();
}
public void startScene(@NonNull Class sceneClass) {
mSceneManager.startScene(sceneClass, null, null);
}
public void startScene(@NonNull Class sceneClass, @Nullable Announcer announcer) {
mSceneManager.startScene(sceneClass, announcer, null);
}
public void startScene(@NonNull Class sceneClass, @Nullable Announcer announcer,
@Nullable Curtain curtain) {
mSceneManager.startScene(sceneClass, announcer, curtain);
}
int getState() {
return mState;
}
void setState(int state) {
mState = state;
}
SceneView createSceneView(Context context) {
return new SceneView(context);
}
public final void finish() {
mSceneManager.finishScene(this);
}
void setHide(boolean hide) {
checkSceneView();
mHide = hide;
if (hide) {
mSceneView.setVisibility(View.GONE);
} else {
mSceneView.setVisibility(View.VISIBLE);
}
}
boolean getHide() {
return mHide;
}
void init() {
onInit();
}
void rebirth() {
if (mCurtain != null) {
mCurtain.onRebirth();
}
onRebirth();
}
void create(boolean rebirth) {
onCreate(rebirth);
if (mSceneView == null) {
// Not call setContentView in onCreate, create scene view by ourself
mSceneView = createSceneView(getStageActivity());
}
mSceneView.setBackgroundColor(mBackgroundColor);
mSceneView.setFocusable(true);
mSceneView.setFocusableInTouchMode(true);
mSceneView.requestFocus();
// Make sure scene view is attach from stage
attachToStage();
// Hide it if it is hidden
if (mHide) {
mSceneView.setVisibility(View.INVISIBLE);
mSceneView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
ViewUtils.removeOnGlobalLayoutListener(mSceneView.getViewTreeObserver(), this);
mSceneView.setVisibility(View.GONE);
}
});
}
}
void bind() {
onBind();
}
void restore() {
onRestore();
}
void destroy(boolean die) {
onDestroy(die);
}
void die(boolean keepView) {
onDie();
// Make sure soft key broad is hidden
InputMethodManager imm = (InputMethodManager) getStageActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getSceneView().getWindowToken(), 0);
if (!keepView) {
detachFromeStage();
}
SceneApplication.getRefWatcher(getStageActivity()).watch(this);
}
void pause() {
getSceneView().setEnableTouch(false);
onPause();
}
void resume() {
getSceneView().setEnableTouch(true);
mSceneView.requestFocus();
onResume();
}
void open() {
onOpen();
}
void close() {
onClose();
}
protected void onInit() {
}
protected void onRebirth() {
}
protected void onCreate(boolean rebirth) {
}
protected void onBind() {
}
protected void onRestore() {
}
protected void onDestroy(boolean die) {
}
protected void onDie() {
}
protected void onPause() {
}
protected void onResume() {
}
protected void onOpen() {
}
protected void onClose() {
}
void setFitPaddingBottom(int b) {
onGetFitPaddingBottom(b);
}
/**
* @return true if the scene is dead or will be
*/
public boolean canNotBeSaved() {
return mState == SCENE_STATE_CLOSE || mState == SCENE_STATE_DESTROY || mState == SCENE_STATE_DIE;
}
/**
* @return true if the scene is dead
*/
public boolean isDead() {
return mState == SCENE_STATE_DIE;
}
public boolean isRunning() {
return mState == SCENE_STATE_RUN;
}
public void setBackgroundColor(int bgColor) {
if (mBackgroundColor != bgColor) {
mBackgroundColor = bgColor;
View sceneView = mSceneView;
if (sceneView != null) {
sceneView.setBackgroundColor(bgColor);
}
}
}
public int getBackgroundColor() {
return mBackgroundColor;
}
protected void setContentView(int resId) {
StageActivity sa = getStageActivity();
mSceneView = createSceneView(sa);
mSceneView.setBackgroundColor(mBackgroundColor);
sa.getLayoutInflater().inflate(resId, mSceneView);
}
protected void setContentView(View view) {
StageActivity sa = getStageActivity();
mSceneView = createSceneView(sa);
mSceneView.addView(view, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
protected void onNewAnnouncer(Announcer announcer) {
}
/**
* It will be called if nav bar is transparent.
* @param b the nav bar's height
*/
protected void onGetFitPaddingBottom(int b) {
}
/**
* Finds a view that was identified by the id attribute from the XML that
* was processed in {@link #onCreate}.
*
* @param resId the Id
* @return The view if found or null otherwise.
*/
public View findViewById(int resId) {
View sceneView = mSceneView;
if (sceneView != null) {
return sceneView.findViewById(resId);
} else {
return null;
}
}
/**
* Is scene view attached to stage
*
* @return if scene view is null, always return false;
*/
public boolean isInStage() {
if (mSceneView == null) {
return false;
} else {
ViewParent parent = mSceneView.getParent();
if (parent == null) {
return false;
} else if (parent == getStageActivity().getStageLayout()) {
return true;
} else {
throw new IllegalStateException("Scene view should only be the child of stage layout");
}
}
}
// Add scene view to stage layout
void attachToStage() {
if (mSceneView != null && !isInStage()) {
doAttachToStage();
}
}
// Remove scene view from stage layout
void detachFromeStage() {
if (mSceneView != null && isInStage()) {
doDetachFromeStage();
}
}
void doAttachToStage() {
getStageActivity().attachSceneToStage(this);
}
void doDetachFromeStage() {
getStageActivity().detachSceneFromStage(this);
}
void onBackPressedInternal() {
if (!endCurtainAnimation() && !mSceneManager.endLegacyScene()) {
onBackPressed();
}
}
/**
* Not all back press will call it
*/
public void onBackPressed() {
finish();
}
/**
* End curtainAnimation if it is running
*
* @return True if curtain animation is running
*/
boolean endCurtainAnimation() {
if (mCurtain != null && mCurtain.isInAnimation()) {
mCurtain.endAnimation();
return true;
}
return false;
}
void openFinished() {
mSceneManager.openScene(this);
}
void closeFinished() {
mSceneManager.closeScene(this);
}
// It is constant
private String getStateKey() {
return "scene:" + mId;
}
void saveInstanceState(Bundle outState) {
SparseArray