Как обычная клавиатура iOS от Google, Gboard, программно отклоняет самое ближайшее приложение?
Пользовательское приложение iOS от Google, Gboard, имеет интересную функцию, которая не может быть выполнена с использованием общедоступных API-интерфейсов в SDK для iOS ( с IOS 10). Я хотел бы точно знать, как Google выполняет задачу по программному возврату одного приложения в стек переключения приложений в Gboard.
Пользовательские клавиатуры iOS имеют два основных компонента: контейнерное приложение и расширение для клавиатуры. Расширение приложения для клавиатуры запускается в отдельном процессе ОС, который запускается всякий раз, когда пользователь находится в любом приложении на своем телефоне, которое требует ввода текста.
Это приблизительные шаги, которые можно выполнить, используя Gboard, чтобы увидеть эффект программного возврата в предыдущее приложение:
- Пользователь запускает приложение Apple Messages на своем iPhone и отбирает текстовое поле, чтобы начать ввод текста.
- Открывается расширение клавиатуры Gboard, и пользователи видят специальную клавиатуру Gboard (пока они все еще находятся в приложении Apple Messages).
- Пользователь вводит ключ микрофона внутри расширения клавиатуры Gboard для ввода текста в речь.
- Gboard использует настраиваемую схему URL для запуска приложения-контейнера Gboard. Клавиатура Gboard и приложение Apple отправляются на один уровень в стек приложения, а приложение контейнера Gboard теперь является самым передним приложением в стеке приложений. Приложение контейнера Gboard использует микрофон для прослушивания речи пользователя и переводит его в текст, который он помещает на экран.
- Пользователь нажимает кнопку "Готово", когда их удовлетворяет ввод текста, который они видят на экране.
- Здесь происходит волшебство... при отключении экрана ввода текста приложение-контейнер Gboard также автоматически отбрасывается. Приложение контейнера Gboard уходит и заменяется приложением Apple Messages (иногда процесс расширения клавиатуры Gboard все еще жив, иногда он перезагружается, а иногда его нужно перезапустить вручную, нажав внутри текстового поля.). Как Google выполняет это?
- Наконец, пользователь видит текст, который был просто переведен автоматически вставлен в поле ввода текста. Предположительно Google выполняет это посредством обмена данными между приложением контейнера Gboard и расширением клавиатуры.
Я бы предположил, что Google использует частные API-интерфейсы, исследуя иерархию представлений строки состояния, используя Objective-C интроспекцию времени выполнения и как-то синтезируя события крана или вызывая открытый объект/действие. Я изучил это очень мало и смог найти интересные подклассы UIView внутри строки состояния, например UIStatusBarBreadcrumbItemView, который содержит массив UISystemNavigationAction s. Я продолжаю изучать эти классы в надежде, что смогу найти способ репликации взаимодействия с пользователем.
Я понимаю, что использование частных API-интерфейсов - это хороший способ, чтобы ваше приложение отклонялось из App Store - это не проблема, о которой я бы хотел ответить в ответ. Я смотрю в первую очередь на конкретные ответы о том, как именно Google выполняет задачу по программному возврату одного приложения в стек переключения приложений в Gboard.
Ответы
Ответ 1
Ваша догадка правильная - Gboard использует частный API для этого.
... хотя и не путем изучения иерархии представлений или вставки событий.
Когда действие "голос-текст" выполняется, мы можем проверить syslog из Xcode или Console, который вызывает метод -[AVAudioSession setActive:withOptions:error:]
. Поэтому я обратил свое внимание на приложение Gboard и искал трассировку стека, связанную с этим.
Поднимая стек вызовов, мы можем найти метод -[GKBVoiceRecognitionViewController navigateBackToPreviousApp]
и...
![введите описание изображения здесь]()
... _systemNavigationAction
? Да, определенно частный API.
Так как class_getInstanceVariable
является общедоступным API, а "_systemNavigationAction"
является строковым литералом, автоматическая проверка не может заметить использование частного API, и, по мнению экспертов, люди не видят ничего плохого в "прыжке назад" предыдущее приложение "поведение. Или, вероятно, потому, что они Google, а вы не...
Фактический код, который выполняет действие "вернуться к предыдущему приложению", выглядит следующим образом:
@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 повезло времени)