gpt4 book ai didi

ios - Google 的自定义 iOS 键盘 Gboard 如何以编程方式关闭最前面的应用程序?

转载 作者:IT王子 更新时间:2023-10-29 07:41:34 29 4
gpt4 key购买 nike

Google 的自定义 iOS 应用,Gboard ,有一个有趣的特性,在 iOS SDK(自 iOS 10 起)中使用公共(public) API 无法实现。 我想确切地知道 Google 如何以编程方式在 Gboard 的 App Switching 堆栈中完成弹出一个应用程序的任务。

自定义 iOS 键盘有两个主要组件:容器应用程序和键盘应用程序扩展。键盘应用程序扩展在单独的操作系统进程中运行,只要用户在手机上需要文本输入的任何应用程序中,该进程就会启动。

这些是使用 Gboard 可以遵循的大致步骤,以查看以编程方式返回到以前的应用程序的效果:

  1. 用户启动 Apple Messages iPhone 上的应用程序,然后点击文本字段开始输入文本。
  2. Gboard 键盘扩展程序已启动,用户会看到 Gboard 自定义键盘(此时他们仍在 Apple Messages 应用中)。
  3. 用户点击 Gboard 键盘扩展内的麦克风键进行语音到文本输入。
  4. Gboard 使用 custom url scheme启动 Gboard 容器应用程序。 Gboard 键盘和 Apple 消息应用程序在应用程序堆栈中被下推一层,Gboard 容器应用程序现在是应用程序堆栈中最前面的应用程序。 Gboard 容器应用使用麦克风收听用户的语音并将其翻译成文本并显示在屏幕上。
  5. 当用户对他们在屏幕上看到的文本输入感到满意时,他们会点击“完成”按钮。
  6. 这就是奇迹发生的地方……随着文本输入屏幕关闭,Gboard 容器应用程序也会自动关闭。 Gboard 容器应用程序消失并被 Apple Messages 应用程序取代(有时 Gboard 键盘扩展进程仍然存在,有时它会重新启动,有时需要通过在文本字段内点击来手动重新启动。)。 Google 如何做到这一点?
  7. 最后,用户会看到刚刚翻译的文本自动插入到文本输入字段中。大概谷歌通过 sharing data 完成了这个在 Gboard 容器应用和键盘扩展之间。

我假设 Google 通过使用 Objective-C 运行时内省(introspection)探索状态栏的 View 层次结构并以某种方式合成点击事件或调用公开的目标/操作来使用私有(private) API。我对此进行了一些探索,并且能够在状态栏中找到有趣的 UIView 子类,例如 UIStatusBarBreadcrumbItemView其中包含 UISystemNavigationAction 的数组秒。我将继续探索这些类,希望能找到一些方法来复制用户交互。

我知道使用私有(private) API 是让您的应用程序提交被 App Store 拒绝的好方法 - 这不是我希望在答案中解决的问题。我主要是在寻找有关 Google 如何准确地完成以编程方式在 Gboard 的应用程序切换堆栈中弹出一个应用程序的任务的具体答案。

最佳答案

您的猜测是正确的 — Gboard 使用私有(private) API 来执行此操作。

…尽管不是通过探索 View 层次结构或事件注入(inject)。

语音转文本操作完成后,我们可以从 Xcode 或控制台检查它调用 -[AVAudioSession setActive:withOptions:error:] 方法的系统日志。所以我对 Gboard 应用程序进行了逆向工程,并寻找与此相关的堆栈跟踪。

向上攀登调用堆栈,我们可以找到 -[GKBVoiceRecognitionViewController navigateBackToPreviousApp] 方法,以及......

enter image description here

_systemNavigationAction?是的,绝对是私有(private) API。

由于 class_getInstanceVariable 是一个公共(public) API 而 "_systemNavigationAction" 是一个字符串文字,自动检查器无法记录私有(private) API 的用法,人工审核者可能“跳回上一个应用程序”行为没有任何问题。或者可能是因为他们是谷歌而你不是……


执行“跳回上一个应用程序”操作的实际代码是这样的:

@import UIKit;
@import ObjectiveC.runtime;

@interface UISystemNavigationAction : NSObject
@property(nonatomic, readonly, nonnull) NSArray<NSNumber*>* destinations;
-(BOOL)sendResponseForDestination:(NSUInteger)destination;
@end

inline BOOL jumpBackToPreviousApp() {
Ivar sysNavIvar = class_getInstanceVariable(UIApplication.class, "_systemNavigationAction");
UIApplication* app = UIApplication.sharedApplication;
UISystemNavigationAction* action = object_getIvar(app, sysNavIvar);
if (!action) {
return NO;
}
NSUInteger destination = action.destinations.firstObject.unsignedIntegerValue;
return [action sendResponseForDestination:destination];
}

特别是,-sendResponseForDestination: 方法执行实际的“返回”操作。

(由于 API 未记录,Gboard 实际上错误地使用了 API。他们使用了错误的签名 -(void)sendResponseForDestination:(id)destination。但是它碰巧除 1 以外的所有数字都将工作相同,所以这次 Google 开发人员很幸运)

关于ios - Google 的自定义 iOS 键盘 Gboard 如何以编程方式关闭最前面的应用程序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42516357/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com