[Xposed Framework Modules] Roughy:
Exi for Swiftkey 2.3.5 release

2.3.5 [22.08.2019 ] - Compatibility ( Stable: 7.3.6.19 Beta: 7.3.7.18 )

Changes in 2.3.5

Fixed

- Compatibility targeting 7.3.7.18 ( Everything, backwards compatible except custom sound )

Changed

- Toolbar shortcut removed

The Xposed Installer app does not appear to be refreshing properly and may not list the latest version.

Please download it directly from the Xposed Repository below using a browser.

Download:

Exi for Swiftkey on Xposed module repository

My own mirror of all versions

Notes:

ClassHunter

A large part of what makes maintaining Xposed modules for apps difficult is that the code is all obfuscated and minimized.

The names and locations of all the classes and methods are garbled anew for every update, so you can't just figure these things out once and be done with it.

I developed ClassHunter to allow me to generate profiles for classes and methods, and find them in every release of Swiftkey.

We just chuck the profile at it and use the class or method it decides is the most similar.

Traditionally Proguard has been used for obfuscation, but Google recently introduced the R8 engine, which behaves /slightly/ differently. Or maybe they just changed the proguard rules.

The most significant change in the output is the access modifiers changed pretty much everywhere ( public, private, protected ). These access modifiers decide on what bits of code are allowed to access other bits of code.

Ideally ClassHunter would have been able to cope with this just fine, but it turns out I never implemented proper similarity score calculation for modifiers, and mostly just relied on a perfect match. In many cases I also did not weight these scores properly, so a class could be a 90% match, but because of a mismatched modifier the score would get lowered to 45%.

So most of the work this time has been improving ClassHunter. Modifiers now have proper similarity scores, class profile scores are weighted more reasonably, and several locations that used to rely on perfect matches now pass along the corresponding similarity score instead.

All this extra works naturally means that the one-time setup that causes Swiftkey to freeze for a while after an update takes longer than before.

Initially this was all the way up to 185 seconds on my device, up from about 20 seconds. After implementing some simpler score checking to weed out candidates whose max score would never meet a certain threshold, that is down to about 25 seconds, only slightly slower than before.

With these improvements the old profiles will identify the correct classes ( and the new profiles the old classes ).

Compatibility

Next came figuring out why all sorts of things were broken.

In rare cases I am forced to rely on the assumption that methods will appear in a certain order, have a specific name ( usually tied to the order ), or is the only one with a specific modifier.

In some cases methods will be removed entirely from the class, or a bunch of new methods will have been added to it making it difficult to track down the right one. The obfuscation and minimizing process will often split a class into several smaller classes.

Needless to say anywhere this approach was used was completely broken.

The method were we insert our gesture and keyboard overlay views can no longer be differentiated from its brethren, and we now instead rely on another method that it coincidentally calls just before returning the view we are interested in. It's the only method that takes an array ( varargs in this case ) of Views, making it far more unique and easier to identify than the old one.

The exact method that informs us of a swiftkey-key being pressed is no longer identifiable, so we hook every similar-ish method and rely on our gesture overlay view to tell us if it was a key-up or key-down.

We hook another method to cancel key actions, and when there's a KeyDown event we decide whether or not we want to cancel the corresponding event.

I'm not entirely sure how this is all set up, but before we could just cancel all key actions /before/ the KeyDown hook and it would still allow the current one through.

Probably as a result of us hooking a slightly different method now it will also cancel the current action, meaning you couldn't enter the symbols/numbers layout if you had symbols set as a modifier. This was solved by setting a flag that you could set to have it cancel all events /after/ the end of the current KeyDown event.

Apart from this there was a lot of adjusting and updating method profiles to make them work with both the stable and beta branches of Swiftkey, so we could avoid having to release an update that was not compatible with both.

In one case the class has changed so much that the it cannot be identifier in both stable and beta using the same profile.

I currently have it set to search for both profiles, and using the one that best matches its profile.

That will hopefully keep it going long enough for stable to catch up to beta, then I can remove it.

Everything should work fine in both beta and stable, with the exception of custom sounds which only works in beta. Will have to wait for stable to catch up.

The toolbar shortcut to open the Exi config app was working, but it would sometimes remain visible when the toolbar itself was minimized.

Removed rather than trying to fix.