Google的自定义iOS键盘Gboard如何以编程方式关闭最前端的应用程序? [英] How does Google's custom iOS keyboard, Gboard, programmatically dismiss the frontmost app?

查看:225
本文介绍了Google的自定义iOS键盘Gboard如何以编程方式关闭最前端的应用程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Google的自定义iOS应用 Gboard ,具有一个有趣的功能,无法使用iOS SDK(从iOS 10开始)中的公共API来完成. 我想确切地了解Google如何完成以编程方式弹出Gboard的应用切换"堆栈中的一个应用的任务.

Google's custom iOS app, Gboard, has an interesting feature that can't be accomplished using public APIs for in the iOS SDK (as of iOS 10). I'd like to know exactly how Google accomplishes the task of programmatically popping back one app in the App Switching stack in Gboard.

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

Custom iOS keyboards have two major components: the container app and the keyboard app extension. The keyboard app extension runs in a separate OS process that is started up whenever a user is in any app on their phone that requires text input.

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

These are the approximate steps that can be followed, using Gboard, to see the effect of programmatically returning to a previous app:

  1. 用户在其iPhone上启动Apple 消息应用,然后点击一个文本字段以开始输入文本.
  2. 启动了Gboard键盘扩展,用户可以看到Gboard自定义键盘(虽然它们仍在Apple Messages应用程序中).
  3. 用户点击Gboard键盘扩展部分内的麦克风键以进行语音到文本的输入.
  4. Gboard使用
  5. 这是发生魔术的地方……由于关闭了文本输入屏幕,因此Gboard容器应用也自动被关闭. Gboard容器应用程序消失了,由Apple Messages应用程序代替(有时Gboard键盘扩展过程仍在运行,有时需要重新启动,有时需要通过在文本字段内点击来手动重新启动.). Google如何做到这一点?
  6. 最后,用户看到刚翻译的文本自动插入到文本输入字段中.大概是Google通过在Gboard容器应用和键盘扩展之间共享数据.
  1. A user starts the Apple Messages app on their iPhone and taps a text field to begin entering text.
  2. The Gboard keyboard extension is launched and the users sees the Gboard custom keyboard (while they are still in the Apple Messages app).
  3. The user taps the microphone key inside the Gboard keyboard extension to do voice-to-text input.
  4. Gboard uses a custom url scheme to launch the Gboard container app. The Gboard keyboard and Apple messages app are pushed down one layer in the App stack and the Gboard container app is now the frontmost app in the App stack. The Gboard container app uses the microphone to listen to the user's speech and translates it into text which it places onto the screen.
  5. The user taps the "Done" button when they are satisfied with the text input they see on the screen.
  6. This is where the magic happens… as the text input screen is dismissed, the Gboard container app is also dismissed automatically. The Gboard container app goes away and is replaced by the Apple Messages app (sometimes the Gboard keyboard extension process is still alive, sometimes it is relaunched, and sometimes it needs to be re-launched manually by tapping inside a text field.) . How does Google accomplish this?
  7. Finally, the user sees the text that was just translated inserted automatically inside the text input field. Presumably Google accomplishes this by sharing data between the Gboard container app and the keyboard extension.

我会假设Google通过使用Objective-C运行时自省来探索状态栏的视图层次结构,并以某种方式合成点击事件或调用公开的目标/动作来使用私有API.我对此进行了很少的探索,并且能够在状态栏中找到有趣的UIView子类,例如

I would assume that Google is using private APIs by exploring the status bar's view hierarchy using Objective-C runtime introspection and somehow synthesizing tap events or calling an exposed target / action. I've explored this a very little and have been able to find interesting UIView subclasses inside the status bar, like UIStatusBarBreadcrumbItemView which contains an array of UISystemNavigationActions. I'm continuing to explore these classes in the hope that I can find some way of replicating the user interaction.

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

I understand that using private APIs is a good way to get your app submission rejected from the App Store - this isn't a concern that I'd like to be addressed in the answer. I'm looking primarily for specific answers about how exactly how Google accomplishes the task of programmatically popping back one app in the App Switching stack in Gboard.

推荐答案

您的猜测是正确的-Gboard正在使用私有API来实现.

…,尽管不是通过探索视图层次结构或事件注入.

完成语音转文本操作后,我们可以从Xcode或Console检查syslog是否调用了-[AVAudioSession setActive:withOptions:error:]方法.因此,我对Gboard应用程序进行了反向工程,并寻找与此相关的堆栈跟踪.

When the voice-to-text action is done, we can check the syslog from Xcode or Console that it calls the -[AVAudioSession setActive:withOptions:error:] method. So I've reverse-engineered the Gboard app and look for the stack trace related to this.

攀爬调用堆栈,我们可以找到-[GKBVoiceRecognitionViewController navigateBackToPreviousApp]方法,然后…

Climbing up the call stack we can find the -[GKBVoiceRecognitionViewController navigateBackToPreviousApp] method, and…

_systemNavigationAction ?是的,绝对是私有API.

_systemNavigationAction? Yep, definitely private API.

由于class_getInstanceVariable是公共API,而"_systemNavigationAction"是字符串文字,因此自动检查器无法记录私有API的使用情况,人工审核者可能看不到跳转到以前的应用"行为.或者可能是因为它们是Google,而您却不是…

Since class_getInstanceVariable is a public API and "_systemNavigationAction" is a string literal, the automatic checker is not able to note the private API usage, and the human reviewers probably don't see anything wrong with the "jump back to the previous app" behavior. Or probably because they are Google and you are not…

执行跳回上一个应用"操作的实际代码如下:

The actual code that performs the "jump back to previous app" action is like this:

@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:方法执行实际的返回"操作.

In particular, the -sendResponseForDestination: method performs the actual "go back" action.

(由于该API未公开,因此Gboard实际上错误地使用了该API .他们使用了错误的签名-(void)sendResponseForDestination:(id)destination.但是,除了1以外的所有数字都可以正常工作,所以这次Google开发人员很幸运)

(Since the API is undocumented, Gboard is actually using the API incorrectly. They used the wrong signature -(void)sendResponseForDestination:(id)destination. But it happens that all numbers other than 1 will work the same, so the Google developers are lucky this time)

这篇关于Google的自定义iOS键盘Gboard如何以编程方式关闭最前端的应用程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆