鸿蒙学习实战之路-STG系列(4/11)-应用选择页功能详解

朋友们,前三篇我们学习了 Screen Time Guard Kit 的基本概念、开发准备和用户授权管理。今天这篇我们就来学习如何使用应用选择页功能,包括拉起应用选择页和拉起许可应用跳转页 o(╯□╰)o

应用选择页就像是"点菜系统",用户可以在一个半模态页面中选择自己想要管控的应用,系统会返回应用的 token。有了 token,就可以对选中的应用进行管控了~

今天这篇,我会手把手带你实现应用选择页的完整功能,全程不超过5分钟(不含你测试的时间)~


一、什么是应用选择页?

应用选择页是 Screen Time Guard Kit 提供的一个半模态页面,让用户可以方便地选择要管控的应用。

1. 两种应用选择页

类型 说明 适用场景
应用选择页 用户可以选择要管控的应用 设置管控策略、设置应用访问限制
许可应用跳转页 展示用户可以使用的许可应用,点击可跳转 管控期间快速访问许可应用

2. 应用 Token 是什么?

Token 就像是应用的"身份证号",系统用它来唯一标识一个应用。Token 不包含应用的包名、应用名等敏感信息,保护用户隐私安全 _

🥦 西兰花小贴士:
Token 是系统生成的唯一标识,开发者无法获取到应用的包名和应用名,这样既保护了用户隐私,又防止了开发者滥用~


二、拉起应用选择页

拉起应用选择页是最常用的功能,就像让用户在菜单上点菜一样。

1. 业务流程

拉起应用选择页的流程就像这样:

应用调用拉起应用选择页接口
    ↓
系统检查应用是否有权限、用户是否授权
    ↓
如果没有权限 → 抛出错误码
如果有权限 → 拉起应用选择列表
    ↓
如果传入 token → 预勾选对应的应用
如果没传入 token → 不预勾选
    ↓
用户选择应用后点击完成
    ↓
返回用户选中的应用 token 列表

2. 核心接口

拉起应用选择页的接口:

接口名 说明
startAppPicker(context, appSelection) 拉起应用选择页,返回选中的应用 token 列表

3. 应用列表说明

应用选择页中的应用列表有一些限制:

不包含的应用类型 说明
系统关键应用 电话、联系人、设置、未成年模式等
管控发起应用本身 你的应用本身不会出现在列表中
已授权的管控应用 其他已经获得管控权限的应用

🥦 西兰花小贴士:
系统关键应用不能被管控,这是为了保障手机的基本功能正常运行。这个限制也合理吧? (┓( ´∀` )┏

4. 开发步骤

步骤 1: 导入相关模块
import { appPicker } from '@kit.ScreenTimeGuardKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
步骤 2: 拉起应用选择页
@Entry
@Component
struct AppPickerDemo {
  @State selectedTokens: string[] = [];
  @State selectedCount: number = 0;

  build() {
    Column() {
      Text('应用选择页演示')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 40 })

      Text('已选择应用数量: ' + this.selectedCount)
        .fontSize(18)
        .margin({ top: 30 })

      Button('拉起应用选择页')
        .onClick(async () => {
          try {
            // 拉起应用选择页
            const tokens = await appPicker.startAppPicker(
              this.getUIContext().getHostContext() as common.UIAbilityContext,
              { appTokens: this.selectedTokens } // 传入已选择的 token,用于预勾选
            );

            this.selectedTokens = tokens;
            this.selectedCount = tokens.length;

            hilog.info(0x0000, 'AppPickerDemo', 
              `selected tokens: ${JSON.stringify(tokens)}`);
          } catch (err) {
            const message = (err as BusinessError).message;
            const code = (err as BusinessError).code;
            hilog.error(0x0000, 'AppPickerDemo',
              `startAppPicker failed with error code: ${code}, message: ${message}`);
          }
        })
        .margin({ top: 30 })
        .width('80%')

      Button('清空选择')
        .onClick(() => {
          this.selectedTokens = [];
          this.selectedCount = 0;
        })
        .margin({ top: 10 })
        .width('80%')
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }
}

🥦 西兰花小贴士:
传入 appTokens 参数可以实现预勾选功能,用户再次打开选择页时会看到之前选中的应用~


三、拉起许可应用跳转页

许可应用跳转页是 6.0.2(22) 版本新增的功能,用于在被管控期间快速访问许可应用。

1. 业务流程

拉起许可应用跳转页的流程就像这样:

应用调用拉起许可应用跳转页接口
    ↓
系统检查应用是否有权限、用户是否授权
    ↓
如果没有权限 → 抛出错误码
如果有权限 → 获取全量应用信息
    ↓
判断是否显示 TrustApp(可信应用)
    ↓
拉起应用列表 Form
    ↓
用户点击应用图标
    ↓
跳转到对应的应用

2. 核心接口

拉起许可应用跳转页的接口:

接口名 说明
startAppForm(context, appSelection, appSubTitle, displayTrustApp) 拉起许可应用跳转页

参数说明:

  • context: 应用上下文
  • appSelection: 应用信息,包含 token 列表
  • appSubTitle: 页面副标题
  • displayTrustApp: 是否显示可信应用

3. 开发步骤

@Entry
@Component
struct AppFormDemo {
  @State selectedTokens: string[] = [];

  build() {
    Column() {
      Text('许可应用跳转页演示')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 40 })

      Button('1. 先选择应用')
        .onClick(async () => {
          try {
            // 先拉起应用选择页,获取 token
            const tokens = await appPicker.startAppPicker(
              this.getUIContext().getHostContext() as common.UIAbilityContext,
              { appTokens: [] }
            );

            this.selectedTokens = tokens;
            hilog.info(0x0000, 'AppFormDemo', 
              `selected tokens: ${JSON.stringify(tokens)}`);
          } catch (err) {
            const message = (err as BusinessError).message;
            const code = (err as BusinessError).code;
            hilog.error(0x0000, 'AppFormDemo',
              `startAppPicker failed with error code: ${code}, message: ${message}`);
          }
        })
        .margin({ top: 30 })
        .width('80%')

      Button('2. 拉起许可应用跳转页')
        .onClick(async () => {
          if (this.selectedTokens.length === 0) {
            hilog.warn(0x0000, 'AppFormDemo', '请先选择应用');
            return;
          }

          try {
            // 拉起许可应用跳转页
            await appPicker.startAppForm(
              this.getUIContext().getHostContext() as common.UIAbilityContext,
              { appTokens: this.selectedTokens },
              '许可应用', // 页面副标题
              false // 是否显示可信应用
            );

            hilog.info(0x0000, 'AppFormDemo', 'startAppForm succeeded');
          } catch (err) {
            const message = (err as BusinessError).message;
            const code = (err as BusinessError).code;
            hilog.error(0x0000, 'AppFormDemo',
              `startAppForm failed with error code: ${code}, message: ${message}`);
          }
        })
        .margin({ top: 10 })
        .width('80%')
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }
}

🥦 西兰花小贴士:
许可应用跳转页通常用于管控期间,让用户快速访问一些允许使用的应用,比如学习类应用、通讯工具等~


四、完整示例代码

下面是一个完整的示例,展示了如何实现应用选择页的完整功能:

import { appPicker } from '@kit.ScreenTimeGuardKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';
import { preferences } from '@kit.ArkData';

@Entry
@Component
struct AppPickerFullDemo {
  @State selectedTokens: string[] = [];
  @State selectedCount: number = 0;
  @State lastUpdateTime: string = '';
  private pref: preferences.Preferences | null = null;

  async aboutToAppear() {
    // 初始化用户首选项
    let context = this.getUIContext().getHostContext() as common.UIAbilityContext;
    this.pref = await preferences.getPreferences(context, 'app_picker_data');

    // 加载之前保存的 token
    await this.loadSelectedTokens();
  }

  /**
   * 加载之前保存的 token
   */
  async loadSelectedTokens() {
    try {
      if (this.pref) {
        const tokens = await this.pref.get('selected_tokens', '') as string;
        if (tokens) {
          this.selectedTokens = JSON.parse(tokens);
          this.selectedCount = this.selectedTokens.length;
        }

        const updateTime = await this.pref.get('last_update_time', '') as string;
        this.lastUpdateTime = updateTime;
      }
    } catch (err) {
      hilog.error(0x0000, 'AppPickerFullDemo', 'loadSelectedTokens failed');
    }
  }

  /**
   * 保存 token
   */
  async saveSelectedTokens() {
    try {
      if (this.pref) {
        await this.pref.put('selected_tokens', JSON.stringify(this.selectedTokens));
        await this.pref.put('last_update_time', new Date().toLocaleString());
        await this.pref.flush();
        this.lastUpdateTime = new Date().toLocaleString();
      }
    } catch (err) {
      hilog.error(0x0000, 'AppPickerFullDemo', 'saveSelectedTokens failed');
    }
  }

  /**
   * 拉起应用选择页
   */
  async startAppPicker() {
    try {
      const tokens = await appPicker.startAppPicker(
        this.getUIContext().getHostContext() as common.UIAbilityContext,
        { appTokens: this.selectedTokens } // 传入已选择的 token,用于预勾选
      );

      this.selectedTokens = tokens;
      this.selectedCount = tokens.length;

      // 保存到用户首选项
      await this.saveSelectedTokens();

      hilog.info(0x0000, 'AppPickerFullDemo', 
        `selected tokens: ${JSON.stringify(tokens)}`);
    } catch (err) {
      const message = (err as BusinessError).message;
      const code = (err as BusinessError).code;
      hilog.error(0x0000, 'AppPickerFullDemo',
        `startAppPicker failed with error code: ${code}, message: ${message}`);
    }
  }

  /**
   * 拉起许可应用跳转页
   */
  async startAppForm() {
    if (this.selectedTokens.length === 0) {
      hilog.warn(0x0000, 'AppPickerFullDemo', '请先选择应用');
      return;
    }

    try {
      await appPicker.startAppForm(
        this.getUIContext().getHostContext() as common.UIAbilityContext,
        { appTokens: this.selectedTokens },
        '许可应用',
        false
      );

      hilog.info(0x0000, 'AppPickerFullDemo', 'startAppForm succeeded');
    } catch (err) {
      const message = (err as BusinessError).message;
      const code = (err as BusinessError).code;
      hilog.error(0x0000, 'AppPickerFullDemo',
        `startAppForm failed with error code: ${code}, message: ${message}`);
    }
  }

  /**
   * 清空选择
   */
  clearSelection() {
    this.selectedTokens = [];
    this.selectedCount = 0;
    this.lastUpdateTime = '';
  }

  build() {
    Column() {
      Text('应用选择页完整演示')
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 40 })

      Text('已选择应用数量: ' + this.selectedCount)
        .fontSize(18)
        .margin({ top: 30 })

      if (this.lastUpdateTime) {
        Text('最后更新时间: ' + this.lastUpdateTime)
          .fontSize(14)
          .margin({ top: 5 })
          .fontColor('#999999')
      }

      Button('拉起应用选择页')
        .onClick(() => this.startAppPicker())
        .margin({ top: 30 })
        .width('80%')

      Button('拉起许可应用跳转页')
        .onClick(() => this.startAppForm())
        .margin({ top: 10 })
        .width('80%')

      Button('清空选择')
        .onClick(() => this.clearSelection())
        .margin({ top: 10 })
        .width('80%')
    }
    .width('100%')
    .height('100%')
    .padding(20)
  }
}

五、应用选择页的使用场景

应用选择页在很多场景下都非常有用:

1. 设置管控策略

用户可以选择要管控的应用,然后设置时间策略。

// 选择要管控的游戏应用
const tokens = await appPicker.startAppPicker(context, { appTokens: [] });

// 为选中的应用设置管控策略
const strategy: guardService.GuardStrategy = {
  name: '游戏管控',
  timeStrategy: {
    type: guardService.TimeStrategyType.START_END_TIME_TYPE,
    startTime: '09:00',
    endTime: '18:00',
    repeat: [1, 2, 3, 4, 5] // 周一到周五
  },
  appInfo: { appTokens: tokens },
  appRestrictionType: guardService.RestrictionType.BLOCKLIST_TYPE
};

await guardService.addGuardStrategy(strategy);

2. 设置应用访问限制

用户可以选择要限制的应用,然后设置访问限制。

// 选择要限制的娱乐应用
const tokens = await appPicker.startAppPicker(context, { appTokens: [] });

// 设置应用访问限制
await guardService.setAppsRestriction(
  { appTokens: tokens },
  guardService.RestrictionType.BLOCKLIST_TYPE
);

3. 管控期间快速访问许可应用

用户在被管控期间,可以快速访问一些许可应用。

// 显示学习类应用,让用户快速访问
const learningAppTokens = ['token1', 'token2', 'token3'];
await appPicker.startAppForm(
  context,
  { appTokens: learningAppTokens },
  '学习应用',
  false
);

六、注意事项

🥦 西兰花警告:

  1. 必须先获得用户授权: 使用应用选择页之前,必须先请求用户授权,否则会报错
  2. 系统关键应用不能选择: 电话、联系人、设置等系统关键应用不会出现在列表中
  3. 管控发起应用不会出现: 你的应用本身不会出现在选择列表中
  4. Token 需要妥善保管: Token 是应用的身份标识,需要保存好,用于后续的管控操作
  5. 许可应用跳转页需要 Token: 拉起许可应用跳转页之前,需要先获取应用的 Token
  6. 版本要求: 许可应用跳转页功能需要 6.0.2(22) 及以上版本

七、文档资源

官方文档链接:


八、总结

应用选择页是 Screen Time Guard Kit 提供的便捷功能,让用户可以方便地选择要管控的应用。

核心要点:

  1. 应用选择页让用户选择要管控的应用
  2. 系统返回应用的 Token,用于后续的管控操作
  3. 许可应用跳转页用于管控期间快速访问许可应用
  4. Token 不包含应用的敏感信息,保护用户隐私
  5. 系统关键应用不能被管控

应用选择页就像"点菜系统",用户选好后,系统会给你一张"菜单"(Token 列表),然后就可以对这些应用进行管控了 _


下一步行动

建议你:

  1. 先实现基本的应用选择页功能
  2. 添加 Token 保存和加载功能
  3. 实现许可应用跳转页功能
  4. 结合用户授权管理,实现完整的流程
  5. 测试各种场景,确保功能正常

记住,不教理论,只给你能跑的代码和避坑指南! _


我是盐焗西兰花,
不教理论,只给你能跑的代码和避坑指南。
下期见!🥦

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐