package com.elphin.framework.app.fpstack; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.text.TextUtils; import android.util.Log; import com.elphin.framework.exception.BMException; import de.greenrobot.event.EventBus; import java.lang.ref.SoftReference; import java.net.URI; import java.util.*; /** * <p>页面栈管理类,该类不是线程安全的,必须在主线程调用</p> * * @author elphin * @version 1.0 * @date 13-5-26 下午2:17 */ class TaskManagerImpl implements TaskManager { private static final boolean DEBUG = false; private static final String TAG = "TaskManager"; // task and pag type map private Map<String, ArrayList<String>> mPageMap = new HashMap<String, ArrayList<String>>(); // tracker of the actual page private ReorderStack<HistoryRecord> mHistoryList = new ReorderStack<HistoryRecord>(); private HistoryRecord mRootRecord; /** * 子级页面栈实例 */ private List<SoftReference<Task>> mTaskList = new ArrayList<SoftReference<Task>>(); private SoftReference<Task> mCurTask = new SoftReference<Task>(null); private static class TaskManagerHolder { static final TaskManager sINSTANCE = new TaskManagerImpl(); } /** * 获取TaskManager的实例 */ public static TaskManager getInstance() { return TaskManagerHolder.sINSTANCE; } private TaskManagerImpl() { EventBus.getDefault().register(this); } /** * 注册页面 * * @param task 页面所在的栈 * @param page 页面类 * @return */ @Override public boolean registerPage(Class<?> task, Class<?> page) { if (task == null) return false; //对于只注册栈 if (page == null) { return (!mPageMap.containsKey(task.getName())) && null != mPageMap.put(task.getName(), new ArrayList<String>()); } String taskClsName = task.getName(); String pageClsName = page.getName(); if (mPageMap.containsKey(taskClsName)) { ArrayList<String> pageList = mPageMap.get(taskClsName); if (pageList == null) { pageList = new ArrayList<String>(); return pageList.add(pageClsName) && (mPageMap.put(taskClsName, pageList) != null); } else { return !pageList.contains(pageClsName) && pageList.add(pageClsName); } } else { ArrayList<String> pageList = new ArrayList<String>(); return pageList.add(pageClsName) && (mPageMap.put(taskClsName, pageList) == null); } } /** * 指定页面Tag跳转页面,附加页面参数 * </p> * 为跳转的页面添加标签,构造多实例页面 * * @param ctx Context * @param pageCategory 目标页面类型,区分Map还是组件 * @param pageClsName 目标页面的类名(全限定名) * @param pageTagString 目标页面标签 * @param pageArgs 页面参数 */ public void navigateTo(Context ctx, PageCategory pageCategory, String pageClsName, String pageTagString, Bundle pageArgs) { if(DEBUG) Log.d(TAG,"== TaskMgr navigateTo == " + pageClsName); if (ctx == null) { try { throw new BMException("The Context is Null!!"); } catch (BMException e) { e.printStackTrace(); return; } } // try if component platform could take over this nav, if true, use component page to complete this nav ,else use map's page if (ComponentManager.getInstance().tryNavigeteToComponent(pageCategory, pageClsName, pageTagString, pageArgs)) { return; } String taskName = getTargetTaskName(pageClsName); if (taskName == null || TextUtils.isEmpty(taskName)) { return; } try { Class<?> targetCls = Class.forName(taskName); if (mCurTask != null && targetCls != null) { Task curTask = mCurTask.get(); if(curTask!=null && targetCls.equals(curTask.getClass()) && ctx.equals(curTask)){ if(DEBUG) Log.d(TAG,"== curTask navigateTo== " + pageClsName); mCurTask.get().navigateTo(pageClsName, pageTagString, pageArgs); return; } } if (targetCls != null) { Intent localIntent = new Intent(ctx, targetCls); localIntent.setAction(ACTION_NAVIGATE_PAGE); localIntent.putExtra(NAVIGATE_PAGE_TAG,pageTagString); localIntent.putExtra(NAVIGATE_PAGE_NAME, pageClsName); if (pageArgs != null) { localIntent.putExtra(NAVIGATE_PAGE_PARAM, pageArgs); } localIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); if (!(ctx instanceof Activity)) { localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } if(DEBUG) Log.d(TAG,"== change task navigateTo== " + pageClsName); ctx.startActivity(localIntent); } } catch (ClassNotFoundException e) { // 如果没有找到该类,什么也不做 } } /** * 跳转页面 * * @param ctx 所在Context * @param pageClsName 页面类名 * @param pageArgs 参数 */ @Override public void navigateTo(Context ctx, String pageClsName, String pageTagString, Bundle pageArgs) { // Add By Component platform navigateTo(ctx, PageCategory.MAP, pageClsName, pageTagString, pageArgs); } @Override public void navigateTo(Context ctx, String pageClsName) { navigateTo(ctx,pageClsName,null,null); } @Override public void navigateTo(Context ctx, String pageClsName, Bundle pageArgs) { navigateTo(ctx,pageClsName,null,pageArgs); } @Override public void navigateTo(Context ctx, String pageClsName, String pageTagString) { navigateTo(ctx, pageClsName, pageTagString,null); } /** * 跳转到指定Task,显示默认页面 * * @param ctx Context * @param intent Intent 参数 */ @Override public void navigateToTask(Context ctx, Intent intent) { if (ctx == null || intent == null) return; if(DEBUG) Log.d(TAG,"== navigateToTask == [intent]:"+intent); intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); ctx.startActivity(intent); } @Override public void navigateToTask(Context ctx, Intent intent,int flags) { if (ctx == null || intent == null) return; if(DEBUG) Log.d(TAG,"== navigateToTask == [intent]:"+intent); if(flags != 0) intent.addFlags(flags); ctx.startActivity(intent); } /** * 打开页面 * //TODO 是否需要支持URI跳页面 * * @param uri 页面的uri bdui://baidumap/goto?task#page */ @Override public void navigateTo(Context ctx, URI uri, Bundle pageArgs) { } /** * @hide */ @Override public void onGoBack() { onGoBack(null); } /** * 页面回退 * 切换Task * @param backArgs * @hide */ @Override public void onGoBack(Bundle backArgs) { // removeTaskFromList(mCurTask.get()); HistoryRecord target = getLatestRecord(); if(target!=null) { String pageName = target.pageName; //if(pageName == null) { try { Intent backIntent = new Intent(getContext(),Class.forName(target.taskName)); backIntent.putExtra(ACTION_NAVIGATE_BACK, true); backIntent.putExtra(NAVIGATE_PAGE_PARAM,backArgs); navigateToTask(getContext(),backIntent); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } // } else { // // TODO task naviback to page //// navigateTo(getContext(),pageName); // } } // mCurTask.get().finish(); mCurTask.clear(); if(DEBUG) Log.d(TAG,"GOBACK "+Arrays.toString(mTaskList.toArray())); } /** * 记录页面历史 * * @param record 历史记录 */ @Override public void track(HistoryRecord record) { if(record == null) return; mHistoryList.push(record); if(DEBUG) Log.d(TAG,"track record:"+record+"\n"+dump()); //如果查看根页面,需要保留根页面的记录 if(record.equals(mRootRecord) && mHistoryList.indexOf(record)!=0) { mHistoryList.insertElementAt(record,0); } } /** * 以record作为标记重置页面栈状态 * <p>切断历史记录,将record之前的记录清掉。</p> * * @return 成功返回 true,否则返回 false */ @Override public boolean resetStackStatus(HistoryRecord record) { int lastIndex = getValidRecordLastIndex(record); if(lastIndex == -1) return false; Stack<HistoryRecord> del = new Stack<HistoryRecord>(); for (int i = 0; i < lastIndex; ++i) { //排除根节点的主页 if (mRootRecord != null && (mHistoryList.elementAt(i).equals(mRootRecord) && i==0)) continue; del.add(mHistoryList.elementAt(i)); } for(HistoryRecord r : del){ removeStackRecord(r); } return true; } private int getValidRecordIndex(HistoryRecord record) { int retVal = -1; for(int i=0,cnt=mHistoryList.size(); i<cnt; ++i) { if(mHistoryList.elementAt(i).equalsIgnoreSig(record)) return i; } return retVal; } private int getValidRecordLastIndex(HistoryRecord record) { int retVal = -1; for(int cnt = mHistoryList.size()-1,i=cnt; i>=0; i--) { if(mHistoryList.elementAt(i).equalsIgnoreSig(record)) return i; } return retVal; } @Override public void clear() { if(mHistoryList.isEmpty()) return; List<HistoryRecord> delRecords = new ArrayList<HistoryRecord>(); delRecords.addAll(mHistoryList); for(HistoryRecord delRecord : delRecords) { removeStackRecord(delRecord); } } @Override public boolean removeStackRecord(HistoryRecord record) { if(DEBUG) Log.d(TAG,"removeStackRecord\n record:"+record); int index = getValidRecordLastIndex(record); if(index == -1) return false; mHistoryList.remove(index); Task delItem = null; for (SoftReference<Task> taskRef : mTaskList) { Task task = taskRef.get(); if (task != null && task.getClass().getName().equals(record.taskName)){ if (task.getPageStack() == null || task.getPageStack().isEmpty()) { if(index == 0 && record.equals(mRootRecord)) { }else { delItem = task; task.finish(); } break; } else { Stack<Page> pages = task.getPageStack(); Page topPage = pages.peek(); //需移除顶层页面 if(topPage.getClass().getName().equals(record.pageName)) { pages.remove(topPage); FragmentManager fm = ((BaseTask)task).getSupportFragmentManager(); FragmentTransaction ft = fm.beginTransaction(); ft.remove((BasePage)topPage); ft.commitAllowingStateLoss(); PageFactoryImpl.getInstance().removePage((BasePage)topPage); topPage = null; continue; } Page delPage = null; for (Page page : pages) { if (page.getClass().getName().equals(record.pageName)) { delPage = page; break; } } if (delPage != null) { pages.remove(delPage); PageFactoryImpl.getInstance().removePage((BasePage)delPage); delPage = null; } continue; } }else continue; } if (delItem != null) { removeTaskFromList(delItem); delItem = null; } if(DEBUG) Log.d(TAG,"cur records:"+dump()); return true; } @Override public HistoryRecord getRootRecord() { return mRootRecord; } @Override public final HistoryRecord getLatestRecord() { if (mHistoryList.isEmpty()) return null; return mHistoryList.get(mHistoryList.size() - 1); } @Override public final ReorderStack<HistoryRecord> getHistoryRecords() { return mHistoryList; } @Override public boolean pop() { return !mHistoryList.isEmpty() && null != mHistoryList.remove(mHistoryList.size() - 1); } /** * 输出栈历史记录,调试用 * * @return 历史记录的格式化字符串 */ @Override public String dump() { StringBuilder sb = new StringBuilder(); int index = 0; final ReorderStack<HistoryRecord> records = mHistoryList; if (records == null) { return ""; } for (HistoryRecord r : records) { if(r == null) continue; sb.append("#").append(index).append(":").append(r.toString()).append("\n"); index++; } return sb.toString(); } @Override public int getStackStrategy() { return TaskManager.STACK_STRATEGY_REPLACE; } @Override public void setRootRecord(HistoryRecord record) { mRootRecord = record; } @Override public void resetRootRecord(HistoryRecord record) { if(DEBUG) Log.d(TAG,"resetRootRecord:"+record); mRootRecord = record; List<HistoryRecord> delRecords = new ArrayList<HistoryRecord>(); delRecords.addAll(mHistoryList); for(HistoryRecord r : delRecords) { removeStackRecord(r); } track(record); } @Override public void resetToRootRecord() { if(DEBUG) Log.d(TAG,"resetToRootRecord"); List<HistoryRecord> delRecords = new ArrayList<HistoryRecord>(); delRecords.addAll(mHistoryList); for(HistoryRecord r : delRecords) { removeStackRecord(r); } track(mRootRecord); } @Override public void clearTop(HistoryRecord record) { if(DEBUG) Log.d(TAG,"clearTop:"+record); //a,b,c,d,e | ->c | a,b | -> a,b,c // if(!mHistoryList.contains(record)) // return; int lastIndex = getValidRecordIndex(record); if(lastIndex == -1) return; // int index = mHistoryList.indexOf(record); if(lastIndex >= mHistoryList.size()-1) return; List<HistoryRecord> delRecords = new ArrayList<HistoryRecord>(); delRecords.addAll(mHistoryList.subList(lastIndex,mHistoryList.size())); for(HistoryRecord delRecord : delRecords) { removeStackRecord(delRecord); } } @Override public void destroy() { if(DEBUG) Log.d(TAG,"destroy"); clear(); mTaskList.clear(); mCurTask.clear(); EventBus.getDefault().unregister(this); PageFactoryImpl.getInstance().clearCache(); } @Override public Context getContext() { return (Context)mCurTask.get(); } @Override public Parcelable saveState() { if(mHistoryList == null || mHistoryList.size() <= 0) return null; TaskState saveState = new TaskState(); saveState.mRecords = new HistoryRecord[mHistoryList.size()]; saveState.mRootRecord = mRootRecord; for(int i=0,len = mHistoryList.size();i<len;++i) { saveState.mRecords[i] = mHistoryList.elementAt(i); } return saveState; } @Override public void restoreState(Parcelable state) { if(state == null) return; TaskState taskState = (TaskState)state; if(taskState.mRecords == null) return; mHistoryList.clear(); for(HistoryRecord r : taskState.mRecords) { mHistoryList.add(r); } mRootRecord = taskState.mRootRecord; } /** * 根据页面类型获取所在的栈 * <p/> * 每个页面只会出现在一个栈中 * * @param pageClsName 页面类名 * @return 目标Task的名字 */ private String getTargetTaskName(String pageClsName) { Set<Map.Entry<String, ArrayList<String>>> entries = mPageMap.entrySet(); if (entries == null) return null; for (Map.Entry entry : entries) { Object value = entry.getValue(); if (value instanceof ArrayList) { ArrayList<String> pages = (ArrayList<String>) entry.getValue(); if (pages != null) { for (String page : pages) { if (page.equals(pageClsName)) { return (String) entry.getKey(); } } } } } return null; } private void onEventMainThread(TaskChangeEvent event) { if(event.type == TaskChangeEvent.CHANGE_CUR_TASK) { if(DEBUG) Log.d(TAG,"onTaskChangeEvent CHANGE_CUR_TASK"); mCurTask = new SoftReference<Task>(event.curTask); addCurTask(event.curTask); }else if(event.type == TaskChangeEvent.REMOVE_TASK) { if(DEBUG) Log.d(TAG,"onTaskChangeEvent REMOVE_TASK"); removeTaskFromList(event.curTask); removeTaskRecord(event.curTask); } if(DEBUG) Log.d(TAG,"onTaskChangeEvent curTaskList: "+Arrays.toString(mTaskList.toArray())); } private void addCurTask(Task task) { if(task == null) return; SoftReference<Task> item = null; for(SoftReference<Task> ref : mTaskList) { if(ref.get()!=null) { if(ref.get().equals(task)) { return; } } } item = new SoftReference<Task>(task); mTaskList.add(item); } private void removeTaskFromList(Task task){ if(task == null) return; SoftReference<Task> item = null; for(SoftReference<Task> ref : mTaskList) { if(ref.get()!=null) { if(ref.get().equals(task)) { item = ref; break; } } } if(item!=null) mTaskList.remove(item); } private void removeTaskRecord(Task task) { HistoryRecord del = null; for(HistoryRecord r: mHistoryList) { if(r.taskName == null) continue; if(r.taskName.equals(task.getClass().getName()) && r.taskSignature!=null &&r.taskSignature.equals(HistoryRecord.genSignature(task))){ del = r; break; } } if(del!=null) mHistoryList.remove(del); } void updateHistoryRecord(String taskClsName,String oldSig,String newSig ) { for(HistoryRecord r : mHistoryList) { if(r!=null && r.taskName.equals(taskClsName) && r.taskSignature.equals(oldSig)) { r.taskSignature = newSig; } } if(mRootRecord!=null && mRootRecord.taskName.equals(taskClsName) && mRootRecord.taskSignature.equals(oldSig)) { mRootRecord.taskSignature = newSig; } } void clearHistoryRecords() { mHistoryList.clear(); } } final class TaskState implements Parcelable { HistoryRecord[] mRecords; HistoryRecord mRootRecord; public TaskState(){ } public TaskState(Parcel in) { mRecords = in.createTypedArray(HistoryRecord.CREATOR); mRootRecord = in.readParcelable(this.getClass().getClassLoader()); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeTypedArray(mRecords,flags); dest.writeParcelable(mRootRecord,flags); } public static final Parcelable.Creator<TaskState> CREATOR = new Parcelable.Creator<TaskState>() { public TaskState createFromParcel(Parcel in) { return new TaskState(in); } public TaskState[] newArray(int size) { return new TaskState[size]; } }; }