5a93814e创建于 2025年2月24日历史提交
/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * 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.
 */

import { hilog } from '@kit.PerformanceAnalysisKit';
import deviceInfo from '@ohos.deviceInfo';
import { display, mediaquery } from '@kit.ArkUI';
import base from '@ohos.base';

const TAG = 'DeviceHelper';

/**
 * device info util
 *
 */
export class DeviceHelper {
  static readonly TYPE_DEFAULT = 'default';
  static readonly TYPE_PHONE = 'phone';
  static readonly TYPE_TABLET = 'tablet';
  static readonly DEVICE_TYPE = deviceInfo.deviceType;

  /**
   * whether the device type is phone
   *
   * @returns true if is phone
   */
  static isPhone(): boolean {
    return (DeviceHelper.DEVICE_TYPE === DeviceHelper.TYPE_PHONE ||
      DeviceHelper.DEVICE_TYPE === DeviceHelper.TYPE_DEFAULT);
  }

  /**
   * whether the device type is tablet
   *
   * @returns true if is tablet
   */
  public static isTablet(): boolean {
    return DeviceHelper.DEVICE_TYPE === DeviceHelper.TYPE_TABLET;
  }

  /**
   * Check if is foldable
   *
   * @returns true if is foldable
   */
  static isFold(): boolean {
    let isFold: boolean = false;
    try {
      isFold = display.isFoldable();
    } catch (e) {
      hilog.error(0x0000, TAG, 'isFold -> isFoldable try error:', e);
    }
    return isFold;
  }

  /**
   * Check if is expanded
   *
   * @returns true if is expanded
   */
  static isExpanded(): boolean {
    let isExpanded: boolean = false;
    try {
      isExpanded = display.getFoldStatus() === display.FoldStatus.FOLD_STATUS_EXPANDED;
    } catch (e) {
      hilog.error(0x0000, TAG, 'isExpanded -> try error:', e);
    }
    return isExpanded;
  }

  /**
   * Check if is column
   *
   * @returns true if is column
   */
  static isColumn(): boolean {
    let isColumn: boolean = false;
    try {
      isColumn = display.isFoldable() && (display.getFoldStatus() === display.FoldStatus.FOLD_STATUS_EXPANDED ||
        display.getFoldStatus() === display.FoldStatus.FOLD_STATUS_HALF_FOLDED);
    } catch (e) {
      hilog.error(0x0000, TAG, 'isColumn -> try error:', e);
    }
    return isColumn;
  }

  /**
   * Check if is straight product
   *
   * @returns true if is straight product
   */
  public static isStraightProduct(): boolean {
    return DeviceHelper.isPhone() && !DeviceHelper.isFold();
  }
}

export class DeviceListenerManager {
  private static instance: DeviceListenerManager | undefined;
  private portraitListener = mediaquery.matchMediaSync('(orientation: portrait)');
  private drawableWidthLargeListener = mediaquery.matchMediaSync('(width >= 600vp)');
  private isPortrait: boolean | undefined = undefined;
  private onOrientationChange: Function | undefined = undefined;
  private isLarge: boolean | undefined = undefined;
  private onDrawableWidthChange: Function | undefined = undefined;

  public static getInstance(): DeviceListenerManager {
    if (DeviceListenerManager.instance === undefined) {
      DeviceListenerManager.instance = new DeviceListenerManager();
    }
    return DeviceListenerManager.instance;
  }

  private onPortraitChange(result: mediaquery.MediaQueryResult) {
    let isChanged: boolean = false;
    if (DeviceListenerManager.getInstance().isPortrait === undefined) {
      DeviceListenerManager.getInstance().isPortrait = result.matches;
      isChanged = true;
    } else {
      if (result.matches) {
        if (!DeviceListenerManager.getInstance().isPortrait) {
          DeviceListenerManager.getInstance().isPortrait = true;
          isChanged = true;
          hilog.debug(0x0000, 'MultiNavigation', 'display portrait');
        }
      } else {
        if (DeviceListenerManager.getInstance().isPortrait) {
          DeviceListenerManager.getInstance().isPortrait = false;
          isChanged = true;
          hilog.debug(0x0000, 'MultiNavigation', 'display landscape');
        }
      }
    }
    if (isChanged) {
      DeviceListenerManager.getInstance().notifyOrientationChange();
    }
  }

  private notifyOrientationChange() {
    this.onOrientationChange && this.onOrientationChange(this.isPortrait);
  }

  private onDrawableWidthLargeChange(result: mediaquery.MediaQueryResult) {
    let isChanged: boolean = false;
    if (DeviceListenerManager.getInstance().isLarge === undefined) {
      DeviceListenerManager.getInstance().isLarge = result.matches;
      isChanged = true;
    } else {
      if (result.matches) {
        if (!DeviceListenerManager.getInstance().isLarge) {
          DeviceListenerManager.getInstance().isLarge = true;
          isChanged = true;
          hilog.debug(0x0000, 'MultiNavigation', 'display isLarge');
        }
      } else {
        if (DeviceListenerManager.getInstance().isLarge) {
          DeviceListenerManager.getInstance().isLarge = false;
          isChanged = true;
          hilog.debug(0x0000, 'MultiNavigation', 'display not large');
        }
      }
    }

    if (isChanged) {
      DeviceListenerManager.getInstance().notifyWidthChange();
    }
  }

  private notifyWidthChange() {
    this.onDrawableWidthChange && this.onDrawableWidthChange(this.isLarge);
  }

  public registerOrientationLister(func: Function): void {
    this.onOrientationChange = func;
    this.onOrientationChange && this.isPortrait && this.onOrientationChange(this.isPortrait);
  }

  public unregisterOrientationLister(): void {
    this.onOrientationChange = undefined;
  }

  public registerDrawableWidthLister(func: Function): void {
    this.onDrawableWidthChange = func;
    this.onDrawableWidthChange && this.isLarge && this.onDrawableWidthChange(this.isLarge);
  }

  public unregisterDrawableWidthLister(): void {
    this.onDrawableWidthChange = undefined;
  }

  public initListener(): void {
    this.portraitListener.on('change', this.onPortraitChange);
    this.drawableWidthLargeListener.on('change', this.onDrawableWidthLargeChange);
  }

  public finalizeListener() {
    this.portraitListener.off('change', this.onPortraitChange);
    this.drawableWidthLargeListener.off('change', this.onDrawableWidthLargeChange);
  }
}

@Observed
export class NavWidthRangeAttrModifier implements AttributeModifier<NavigationAttribute> {
  isApplicationSet: boolean = false;
  minHomeWidth: Percentage = '50%';
  maxHomeWidth: Percentage = '50%';

  applyNormalAttribute(instance: NavigationAttribute): void {
    if (this.isApplicationSet) {
      instance.navBarWidthRange([this.minHomeWidth, this.maxHomeWidth]);
    }
  }
}

@Component
export struct SubNavigation {
  @Link isPortrait: boolean;
  @State displayMode: number = 0;
  @ObjectLink multiStack: MultiNavPathStack
  @BuilderParam navDestination: NavDestinationBuildFunction;
  primaryStack: MyNavPathStack = new MyNavPathStack();
  @State secondaryStack: MyNavPathStack = new MyNavPathStack();
  @State primaryWidth: number | string = '50%';
  @ObjectLink needRenderIsFullScreen: NeedRenderIsFullScreen;
  @ObjectLink needRenderLeftClickCount: NeedRenderLeftClickCount;
  @ObjectLink navWidthRangeModifier: NavWidthRangeAttrModifier;
  @ObjectLink needRenderDisplayMode: NeedRenderDisplayMode;
  onNavigationModeChange?: OnNavigationModeChangeCallback = (mode: NavigationMode) => {};

  @Builder
  SubNavDestination(name: string, param?: object) {
    this.navDestination(name, param);
  }

  getMode(): NavigationMode {
    this.displayMode = this.needRenderDisplayMode.displayMode;
    if (DeviceHelper.isPhone() && DeviceHelper.isStraightProduct()) {
      return NavigationMode.Stack;
    }
    if (this.displayMode === display.FoldStatus.FOLD_STATUS_UNKNOWN) {
      try {
        this.displayMode = display.getFoldStatus();
      } catch (err) {
        hilog.warn(0x0000, 'MultiNavigation', 'Failed to get fold status. error:' + JSON.stringify(err));
      }
    }
    if (DeviceHelper.isTablet() && this.isPortrait) {
      hilog.info(0x0000, 'MultiNavigation', 'SubNavigation getMode tablet portrait');
      return NavigationMode.Stack;
    }
    if (this.needRenderIsFullScreen.isFullScreen == undefined) {
      if (DeviceHelper.isPhone()) {
        return this.secondaryStack.size() > 0 && DeviceHelper.isColumn() ? NavigationMode.Auto : NavigationMode.Stack;
      }
      return this.secondaryStack.size() > 0 ? NavigationMode.Auto : NavigationMode.Stack;
    }
    return this.needRenderIsFullScreen.isFullScreen ? NavigationMode.Stack : NavigationMode.Auto;
  }

  aboutToAppear(): void {
    hilog.debug(0x0000, 'MultiNavigation', 'SubNavigation aboutToAppear param = ' + JSON.stringify(this.primaryStack));
  }

  build() {
    NavDestination() {
      Navigation(this.secondaryStack) {
        Navigation(this.primaryStack) {
        }
        .hideNavBar(true)
        .mode(NavigationMode.Stack)
        .navDestination(this.SubNavDestination)
        .hideTitleBar(true)
        .hideToolBar(true)
        .hideBackButton(true)
        .onTouch((event) => {
          if (event.type === TouchType.Down) {
            this.needRenderLeftClickCount.leftClickCount = 2;
          }
        })
      }
      .mode(this.getMode())
      .onNavigationModeChange(this?.onNavigationModeChange)
      .hideBackButton(true)
      .hideTitleBar(true)
      .navDestination(this.SubNavDestination)
      .navBarWidth(this.primaryWidth)
      .attributeModifier(this.navWidthRangeModifier)
      .onTouch((event) => {
        if (event.type === TouchType.Down) {
          hilog.info(0x0000, 'MultiNavigation', 'outer navigation this.outerStack.leftClickCount ' +
          this.needRenderLeftClickCount.leftClickCount);
          this.needRenderLeftClickCount.leftClickCount--;
        }
      })
    }
    .onBackPressed(() => {
      hilog.debug(0x0000, 'MultiNavigation', 'subNavigation NavDestination onBackPressed');
      if (this.multiStack && this.secondaryStack.size() === 1) {
        hilog.info(0x0000, 'MultiNavigation', 'subNavigation NavDestination onBackPressed multiStack.pop');
        this.multiStack.pop();
        return true;
      }
      return false;
    })
    .hideTitleBar(true)
  }
}

export enum SplitPolicy {
  HOME_PAGE = 0,
  DETAIL_PAGE = 1,
  FULL_PAGE = 2,
  // PlACE_HOLDER_PAGE is not declared in SDK
  PlACE_HOLDER_PAGE = 3,
}

let that: MultiNavigation;

@Component
export struct MultiNavigation {
  private foldStatusCallback: Callback<display.FoldStatus> = (data: display.FoldStatus) => {
    hilog.info(0x0000, 'MultiNavigation', 'foldStatusCallback data.valueOf()=' + data.valueOf());
    this.multiStack.needRenderDisplayMode.displayMode = data.valueOf();
    this.multiStack.handleRefreshPlaceHolderIfNeeded();
  };
  @State multiStack: MultiNavPathStack = new MultiNavPathStack();
  @BuilderParam navDestination: (name: string, param?: object) => void;
  mode: NavigationMode | undefined = undefined;
  onNavigationModeChangeCallback?: (mode: NavigationMode) => void = (mode: NavigationMode) => {
  };
  onHomeShowOnTop?: OnHomeShowOnTopCallback = (name: string) => {};
  @State isPortrait: boolean = false;

  @Builder
  MultiNavDestination(name: string, param?: object) {
    if (name === 'SubNavigation') {
      SubNavigation({
        isPortrait: this.isPortrait,
        multiStack: this.multiStack,
        navDestination: this.navDestination,
        primaryStack: (param as SubNavigationStack).primaryStack,
        secondaryStack: (param as SubNavigationStack).secondaryStack,
        needRenderIsFullScreen: (param as SubNavigationStack).needRenderIsFullScreen,
        needRenderLeftClickCount: this.multiStack.needRenderLeftClickCount,
        navWidthRangeModifier: this.multiStack.navWidthRangeModifier,
        onNavigationModeChange: this?.callback,
        needRenderDisplayMode: this.multiStack.needRenderDisplayMode,
      });
    } else {
      this.navDestination(name, param);
    }
  }

  callback(mode: NavigationMode): void {
    if (that.onNavigationModeChangeCallback !== undefined) {
      if (mode !== that.mode || that.mode === undefined) {
        that?.onNavigationModeChangeCallback(mode);
      }
      that.mode = mode;
    }
  }

  aboutToAppear(): void {
    that = this;
    hilog.info(0x0000, 'MultiNavigation', 'MultiNavigation aboutToAppear');
    try {
      display.on('foldStatusChange', this.foldStatusCallback);
    } catch (exception) {
      console.error('Failed to register callback. Code: ' + JSON.stringify(exception));
    }
    DeviceListenerManager.getInstance().registerOrientationLister((isPortrait: boolean) => {
      hilog.info(0x0000, 'MultiNavigation', 'MultiNavigation orientation change ' + isPortrait);
      this.isPortrait = isPortrait;
      this.multiStack.isPortrait = isPortrait;
      this.multiStack.handleRefreshPlaceHolderIfNeeded();
    });
    DeviceListenerManager.getInstance().registerDrawableWidthLister((isLarge: boolean) => {
      hilog.debug(0x0000, 'MultiNavigation', 'MultiNavigation Drawable width change ' + isLarge);
      this.multiStack.isLarge = isLarge;
      this.multiStack.handleRefreshPlaceHolderIfNeeded();
    });
    try {
      this.multiStack.needRenderDisplayMode.displayMode = display.getFoldStatus();
    } catch (err) {
      hilog.warn(0x0000, 'MultiNavigation', 'Failed to get fold status. error:' + JSON.stringify(err));
    }
    DeviceListenerManager.getInstance().initListener();
    this.multiStack.registerHomeChangeListener({
      onHomeShowOnTop: (name) => {
        this.onHomeShowOnTop?.(name);
      },
    })
  }

  aboutToDisappear(): void {
    try {
      display.off('foldStatusChange');
    } catch (exception) {
      console.error('Failed to unregister callback. Code: ' + JSON.stringify(exception));
    }
    DeviceListenerManager.getInstance().unregisterOrientationLister();
    DeviceListenerManager.getInstance().unregisterDrawableWidthLister();
    DeviceListenerManager.getInstance().finalizeListener();
    this.multiStack.unregisterHomeChangeListener();
  }

  build() {
    Navigation(this.multiStack.outerStack) {
    }
    .mode(NavigationMode.Stack)
    .navDestination(this.MultiNavDestination)
    .hideBackButton(true)
    .hideTitleBar(true)
    .hideToolBar(true)
    .hideNavBar(true)
  }
}

@Observed
export class MultiNavPathStack extends NavPathStack {
  outerStack: MyNavPathStack = new MyNavPathStack();
  totalStack: MultiNavPolicyInfo[] = [];
  subStackList: Array<SubNavigationStack> = new Array<SubNavigationStack>();
  needRenderLeftClickCount: NeedRenderLeftClickCount = new NeedRenderLeftClickCount();
  needRenderDisplayMode: NeedRenderDisplayMode = new NeedRenderDisplayMode();
  disableAllAnimation: boolean = false;
  private mPolicyMap = new Map<string, SplitPolicy>();
  navWidthRangeModifier: NavWidthRangeAttrModifier = new NavWidthRangeAttrModifier();
  homeWidthPercents: number[] = [50, 50];
  keepBottomPageFlag = false;
  homeChangeListener: HomeChangeListener | undefined = undefined;
  placeHolderPolicyInfo: MultiNavPolicyInfo | undefined = undefined;
  isPortrait: boolean = false;
  isLarge: boolean = false;

  navPathStackOperate:MultiNavPathStackOperate = {
    onPrimaryPop:() => {
      hilog.info(0x0000, 'MultiNavigation', 'MyNavPathStack onPrimaryPop');
      this.totalStack.pop();
      this.subStackList.pop();
      this.outerStack.popInner(false);
    },
    onSecondaryPop:() => {
      hilog.info(0x0000, 'MultiNavigation', 'MyNavPathStack onSecondaryPop');
      this.totalStack.pop();
      this.checkAndNotifyHomeChange();
    }
  };

  outerStackOperate: NavPathStackOperate = {
    onSystemPop:() => {
      hilog.info(0x0000, 'MultiNavigation', 'MyNavPathStack onOuterPop');
      this.totalStack.pop();
      this.subStackList.pop();
      this.checkAndNotifyHomeChange();
    }
  };

  constructor() {
    super();
    this.outerStack.registerStackOperateCallback(this.outerStackOperate);
  }

  pushPath(info: NavPathInfo, animated?: boolean, policy?: SplitPolicy): void;
  pushPath(info: NavPathInfo, options?: NavigationOptions, policy?: SplitPolicy): void;
  pushPath(info: NavPathInfo, optionParam?: boolean | NavigationOptions, policy?: SplitPolicy): void {
    hilog.info(0x0000, 'MultiNavigation', 'pushPath policy = ' + policy + ', info.name = ' + info.name);
    let animated: boolean = true;
    if (optionParam !== undefined) {
      if (typeof optionParam === 'boolean') {
        animated = optionParam;
      } else if (optionParam.animated !== undefined) {
        animated = optionParam.animated;
      } else {

      }
    }
    policy = (policy === undefined) ? SplitPolicy.DETAIL_PAGE : policy;
    const subStackLength = this.subStackList.length;
    const multiPolicyStack = new MultiNavPolicyInfo(policy, info);
    hilog.info(0x0000, 'MultiNavigation', 'pushPath subStackLength = ' + subStackLength);
    if (subStackLength > 0) {
      hilog.info(0x0000, 'MultiNavigation', 'pushPath currentTopPrimaryPolicy = ' +
      this.subStackList[subStackLength - 1].getPrimaryPolicy());
    }
    if (policy === SplitPolicy.DETAIL_PAGE && subStackLength > 0 &&
      this.subStackList[subStackLength - 1].getPrimaryPolicy() === SplitPolicy.HOME_PAGE) {
      let detailSize = this.subStackList[subStackLength - 1].getSecondaryInfoList().length;
      hilog.info(0x0000, 'MultiNavigation', 'pushPath detailSize = ' + detailSize );
      if (detailSize === 0) {
        this.subStackList[subStackLength - 1].pushSecondaryPath(multiPolicyStack, animated);
      } else {
        if (this.needRenderLeftClickCount.leftClickCount > 0) {
          // click on home, so we need to clear detail
          if (this.placeHolderPolicyInfo === undefined) {
            this.subStackList[subStackLength - 1].clearSecondary(false);
            this.totalStack.splice(this.totalStack.length - detailSize);
            this.subStackList[subStackLength - 1].pushSecondaryPath(multiPolicyStack, false);
          } else {
            const firstSecondaryPolicy = this.subStackList[subStackLength - 1].getSecondaryInfoList()[0].policy;
            if (firstSecondaryPolicy === SplitPolicy.PlACE_HOLDER_PAGE) {
              if (detailSize === 1 ) {
                // detail has only place holder, so just push
                this.subStackList[subStackLength - 1].pushSecondaryPath(multiPolicyStack, animated);
              } else {
                this.subStackList[subStackLength - 1].clearSecondaryKeepPlaceHolder(false);
                this.totalStack.splice(this.totalStack.length - detailSize + 1);
                this.subStackList[subStackLength - 1].pushSecondaryPath(multiPolicyStack, false);
              }
            } else {
              this.subStackList[subStackLength - 1].clearSecondary(false);
              this.totalStack.splice(this.totalStack.length - detailSize);
              this.subStackList[subStackLength - 1].pushSecondaryPath(multiPolicyStack, false);
            }
          }
        } else {
          // click on detail, so just push
          this.subStackList[subStackLength - 1].pushSecondaryPath(multiPolicyStack, animated);
        }
      }
    } else {
      let subStack = new SubNavigationStack();
      subStack.registerMultiStackOperateCallback(this.navPathStackOperate);
      subStack.disableAnimation(this.disableAllAnimation);
      subStack.pushPrimaryPath(multiPolicyStack, false);
      this.subStackList.push(subStack);
      this.outerStack.pushPath({ name: 'SubNavigation', param: subStack }, animated);
    }

    this.totalStack.push(multiPolicyStack);
    if (policy === SplitPolicy.HOME_PAGE && this.placeHolderPolicyInfo !== undefined &&
    this.needShowPlaceHolder()) {
      this.pushPlaceHolder(subStackLength);
    }
    hilog.info(0x0000, 'MultiNavigation', 'MultiNavPathStack pushPath policy = ' + policy +
      ' stackSize = ' + this.totalStack.length +
      ' this.leftClickCount = ' + this.needRenderLeftClickCount.leftClickCount);
    this.needRenderLeftClickCount.leftClickCount = 0;
  }

  pushPathByName(name: string, param: Object, animated?: boolean, policy?: SplitPolicy): void;
  pushPathByName(name: string, param: Object, onPop?: base.Callback<PopInfo>, animated?: boolean,
    policy?: SplitPolicy): void;
  pushPathByName(name: string, param: Object, onPop?: base.Callback<PopInfo> | boolean,
    animated?: boolean | SplitPolicy, policy?: SplitPolicy): void {
    if (onPop !== undefined && typeof onPop !== 'boolean') {
      this.pushPath({ name: name, param: param, onPop: onPop as base.Callback<PopInfo> }, animated as boolean, policy);
      return;
    }
    if (typeof onPop === 'boolean') {
      this.pushPath({ name: name, param: param }, onPop as boolean, animated as SplitPolicy);
      return;
    }
    // here onpop is undefined
    if (animated !== undefined && typeof animated !== 'boolean') {
      this.pushPath({ name: name, param: param}, undefined, animated as SplitPolicy);
      return;
    }
    if (typeof animated === 'boolean') {
      this.pushPath({ name: name, param: param}, animated as boolean, policy);
      return;
    }
    this.pushPath({ name: name, param: param}, undefined, policy);
  }

  pushDestination(info: NavPathInfo, animated?: boolean, policy?: SplitPolicy): Promise<void>;
  pushDestination(info: NavPathInfo, options?: NavigationOptions, policy?: SplitPolicy): Promise<void>;
  pushDestination(info: NavPathInfo, animated?: boolean | NavigationOptions, policy?: SplitPolicy): Promise<void> {
    hilog.error(0x0000, 'MultiNavigation', 'pushDestination is not support');
    let promise: Promise<void> = Promise.reject({message: 'not support'});
    return promise;
  }

  pushDestinationByName(name: string, param: Object, animated?: boolean, policy?: SplitPolicy): Promise<void>;
  pushDestinationByName(name: string, param: Object, onPop: base.Callback<PopInfo>, animated?: boolean,
    policy?: SplitPolicy): Promise<void>;
  pushDestinationByName(name: string, param: Object, onPop?: boolean | base.Callback<PopInfo>,
    animated?: boolean | SplitPolicy, policy?: SplitPolicy): Promise<void> {
    hilog.error(0x0000, 'MultiNavigation', 'pushDestinationByName is not support');
    let promise: Promise<void> = Promise.reject({message: 'not support'});
    return promise;
  }

  replacePath(info: NavPathInfo, animated?: boolean): void;
  replacePath(info: NavPathInfo, options?: NavigationOptions): void;
  replacePath(info: NavPathInfo, optionParam?: boolean | NavigationOptions): void {
    let animated: boolean = true;
    if (optionParam !== undefined) {
      if (typeof optionParam === 'boolean') {
        animated = optionParam;
      } else if (optionParam.animated !== undefined) {
        animated = optionParam.animated;
      } else {

      }
    }

    let totalSize = this.totalStack.length;
    let subStackSize = this.subStackList.length;
    if (totalSize < 1 || subStackSize < 1) {
      hilog.error(0x0000, 'MultiNavigation', 'replacePath fail stack is empty');
      return;
    }
    let currentTopPolicy = this.totalStack[totalSize - 1].policy;
    if (currentTopPolicy === SplitPolicy.PlACE_HOLDER_PAGE) {
      hilog.warn(0x0000, 'MultiNavigation', 'replacePath fail, not support replace placeHolder');
      return;
    }
    const newPolicyInfo = new MultiNavPolicyInfo(currentTopPolicy, info);
    this.subStackList[subStackSize - 1].replacePath(newPolicyInfo, animated);
    this.totalStack.pop();
    this.totalStack.push(newPolicyInfo);
  }

  replacePathByName(name: string, param: Object, animated?: boolean): void {
    this.replacePath({ name: name, param: param }, animated);
  }

  removeByIndexes(indexes: number[]): number {
    let indexesLength = indexes.length;
    hilog.info(0x0000, 'MultiNavigation', 'removeByIndexes indexesLength=' + indexesLength);
    if (indexesLength <= 0) {
      return 0;
    }
    let oriStackSize = this.totalStack.length;
    hilog.info(0x0000, 'MultiNavigation', 'removeByIndexes oriStackSize=' + oriStackSize);
    indexes.sort((a, b) => a - b);
    let i: number = 0;
    let currentStackInfoLength: number = 0;
    let outerIndexes: number[] = [];
    hilog.info(0x0000, 'MultiNavigation', 'removeByIndexes this.subStackList.length=' + this.subStackList.length +
      ', oriStackSize=' + oriStackSize);
    this.subStackList.forEach((subStack, subStackIndex) => {
      let stepStartIndex = currentStackInfoLength;
      currentStackInfoLength += subStack.getAllInfoLength();
      const subIndexes: number[] = [];
      for (; i < indexes.length; ) {
        if (indexes[i] < currentStackInfoLength) {
          subIndexes.push(indexes[i] - stepStartIndex);
          i++;
        } else {
          break;
        }
      }
      subStack.removeByIndexes(subIndexes);
      if (!subStack.hasPrimaryInfo()) {
        outerIndexes.push(subStackIndex);
      }
    });
    hilog.info(0x0000, 'MultiNavigation', 'removeByIndexes outerIndexes.length=' + outerIndexes.length);
    this.outerStack.removeByIndexes(outerIndexes);
    this.subStackList = this.subStackList.filter((subStack) => {
      return subStack.hasPrimaryInfo()
    });

    this.totalStack = [];
    this.subStackList.forEach((subStack) => {
      this.totalStack.push(...subStack.getPrimaryInfoList());
      this.totalStack.push(...subStack.getSecondaryInfoList());
    })
    this.handleRefreshPlaceHolderIfNeeded();
    this.checkAndNotifyHomeChange();
    let size = oriStackSize - this.totalStack.length;
    hilog.info(0x0000, 'MultiNavigation', 'removeByIndexes size=' + size);
    return size;
  }

  removeByName(name: string): number {
    let oriStackSize = this.totalStack.length;
    hilog.info(0x0000, 'MultiNavigation', 'removeByName name=' + name + ', oriStackSize=' + oriStackSize);
    let outerIndexes: number[] = [];
    this.subStackList.forEach((subStack, index) => {
      subStack.removeByName(name);
      if (!subStack.hasPrimaryInfo()) {
        outerIndexes.push(index);
      }
    });
    this.outerStack.removeByIndexes(outerIndexes);
    hilog.info(0x0000, 'MultiNavigation', 'removeByName outerIndexes.length=' + outerIndexes.length);
    this.subStackList = this.subStackList.filter((subStack) => {
      return subStack.hasPrimaryInfo()
    });

    this.totalStack = [];
    this.subStackList.forEach((subStack) => {
      this.totalStack.push(...subStack.getPrimaryInfoList());
      this.totalStack.push(...subStack.getSecondaryInfoList());
    })
    this.handleRefreshPlaceHolderIfNeeded();
    this.checkAndNotifyHomeChange();
    let size = oriStackSize - this.totalStack.length;
    hilog.info(0x0000, 'MultiNavigation', 'removeByName size=' + size);
    return size;
  }


  pop(animated?: boolean): NavPathInfo | undefined;
  pop(result?: Object, animated?: boolean): NavPathInfo | undefined;
  pop(result?: Object | boolean, animated?: boolean): NavPathInfo | undefined {
    let totalSize = this.totalStack.length;
    let subStackLength = this.subStackList.length;
    if (totalSize < 1 || subStackLength < 1) {
      hilog.error(0x0000, 'MultiNavigation', 'MultiNavPathStack pop fail stack is empty!');
      return undefined;
    }
    hilog.info(0x0000, 'MultiNavigation', 'MultiNavPathStack pop totalSize=' + totalSize +
      ', subStackLength' + subStackLength);

    if (this.keepBottomPageFlag && (totalSize === 1 ||
      (this.placeHolderPolicyInfo !== undefined && totalSize === 2 &&
        this.totalStack[1].policy === SplitPolicy.PlACE_HOLDER_PAGE))) {
      hilog.info(0x0000, 'MultiNavigation', 'MultiNavPathStack pop fail for keep bottom');
      return undefined;
    }
    let currentPath = this.totalStack[totalSize - 1].navInfo;
    let allInfoLength = this.subStackList[subStackLength - 1].getAllInfoLength();
    if (allInfoLength < 1) {
      hilog.error(0x0000, 'MultiNavigation', 'MultiNavPathStack pop fail sub stack is empty');
      return undefined;
    }
    let secondaryStackFirstPolice: SplitPolicy | undefined = undefined;
    if (allInfoLength > 1) {
      secondaryStackFirstPolice = this.subStackList[subStackLength - 1].getSecondaryInfoList()[0].policy;
    }
    hilog.info(0x0000, 'MultiNavigation', 'MultiNavPathStack pop allInfoLength=' + allInfoLength +
      ', secondaryStackFirstPolice' + secondaryStackFirstPolice);
    this.totalStack.pop();
    if (allInfoLength === 1) {
      // pop home
      this.outerStack.popInner(animated);
      let subStack = this.subStackList.pop();
      setTimeout(() => {
        subStack?.pop(false);
        subStack = undefined;
      }, 300);
    } else {
      if (allInfoLength === 2) {
        if (this.placeHolderPolicyInfo !== undefined) {
          if (secondaryStackFirstPolice === SplitPolicy.PlACE_HOLDER_PAGE) {
            this.outerStack.popInner(animated);
            let subStack = this.subStackList.pop();
            setTimeout(() => {
              subStack?.clear(false);
              subStack = undefined;
            }, 300);
            currentPath = this.totalStack.pop()?.navInfo;
          } else {
            if (this.needShowPlaceHolder()) {
              this.subStackList[subStackLength - 1].pop(animated);
              this.pushPlaceHolder(subStackLength - 1)
            } else {
              this.subStackList[subStackLength - 1].pop(animated);
            }
          }
        } else {
          this.subStackList[subStackLength - 1].pop(animated);
        }
      } else {
        this.subStackList[subStackLength - 1].pop(animated);
      }
    }
    hilog.info(0x0000, 'MultiNavigation', 'MultiNavPathStack pop currentPath.name = ' + currentPath?.name);

    if (result !== undefined && typeof result !== 'boolean' &&
      currentPath !== undefined && currentPath.onPop !== undefined) {
      let popInfo: PopInfo = {
        info: currentPath,
        result: result,
      };
      currentPath.onPop(popInfo);
    }
    this.handleRefreshPlaceHolderIfNeeded();
    this.checkAndNotifyHomeChange();
    hilog.info(0x0000, 'MultiNavigation', 'MultiNavPathStack pop stackSize = ' + this.totalStack.length);
    return currentPath;
  }

  popToName(name: string, animated?: boolean): number;
  popToName(name: string, result: Object, animated?: boolean): number;
  popToName(name: string, result?: Object | boolean, animated?: boolean): number {
    let index = this.totalStack.findIndex((value: MultiNavPolicyInfo) => {
      return value.navInfo?.name === name;
    })
    let totalSize = this.totalStack.length;
    let subStackLength = this.subStackList.length;
    if (totalSize < 1 || subStackLength < 1) {
      hilog.error(0x0000, 'MultiNavigation', 'popToName fail stack is empty!');
      return -1;
    }
    if (index !== -1) {
      let currentPath = this.totalStack[totalSize - 1].navInfo;
      let secondaryStackSize: number[] = [];
      this.subStackList.forEach((subStack, index) => {
        secondaryStackSize.push(this.subStackList[index].secondaryStack.size());
      });
      let removeIndex = 0;
      for (let i = 0; i < subStackLength; i++) {
        removeIndex++;
        if (index === removeIndex - 1) {
          this.subStackList[i]?.secondaryStack.clear();
          this.subStackList[i].secondaryStack.policyInfoList.splice(0);
          this.totalStack.splice(index + 1);
          this.clearTrashStack(i + 1, result, animated);
          break;
        } else if (index > removeIndex - 1 && index < removeIndex + secondaryStackSize[i]) {
          this.subStackList[i].secondaryStack.popToIndex(index - removeIndex);
          this.subStackList[i].secondaryStack.policyInfoList.splice(index - removeIndex + 1);
          this.totalStack.splice(index + 1);
          this.clearTrashStack(i + 1, result, animated);
        }
        removeIndex += secondaryStackSize[i];
      }
      if (result !== undefined && typeof result !== 'boolean' &&
        currentPath !== undefined && currentPath.onPop !== undefined) {
        let popInfo: PopInfo = {
          info: currentPath,
          result: result,
        };
        currentPath.onPop(popInfo);
      }
    }
    this.handleRefreshPlaceHolderIfNeeded();
    this.checkAndNotifyHomeChange();
    return index;
  }

  popToIndex(index: number, animated?: boolean): void;
  popToIndex(index: number, result: Object, animated?: boolean): void;
  popToIndex(index: number, result?: Object | boolean, animated?: boolean): void {
    hilog.info(0x0000, 'MultiNavigation', 'MultiNavPathStack popToIndex index = ' + index);
    if (index > this.totalStack.length || index < 0) {
      hilog.error(0x0000, 'MultiNavigation', 'popToIndex fail wrong index');
      return;
    }
    let totalSize = this.totalStack.length;
    let subStackLength = this.subStackList.length;
    if (totalSize < 1 || subStackLength < 1) {
      hilog.error(0x0000, 'MultiNavigation', 'popToIndex fail stack is empty!');
      return;
    }
    let currentPath = this.totalStack[totalSize - 1].navInfo;
    let secondaryStackSize: number[] = [];
    this.subStackList.forEach((subStack, index) => {
      secondaryStackSize.push(this.subStackList[index].secondaryStack.size());
    });
    let removeIndex = 0;
    for (let i = 0; i < subStackLength; i++) {
      removeIndex++;
      if (index === removeIndex - 1) {
        hilog.info(0x0000, 'MultiNavigation', 'MultiNavPathStack popToIndex home' + i);
        this.subStackList[i]?.secondaryStack.clear();
        this.subStackList[i].secondaryStack.policyInfoList.splice(0);
        this.totalStack.splice(index + 1);
        this.clearTrashStack(i + 1, result, animated);
        hilog.info(0x0000, 'MultiNavigation', 'MultiNavPathStack popToIndex totalStack=' + this.totalStack.length);
        break;
      } else if (index > removeIndex - 1 && index < removeIndex + secondaryStackSize[i]) {
        this.subStackList[i].secondaryStack.popToIndex(index - removeIndex);
        this.subStackList[i].secondaryStack.policyInfoList.splice(index - removeIndex + 1);
        this.totalStack.splice(index + 1);
        this.clearTrashStack(i + 1, result, animated);
      }
      removeIndex += secondaryStackSize[i];
    }

    if (result !== undefined && typeof result !== 'boolean' &&
      currentPath !== undefined && currentPath.onPop !== undefined) {
      let popInfo: PopInfo = {
        info: currentPath,
        result: result,
      };
      currentPath.onPop(popInfo);
    }
    this.handleRefreshPlaceHolderIfNeeded();
    this.checkAndNotifyHomeChange();
  }

  private clearTrashStack(index: number, result?: Object | boolean, animated?: boolean): void {
    hilog.info(0x0000, 'MultiNavigation', 'MultiNavPathStack popToIndex clearTrashStack' + index);
    for (let i = index; i < this.subStackList.length; i++) {
      hilog.info(0x0000, 'MultiNavigation', 'MultiNavPathStack popToIndex subStackList' + index);
      this.subStackList[i].primaryStack.clear();
      this.subStackList[i].secondaryStack.clear();
      this.subStackList[i].primaryStack.policyInfoList.splice(0);
      this.subStackList[i].secondaryStack.policyInfoList.splice(0);
    }
    this.subStackList.splice(index);
    hilog.info(0x0000, 'MultiNavigation', 'MultiNavPathStack popToIndex subStackList.length=' + this.subStackList.length);
    this.outerStack.popToIndex(index - 1, result, animated);
    hilog.info(0x0000, 'MultiNavigation', 'MultiNavPathStack popToIndex outerStack.size=' + this.outerStack.size());
  }

  moveToTop(name: string, animated?: boolean): number {
    hilog.info(0x0000, 'MultiNavigation', 'MultiNavPathStack moveToTop name=' + name);
    let index = this.totalStack.findIndex((value) => {
      return value.navInfo?.name === name;
    });
    if (index !== -1) {
      this.moveIndexToTop(index, animated);
    }

    return index;
  }

  moveIndexToTop(index: number, animated?: boolean): void {
    hilog.info(0x0000, 'MultiNavigation', 'MultiNavPathStack moveIndexToTop index=' + index);
    if (index < 0 || index > this.totalStack.length) {
      hilog.error(0x0000, 'MultiNavigation', 'MultiNavPathStack moveIndexToTop wrong index');
      return;
    }
    let subStackLength = this.subStackList.length;
    let currentStackInfoLength: number = 0;
    let outerIndex: number = -1;
    for (let subIndex = 0; subIndex < subStackLength; subIndex++) {
      let stepStartIndex = currentStackInfoLength;
      currentStackInfoLength += this.subStackList[subIndex].getAllInfoLength();
      if (index < currentStackInfoLength) {
        outerIndex = subIndex;
        if (this.subStackList[subIndex].getPrimaryPolicy() === SplitPolicy.HOME_PAGE) {
          let innerIndex = index - stepStartIndex;
          if (innerIndex !== 0) {
            this.subStackList[subIndex].secondaryStack.moveIndexToTop(innerIndex - 1, animated);
            const subInfo = this.subStackList[subIndex].secondaryStack.policyInfoList.splice(innerIndex - 1, 1);
            this.subStackList[subIndex].secondaryStack.policyInfoList.push(...subInfo);
          }
        }
        break;
      }
    }
    if (outerIndex !== -1) {
      let subStack = this.subStackList.splice(outerIndex, 1);
      this.subStackList.push(...subStack);
      this.outerStack.moveIndexToTop(outerIndex, animated);
    }

    this.totalStack = [];
    this.subStackList.forEach((subStack) => {
      this.totalStack.push(...subStack.getPrimaryInfoList());
      this.totalStack.push(...subStack.getSecondaryInfoList());
    });
    this.handleRefreshPlaceHolderIfNeeded();
    this.checkAndNotifyHomeChange();
  }

  clear(animated?: boolean): void {
    hilog.info(0x0000, 'MultiNavigation', 'MultiNavPathStack clear animated = ' + animated + ', keepBottomPage=' +
    this.keepBottomPageFlag);

    if (this.subStackList.length === 0 || this.totalStack.length === 0) {
      hilog.info(0x0000, 'MultiNavigation', 'MultiNavPathStack clear return size is 0');
      return;
    }
    if (this.keepBottomPageFlag) {
      let subStackLength = this.subStackList.length;
      for (let i = 1; i < subStackLength; i++) {
        this.subStackList[i].clear(animated);
      }
      this.outerStack.popToIndex(0, animated);
      this.subStackList.splice(1);
      if (this.placeHolderPolicyInfo !== undefined) {
        if (this.subStackList[0].getSecondaryInfoList().length > 1 &&
          this.subStackList[0].secondaryStack.policyInfoList[0].policy === SplitPolicy.PlACE_HOLDER_PAGE) {
          this.subStackList[0].clearSecondaryKeepPlaceHolder(animated);
          this.totalStack.splice(2);
        } else {
          this.subStackList[0].clearSecondary(animated);
          this.totalStack.splice(1);
          if (this.needShowPlaceHolder()) {
            this.subStackList[0].pushSecondaryPath(this.placeHolderPolicyInfo, animated);
            this.totalStack.push(this.placeHolderPolicyInfo);
          }
        }
      } else {
        this.subStackList[0].clearSecondary(animated);
        this.totalStack.splice(1);
      }

      this.checkAndNotifyHomeChange();
      return;
    }
    this.subStackList.forEach((subStack) => {
      subStack.clear(animated);
    })
    this.outerStack.clear(animated);
    this.subStackList.splice(0);
    this.totalStack.splice(0)
  }

  getAllPathName(): string[] {
    let result: string[] = [];
    this.totalStack.forEach((value) => {
      if (value.navInfo !== undefined) {
        result.push(value.navInfo.name);
      }
    })
    return result;
  }

  getParamByIndex(index: number): Object | undefined {
    let result: Object | undefined = undefined;
    if (index >= 0 && index < this.totalStack.length) {
      result = this.totalStack[index].navInfo?.param as Object;
    }
    return result;
  }

  getParamByName(name: string): Object[] {
    let result: Object[] = [];
    this.totalStack.forEach((value) => {
      if (value.navInfo !== undefined && value.navInfo.name == name) {
        result.push(value.navInfo.param as Object);
      }
    })
    return result;
  }

  getIndexByName(name: string): number[] {
    let result: number[] = [];
    for (let i = 0; i < this.totalStack.length; i++) {
      if (this.totalStack[i].navInfo?.name === name) {
        result.push(i);
      }
    }
    return result;
  }

  getParent(): NavPathStack {
    hilog.error(0x0000, 'MultiNavigation', 'getParent is not support!');
    throw new Error('getParent is not support in multi navigation');
  }

  size(): number {
    return this.totalStack.length;
  }

  disableAnimation(value: boolean): void {
    for (const subStack of this.subStackList) {
      subStack.disableAnimation(value);
    }
    this.outerStack.disableAnimation(value);
    this.disableAllAnimation = value;
  }

  setInterception(interception: NavigationInterception): void {
    hilog.error(0x0000, 'MultiNavigation', 'setInterception is not support!');
    throw new Error('setInterception is not support in multi navigation');
  }

  setPagePolicy(policyMap: Map<string, SplitPolicy>): void {
    this.mPolicyMap = policyMap;
  }

  switchFullScreenState(isFullScreen?: boolean): boolean {
    let totalStackSize = this.totalStack.length;
    let subStackListLength = this.subStackList.length;
    if (subStackListLength < 1 || totalStackSize < 1) {
      return false;
    }
    if (this.subStackList[subStackListLength - 1].getPrimaryPolicy() !== SplitPolicy.HOME_PAGE) {
      return false;
    }
    if (this.totalStack[totalStackSize - 1].policy === SplitPolicy.PlACE_HOLDER_PAGE) {
      return false;
    }
    if (this.totalStack[totalStackSize - 1].isFullScreen === isFullScreen) {
      hilog.info(0x0000, 'MultiNavigation', 'switchFullScreen is same:' + isFullScreen);
      return true;
    }
    hilog.info(0x0000, 'MultiNavigation', 'switchFullScreen name=' +
      this.totalStack[totalStackSize - 1].navInfo?.name +
      ', from ' + this.totalStack[totalStackSize - 1].isFullScreen + ' to ' + isFullScreen);
    this.totalStack[totalStackSize - 1].isFullScreen = isFullScreen;
    this.subStackList[subStackListLength - 1].refreshFullScreen();
    return true;
  }

  setHomeWidthRange(minPercent: number, maxPercent: number): void {
    if (!this.checkInputPercent(minPercent) || !this.checkInputPercent(maxPercent)) {
      hilog.error(0x0000, 'MultiNavigation', 'setHomeWidthRange failed, wrong param:' +
        ', ' + minPercent + ', ' + maxPercent)
      return;
    }
    this.homeWidthPercents = [minPercent, maxPercent];
    this.refreshHomeWidth();
  }

  keepBottomPage(keepBottom: boolean): void {
    this.keepBottomPageFlag = keepBottom;
  }

  registerHomeChangeListener(lister: HomeChangeListener): void {
    if (this.homeChangeListener === undefined) {
      this.homeChangeListener = lister;
    }
  }

  unregisterHomeChangeListener(): void {
    this.homeChangeListener = undefined;
  }

  setPlaceholderPage(info: NavPathInfo): void {
    this.placeHolderPolicyInfo = new MultiNavPolicyInfo(SplitPolicy.PlACE_HOLDER_PAGE, info);
  }

  handleRefreshPlaceHolderIfNeeded() {
    if (this.placeHolderPolicyInfo === undefined) {
      return;
    }
    const subStackListLength = this.subStackList.length;
    if (subStackListLength < 1) {
      return;
    }
    const topStackPrimaryPolicy = this.subStackList[subStackListLength - 1].getPrimaryPolicy();
    if (topStackPrimaryPolicy !== SplitPolicy.HOME_PAGE) {
      return;
    }
    const subStackAllInfoLength = this.subStackList[subStackListLength - 1].getAllInfoLength();
    let secondaryStackFirstPolice: SplitPolicy | undefined = undefined;
    if (subStackAllInfoLength > 1) {
      secondaryStackFirstPolice = this.subStackList[subStackListLength - 1].getSecondaryInfoList()[0].policy;
    }
    if (this.needShowPlaceHolder()) {
      if (subStackAllInfoLength === 1) {
        this.pushPlaceHolder(subStackListLength - 1);
      }
    } else {
      if (secondaryStackFirstPolice === SplitPolicy.PlACE_HOLDER_PAGE) {
        if (subStackAllInfoLength === 2) {
          this.popPlaceHolder(subStackListLength - 1);
        } else {
          this.removeFirstPlaceHolder(subStackListLength - 1);
        }
      }
    }
  }

  private removeFirstPlaceHolder(subIndex: number): void {
    this.subStackList[subIndex].removeByIndexes([1]);
    this.totalStack = [];
    this.subStackList.forEach((subStack) => {
      this.totalStack.push(...subStack.getPrimaryInfoList());
      this.totalStack.push(...subStack.getSecondaryInfoList());
    })
  }

  private pushPlaceHolder(subIndex: number): void {
    this.subStackList[subIndex].pushSecondaryPath(this.placeHolderPolicyInfo!, false);
    this.totalStack.push(this.placeHolderPolicyInfo!);
  }

  private popPlaceHolder(subIndex: number): void {
    this.subStackList[subIndex].pop(false);
    this.totalStack.pop();
    this.checkAndNotifyHomeChange();
  }

  private needShowPlaceHolder(): boolean {
    if (!this.isLarge) {
      hilog.info(0x0000, 'MultiNavigation', 'do not show placeHolder for drawable width is less then breakpoint');
      return false;
    }
    if (DeviceHelper.isStraightProduct()) {
      hilog.info(0x0000, 'MultiNavigation', 'do not show placeHolder for straight product');
      return false;
    }
    if (DeviceHelper.isPhone() && DeviceHelper.isFold() &&
      this.needRenderDisplayMode.displayMode === display.FoldStatus.FOLD_STATUS_FOLDED) {
      hilog.info(0x0000, 'MultiNavigation', 'do not show placeHolder for fold status');
      return false;
    }
    if (DeviceHelper.isTablet() && this.isPortrait) {
      hilog.info(0x0000, 'MultiNavigation', 'do not show placeHolder for portrait tablet');
      return false;
    }
    return true;
  }

  private checkAndNotifyHomeChange(): void {
    if (this.totalStack.length === 0) {
      return;
    }
    let topPolicyInfo = this.totalStack[this.totalStack.length - 1];
    if (topPolicyInfo === undefined) {
      return;
    }
    if (topPolicyInfo.policy === SplitPolicy.HOME_PAGE && topPolicyInfo.navInfo !== undefined) {
      this.homeChangeListener && this.homeChangeListener.onHomeShowOnTop(topPolicyInfo.navInfo.name);
    }
    if (this.totalStack.length <= 1) {
      return;
    }
    let secondPolicyInfo = this.totalStack[this.totalStack.length - 2];
    if (secondPolicyInfo === undefined) {
      return;
    }
    if (topPolicyInfo.policy === SplitPolicy.PlACE_HOLDER_PAGE &&
      secondPolicyInfo.policy === SplitPolicy.HOME_PAGE && secondPolicyInfo.navInfo !== undefined) {
      this.homeChangeListener && this.homeChangeListener.onHomeShowOnTop(secondPolicyInfo.navInfo.name);
    }
  }

  private refreshHomeWidth(): void {
    this.navWidthRangeModifier.minHomeWidth = `${this.homeWidthPercents[0]}%`;
    this.navWidthRangeModifier.maxHomeWidth = `${this.homeWidthPercents[1]}%`;
    this.navWidthRangeModifier.isApplicationSet = true;
  }

  private checkInputPercent(inputPercent: number): boolean {
    return (0 <= inputPercent && inputPercent <= 100);
  }
}

interface HomeChangeListener {
  onHomeShowOnTop: OnHomeShowOnTopCallback;
}

@Observed
export class NeedRenderIsFullScreen {
  isFullScreen: boolean | undefined = undefined;
}

@Observed
export class NeedRenderLeftClickCount {
  leftClickCount: number = 0;
}

@Observed
export class NeedRenderDisplayMode {
  displayMode: number = 0;
}

class MultiNavPolicyInfo {
  policy: SplitPolicy = SplitPolicy.DETAIL_PAGE;
  navInfo: NavPathInfo | undefined = undefined;
  isFullScreen: boolean | undefined = undefined;

  constructor(policy: SplitPolicy, navInfo: NavPathInfo) {
    this.policy = policy;
    this.navInfo = navInfo;
  }
}


export class MyNavPathStack extends NavPathStack {
  operates:NavPathStackOperate[] = [];
  type = 'NavPathStack';
  policyInfoList: MultiNavPolicyInfo[] = [];

  registerStackOperateCallback(operate: NavPathStackOperate) {
    let index = this.operates.findIndex((item) => { return item === operate});
    if (index === -1) {
      this.operates.push(operate);
    }
  }

  unregisterStackOperateCallback(operate: NavPathStackOperate) {
    let index = this.operates.findIndex((item) => { return item === operate});
    if (index !== -1) {
      this.operates.splice(index, 1);
    }
  }

  popInner(result?: Object | boolean, animated?: boolean): NavPathInfo | undefined {
    hilog.info(0x0000, 'MultiNavigation', 'MyNavPathStack pop from inner:');
    return super.pop(result, animated);
  }


  pop(animated?: boolean): NavPathInfo | undefined
  pop(result?: Object, animated?: boolean): NavPathInfo | undefined
  pop(result?: Object | boolean, animated?: boolean): NavPathInfo | undefined {
    hilog.info(0x0000, 'MultiNavigation', 'MyNavPathStack pop from system:');
    let ret: NavPathInfo | undefined = undefined;
    if (typeof animated === 'boolean') {
      ret = super.pop(animated);
    } else {
      ret = super.pop(result, animated);
    }
    this.policyInfoList.pop();
    this.operates.forEach((item) => {
      item.onSystemPop?.();
    })
    return ret;
  }
}

interface NavPathStackOperate {
  onSystemPop: Function;
}

interface MultiNavPathStackOperate {
  onPrimaryPop: Function;
  onSecondaryPop: Function;
}


class SubNavigationStack {
  primaryStack: MyNavPathStack = new MyNavPathStack();
  secondaryStack: MyNavPathStack = new MyNavPathStack();
  needRenderIsFullScreen: NeedRenderIsFullScreen = new NeedRenderIsFullScreen();
  multiOperates:MultiNavPathStackOperate[] = [];

  primaryNavPathStackOperate:NavPathStackOperate = {
    onSystemPop:() => {
      this.multiOperates.forEach((item) => {
        item.onPrimaryPop?.();
      })
    }
  }

  secondaryNavPathStackOperate:NavPathStackOperate = {
    onSystemPop:() => {
      this.multiOperates.forEach((item) => {
        item.onSecondaryPop?.();
      })
      this.refreshFullScreen();
    }
  }

  constructor() {
    this.primaryStack.registerStackOperateCallback(this.primaryNavPathStackOperate);
    this.secondaryStack.registerStackOperateCallback(this.secondaryNavPathStackOperate);
  }

  registerMultiStackOperateCallback(operate: MultiNavPathStackOperate) {
    let index = this.multiOperates.findIndex((item) => { return item === operate});
    if (index === -1) {
      this.multiOperates.push(operate);
    }
  }

  unregisterMultiStackOperateCallback(operate: MultiNavPathStackOperate) {
    let index = this.multiOperates.findIndex((item) => { return item === operate});
    if (index !== -1) {
      this.multiOperates.splice(index, 1);
    }
  }

  getPrimaryPolicy(): SplitPolicy | undefined {
    if (this.primaryStack.policyInfoList.length < 1) {
      return undefined;
    }
    return this.primaryStack.policyInfoList[0].policy;
  }

  getPrimaryInfoList(): MultiNavPolicyInfo[] {
    return this.primaryStack.policyInfoList.slice();
  }

  getSecondaryInfoList(): MultiNavPolicyInfo[] {
    return this.secondaryStack.policyInfoList.slice();
  }

  getAllInfoLength(): number {
    return this.primaryStack.size() + this.secondaryStack.size();
  }

  hasPrimaryInfo(): boolean {
    return this.primaryStack.size() !== 0;
  }

  hasSecondaryInfo(): boolean {
    return this.secondaryStack.size() !== 0;
  }

  pushPrimaryPath(policyStack: MultiNavPolicyInfo, animated?: boolean) {
    this.primaryStack.policyInfoList.push(policyStack);
    this.primaryStack.pushPath(policyStack.navInfo, animated);
    this.refreshFullScreen();
  }

  pushSecondaryPath(policyStack: MultiNavPolicyInfo, animated?: boolean) {
    this.secondaryStack.policyInfoList.push(policyStack);
    this.secondaryStack.pushPath(policyStack.navInfo, animated);
    this.refreshFullScreen();
  }

  removeByIndexes(indexes: number[]): void {
    if (indexes.length < 1) {
      return;
    }
    if (indexes[0] === 0) {
      hilog.info(0x0000, 'MultiNavigation', 'SubNavigationStack removeByIndexes primaryStack');
      this.primaryStack.removeByIndexes([0]);
      this.primaryStack.policyInfoList.pop();
      this.clear(false);
      return;
    }
    if (indexes.length !== 0) {
      let slaveIndexes: number[] = [];
      indexes.forEach((value: number) => {
        slaveIndexes.push(value - 1);
      });
      this.secondaryStack.removeByIndexes(slaveIndexes);
      this.secondaryStack.policyInfoList = this.secondaryStack.policyInfoList.filter((value, index) => {
        return value && !slaveIndexes.includes(index);
      })
    }
    this.refreshFullScreen();
  }

  removeByName(name: string): void {
    this.primaryStack.removeByName(name);
    this.primaryStack.policyInfoList = this.primaryStack.policyInfoList.filter((value) => {
      return value.navInfo?.name !== name
    });
    if (!this.hasPrimaryInfo()) {
      this.clear(false);
      return;
    }
    this.secondaryStack.removeByName(name);
    this.secondaryStack.policyInfoList = this.secondaryStack.policyInfoList.filter((value) => {
      return value.navInfo?.name !== name
    });
    this.refreshFullScreen();
  }

  pop(result?: Object | boolean, animated?: boolean): NavPathInfo | undefined {
    let ret: NavPathInfo | undefined = undefined
    if (this.secondaryStack.policyInfoList.length > 0) {
      ret = this.popSecondary(result, animated);
    } else {
      ret = this.popPrimary(result, animated);
    }
    this.refreshFullScreen();
    return ret;
  }

  clearSecondary(animated?: boolean) {
    this.secondaryStack.clear(animated);
    this.secondaryStack.policyInfoList.splice(0);
    this.refreshFullScreen();
  }

  clearSecondaryKeepPlaceHolder(animated?: boolean) {
    this.secondaryStack.popToIndex(0, animated);
    this.secondaryStack.policyInfoList.splice(1);
    this.refreshFullScreen();
  }

  clear(animated?: boolean) {
    this.secondaryStack.clear(animated);
    this.primaryStack.clear(animated);
    this.secondaryStack.policyInfoList.splice(0);
    this.primaryStack.policyInfoList.splice(0);
  }

  disableAnimation(value: boolean): void {
    this.primaryStack.disableAnimation(value);
    this.secondaryStack.disableAnimation(value);
  }

  replacePath(info: MultiNavPolicyInfo, animated?: boolean): void {
    if (this.secondaryStack.policyInfoList.length > 0) {
      this.replaceSecond(info, animated);
    } else {
      this.replacePrimary(info, animated);
    }
    this.refreshFullScreen();
  }

  refreshFullScreen() {
    let secondInfoListLength = this.secondaryStack.policyInfoList.length
    if (secondInfoListLength > 0) {
      this.needRenderIsFullScreen.isFullScreen =
        this.secondaryStack.policyInfoList[secondInfoListLength - 1].isFullScreen;
      return;
    }
    let primaryInfoListLength = this.primaryStack.policyInfoList.length
    if (primaryInfoListLength > 0) {
      this.needRenderIsFullScreen.isFullScreen =
        this.primaryStack.policyInfoList[primaryInfoListLength - 1].isFullScreen;
    }
  }

  private replacePrimary(info: MultiNavPolicyInfo, animated?: boolean): void {
    this.primaryStack.policyInfoList.pop();
    this.primaryStack.policyInfoList.push(info)
    return this.primaryStack.replacePath(info.navInfo, animated);
  }

  private replaceSecond(info: MultiNavPolicyInfo, animated?: boolean): void {
    this.secondaryStack.policyInfoList.pop();
    this.secondaryStack.policyInfoList.push(info)
    return this.secondaryStack.replacePath(info.navInfo, animated);
  }

  private popPrimary(result?: Object | boolean, animated?: boolean): NavPathInfo | undefined {
    this.primaryStack.policyInfoList.pop();
    return this.primaryStack.popInner(result, animated);
  }

  private popSecondary(result?: Object | boolean, animated?: boolean): NavPathInfo | undefined {
    this.secondaryStack.policyInfoList.pop();
    return this.secondaryStack.popInner(result, animated);
  }
}

declare type NavDestinationBuildFunction = (name: string, param?: object) => void;

declare type OnNavigationModeChangeCallback = (mode: NavigationMode) => void;

declare type OnHomeShowOnTopCallback = (name: string) => void;