package com.app.hubert.guide.core;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.SharedPreferences;
import android.os.Build;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import com.app.hubert.guide.NewbieGuide;
import com.app.hubert.guide.lifecycle.FragmentLifecycleAdapter;
import com.app.hubert.guide.lifecycle.ListenerFragment;
import com.app.hubert.guide.lifecycle.V4ListenerFragment;
import com.app.hubert.guide.listener.OnGuideChangedListener;
import com.app.hubert.guide.listener.OnLayoutInflatedListener;
import com.app.hubert.guide.listener.OnPageChangedListener;
import com.app.hubert.guide.model.GuidePage;
import com.app.hubert.guide.model.RelativeGuide;
import com.app.hubert.guide.util.LogUtil;
import com.app.hubert.guide.util.ScreenUtils;
import java.lang.reflect.Field;
import java.security.InvalidParameterException;
import java.util.List;
/**
* Created by hubert
*
* Created on 2017/7/27.
*
* guide的控制器,可以通过该类控制引导层的显示与回退,或者重置label
*/
public class Controller {
private static final String LISTENER_FRAGMENT = "listener_fragment";
private Activity activity;
private Fragment fragment;
private android.support.v4.app.Fragment v4Fragment;
private OnGuideChangedListener onGuideChangedListener;
private OnPageChangedListener onPageChangedListener;
private String label;
private boolean alwaysShow;
private int showCounts;//显示次数
private List guidePages;
private int current;//当前页
private GuideLayout currentLayout;
private FrameLayout mParentView;
private SharedPreferences sp;
private int indexOfChild = -1;//使用anchor时记录的在父布局的位置
private boolean isShowing;
public Controller(Builder builder) {
this.activity = builder.activity;
this.fragment = builder.fragment;
this.v4Fragment = builder.v4Fragment;
this.onGuideChangedListener = builder.onGuideChangedListener;
this.onPageChangedListener = builder.onPageChangedListener;
this.label = builder.label;
this.alwaysShow = builder.alwaysShow;
this.guidePages = builder.guidePages;
showCounts = builder.showCounts;
View anchor = builder.anchor;
if (anchor == null) {
anchor = activity.findViewById(android.R.id.content);
}
if (anchor instanceof FrameLayout) {
mParentView = (FrameLayout) anchor;
} else {
FrameLayout frameLayout = new FrameLayout(activity);
ViewGroup parent = (ViewGroup) anchor.getParent();
indexOfChild = parent.indexOfChild(anchor);
parent.removeView(anchor);
if (indexOfChild >= 0) {
parent.addView(frameLayout, indexOfChild, anchor.getLayoutParams());
} else {
parent.addView(frameLayout, anchor.getLayoutParams());
}
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams
(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
frameLayout.addView(anchor, lp);
mParentView = frameLayout;
}
sp = activity.getSharedPreferences(NewbieGuide.TAG, Activity.MODE_PRIVATE);
}
/**
* 显示指引layout
*/
public void show() {
final int showed = sp.getInt(label, 0);
if (!alwaysShow) {
if (showed >= showCounts) {
return;
}
}
if (isShowing) {
return;
}
isShowing = true;
mParentView.post(new Runnable() {
@Override
public void run() {
if (guidePages == null || guidePages.size() == 0) {
throw new IllegalStateException("there is no guide to show!! Please add at least one Page.");
}
current = 0;
showGuidePage();
if (onGuideChangedListener != null) {
onGuideChangedListener.onShowed(Controller.this);
}
addListenerFragment();
sp.edit().putInt(label, showed + 1).apply();
}
});
}
/**
* 显示相应position的引导页
*
* @param position from 0 to (pageSize - 1)
*/
public void showPage(int position) {
if (position < 0 || position > guidePages.size() - 1) {
throw new InvalidParameterException("The Guide page position is out of range. current:"
+ position + ", range: [ 0, " + guidePages.size() + " )");
}
if (current == position) {
return;
}
current = position;
//fix #59 GuideLayout.setOnGuideLayoutDismissListener() on a null object reference
if (currentLayout != null) {
currentLayout.setOnGuideLayoutDismissListener(new GuideLayout.OnGuideLayoutDismissListener() {
@Override
public void onGuideLayoutDismiss(GuideLayout guideLayout) {
showGuidePage();
}
});
currentLayout.remove();
} else {
showGuidePage();
}
}
/**
* 显示当前引导页的前一页
*/
public void showPreviewPage() {
showPage(--current);
}
/**
* 显示current引导页
*/
private void showGuidePage() {
GuidePage page = guidePages.get(current);
GuideLayout guideLayout = new GuideLayout(activity, page, this);
guideLayout.setOnGuideLayoutDismissListener(new GuideLayout.OnGuideLayoutDismissListener() {
@Override
public void onGuideLayoutDismiss(GuideLayout guideLayout) {
showNextOrRemove();
}
});
mParentView.addView(guideLayout, new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
currentLayout = guideLayout;
if (onPageChangedListener != null) {
onPageChangedListener.onPageChanged(current);
}
isShowing = true;
}
private void showNextOrRemove() {
if (current < guidePages.size() - 1) {
current++;
showGuidePage();
} else {
if (onGuideChangedListener != null) {
onGuideChangedListener.onRemoved(Controller.this);
}
removeListenerFragment();
isShowing = false;
}
}
/**
* 清楚当前Controller的label记录
*/
public void resetLabel() {
resetLabel(label);
}
/**
* 清除"显示过"的标记
*
* @param label 引导标示
*/
public void resetLabel(String label) {
sp.edit().putInt(label, 0).apply();
}
/**
* 中断引导层的显示,后续未显示的page将不再显示
*/
public void remove() {
if (currentLayout != null && currentLayout.getParent() != null) {
ViewGroup parent = (ViewGroup) currentLayout.getParent();
parent.removeView(currentLayout);
//移除anchor添加的frameLayout
if (!(parent instanceof FrameLayout)) {
ViewGroup original = (ViewGroup) parent.getParent();
View anchor = parent.getChildAt(0);
parent.removeAllViews();
if (anchor != null) {
if (indexOfChild > 0) {
original.addView(anchor, indexOfChild, parent.getLayoutParams());
} else {
original.addView(anchor, parent.getLayoutParams());
}
}
}
if (onGuideChangedListener != null) {
onGuideChangedListener.onRemoved(this);
}
currentLayout = null;
}
isShowing = false;
}
public boolean isShowing() {
return isShowing;
}
private void addListenerFragment() {
//fragment监听销毁界面关闭引导层
if (fragment != null && Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
compatibleFragment(fragment);
FragmentManager fm = fragment.getChildFragmentManager();
ListenerFragment listenerFragment = (ListenerFragment) fm.findFragmentByTag(LISTENER_FRAGMENT);
if (listenerFragment == null) {
listenerFragment = new ListenerFragment();
fm.beginTransaction().add(listenerFragment, LISTENER_FRAGMENT).commitAllowingStateLoss();
}
listenerFragment.setFragmentLifecycle(new FragmentLifecycleAdapter() {
@Override
public void onDestroyView() {
LogUtil.i("ListenerFragment.onDestroyView");
remove();
}
});
}
if (v4Fragment != null) {
android.support.v4.app.FragmentManager v4Fm = v4Fragment.getChildFragmentManager();
V4ListenerFragment v4ListenerFragment = (V4ListenerFragment) v4Fm.findFragmentByTag(LISTENER_FRAGMENT);
if (v4ListenerFragment == null) {
v4ListenerFragment = new V4ListenerFragment();
v4Fm.beginTransaction().add(v4ListenerFragment, LISTENER_FRAGMENT).commitAllowingStateLoss();
}
v4ListenerFragment.setFragmentLifecycle(new FragmentLifecycleAdapter() {
@Override
public void onDestroyView() {
LogUtil.i("v4ListenerFragment.onDestroyView");
remove();
}
});
}
}
private void removeListenerFragment() {
//隐藏引导层时移除监听fragment
if (fragment != null && Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
FragmentManager fm = fragment.getChildFragmentManager();
ListenerFragment listenerFragment = (ListenerFragment) fm.findFragmentByTag(LISTENER_FRAGMENT);
if (listenerFragment != null) {
fm.beginTransaction().remove(listenerFragment).commitAllowingStateLoss();
}
}
if (v4Fragment != null) {
android.support.v4.app.FragmentManager v4Fm = v4Fragment.getChildFragmentManager();
V4ListenerFragment v4ListenerFragment = (V4ListenerFragment) v4Fm.findFragmentByTag(LISTENER_FRAGMENT);
if (v4ListenerFragment != null) {
v4Fm.beginTransaction().remove(v4ListenerFragment).commitAllowingStateLoss();
}
}
}
/**
* For bug of Fragment in Android
* https://issuetracker.google.com/issues/36963722
*
* @param fragment
*/
private void compatibleFragment(Fragment fragment) {
try {
Field childFragmentManager = Fragment.class.getDeclaredField("mChildFragmentManager");
childFragmentManager.setAccessible(true);
childFragmentManager.set(fragment, null);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}