Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(android): Control when system keyboard Keyboard Picker clears activity stack #13313

Open
wants to merge 2 commits into
base: beta
Choose a base branch
from

Conversation

darcywong00
Copy link
Contributor

@darcywong00 darcywong00 commented Feb 21, 2025

Fixes #12991

For as long as Keyman for Android 2.8, the system keyboard KeyboardPickerActivity has been setting the Intent.FLAG_ACTIVITY_CLEAR_TASK flag which becomes the root activity and clears previous tasks (Keyman app).

When using Keyman as a system keyboard within the Keyman app, we don't want this to occur.

TODO:

  • Update API docs

Change

Add call to disable/enable when the system keyboard Keyboard Picker clears previous tasks.

User Testing

Setup - Install PR build of Keyman for Android
From "Get Started", set Keyman as the default system keyboard.

  • TEST_INAPP - Verifies keyboard picker within the Keyman app
  1. Launch Keyman for Android
  2. With the inapp keyboard, hold the globe key to launch the Keyboard Picker
  3. Dismiss the keyboard picker
  4. Verify the Keyman app is not dismissed
  5. From Keyman, access Keyman settings --> Install Keyboard or Dictionary --> Install from keyman.com
  6. From the keyboard search page, select the search box and select Keyman keyboard if need be
  7. With the Keyman OSK displayed, hold the globe key to launch the Keyboard Picker
  8. Dismiss the keyboard picker
  9. Verify the Keyman app is not dismissed
  • TEST_SYSTEM - Verifies keyboard picker outside the Keyman app
  1. Close Keyman if it's running

  2. Launch Chrome browser and select the text area and select Keyman keyboard if need be

  3. With the Keyman system keyboard OSK displayed, hold the globe key to launch the Keyboard Picker

  4. Dismiss the keyboard picker

  5. Verify Keyman is not launched in the background

@darcywong00 darcywong00 added this to the B18S2 milestone Feb 21, 2025
@keymanapp-test-bot keymanapp-test-bot bot added has-user-test user-test-required User tests have not been completed labels Feb 21, 2025
@github-actions github-actions bot added android/ android/engine/ and removed user-test-required User tests have not been completed labels Feb 21, 2025
@keymanapp-test-bot
Copy link

keymanapp-test-bot bot commented Feb 21, 2025

@darcywong00 darcywong00 force-pushed the fix/android/system-keyboard-picker-scope branch from dcd70c5 to 118e15b Compare February 24, 2025 01:35
@darcywong00 darcywong00 marked this pull request as ready for review February 24, 2025 01:35
@sgschantz sgschantz self-assigned this Feb 24, 2025
Copy link
Contributor

@sgschantz sgschantz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any issues without how this code will execute, but I had trouble understanding the naming, and I had to re-read it carefully to not be confused.

Part of the problem is that I didn't know this: "A task is a collection of activities that users interact with when trying to do something in your app." This is confusing to me because I don't think of activities as smaller than tasks. I think of a task as something that is discrete and an activity as something broader. You can't do much about the Android naming, but it is possible to clarify this relationship with your code and naming.

Beyond that, I think I would still be confused about what is being enabled and disabled. You are not clearing immediately but setting the flag to clear later, predicated on some other event. But that event is not mentioned in the name of the flag. I would rather see it included in the name of the flag (even a long name) than being required to read other code or comments to understand the purpose of the flag.

One other confusing thing is that the actual flag is named canClearActivityTask when it seems the flag is forcing the clear to happen. By prefixing it with 'can' I expect that the underlying action is available but optional. The difference in the flag name from the names of the functions that set the flag cause me to doubt that I understand the code that is applying the flag.

@darcywong00
Copy link
Contributor Author

Yeah, I find the naming quite clunky.
Basically we want to toggle Intent.INTENT_FLAG_ACTION_CLEAR

this flag will cause any existing task that would be associated with the activity to be cleared before the activity is started. That is, the activity becomes the new root of an otherwise empty task, and any old activities are finished.

Maybe something like this?

  • clearExistingActivity()
  • dontClearExistingActivity()

@dinakaranr
Copy link

dinakaranr commented Feb 24, 2025

Test Results

I tested this issue with the attached Keyman"18.0.199-beta-test-13313" build(24/02/2025) on Android 14(Physical device) and Android 12(Emulator). Here I am sharing my observation.

  • TEST_INAPP (Passed):
  1. Install the keyman-18.0.199.apk file.
  2. Launch the Keyman app.
  3. The "Get Started" menu appears, enable Keyman as a system keyboard and set it as the default keyboard.
  4. Open the "Keyboard Picker" by pressing the "globe" key.
  5. Close the keyboard picker
  6. Verified that the Keyman app is not closed.
  7. Navigate to the "Search the keyboard" by pressing Keyman settings --> Install Keyboard or Dictionary --> Install from keyman.com.
  8. Click in the Search box. (Search for a keyboard and install a keyboard if needed)
  9. Verified that the OSK displayed.
  10. Open the "Keyboard Picker" by pressing the "globe" key.
  11. Close the keyboard picker
  12. Verified that the Keyman app is not closed.
  • TEST_SYSTEM (Passed):
  1. Install the keyman-18.0.199.apk file.
  2. Launch the Keyman app.
  3. The "Get Started" menu appears, enable Keyman as a system keyboard and set it as the default keyboard.
  4. Close the Keyman app.
  5. Launch Chrome browser.
  6. Navigate to the Google search box(text area)
  7. Verified that the Keyman keyboard appeared.
  8. Open the "Keyboard Picker" by pressing the "globe" key.
  9. Close the keyboard picker
  10. Verified that the Keyman did not launch in the background.

@keymanapp-test-bot keymanapp-test-bot bot removed the user-test-required User tests have not been completed label Feb 24, 2025
@sgschantz
Copy link
Contributor

That is a lot better, but can it also specify when the existing activity is cleared? How do you best describe what actually triggers the clearing of the activity (provided this flag has been set)?

i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
if (KeyboardPickerActivity.getClearActivityTask()) {
// Keyboard Picker Activity becomes root activity and clears Keyman app
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a lot better, but can it also specify when the existing activity is cleared? How do you best describe what actually triggers the clearing of the activity (provided this flag has been set)?

This is the scenario:
When the Keyman system keyboard (as opposed to in-app keyboard) is launching the Keyboard Picker,
we generally want to dismiss the Keyman app if it's running.

The exception is when using the Keyman system keyboard within the Keyman app (e.g. doing a keyboard search).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, just looked at this again. I think what would help would be to associate your functions directly with showing the keyboard picker. The code reads like it is generic to clearing activity task (closing Keyman), but when you call the related functions, you include a comment that shows that this is only relevant to the keyboard picker.

So instead of clearExistingActivity, you could give the context in the name and say clearExistingActivityOnShowKeyboardPicker. That is, assuming you only ever plan to read and apply the flag when showing the keyboard picker. (That makes the comments pretty much redundant, but we'd always rather just read the function if we can instead of needing a comment to help us interpret the code.)

If the 'ExistingActivity' is always going to be Keyman -- it is the thing that is getting cleared or closed, then could it actually be closeKeymanOnShowKeyboardPicker? Or is that going too far?

Sorry to belabor this, but it seems like we are doing something concrete and discrete, but the terminology is referencing things that are generic and of wider scope. So my lack of familiarity with the Android architecture made it hard to comprehend. I think the code could be in more concrete domain/Keyman terms where the reference and mapping to the Android API only happens when you actually set Intent.FLAG_ACTIVITY_CLEAR_TASK.

I still have a problem with the variable canClearActivityTask which seems to really mean forceClearActivityTask or forceCloseKeyman (if closing Keyman is the only possible ActivityTask that we will be clearing).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, latest round of renames

I called the flag canCloseParentAppOnShowKeyboardPicker because there may be other app developers using Keyman Engine for Android (besides Keyman).

And then went with

  • dontCloseParentAppOnShowKeyboardPicker()
  • closeParentAppOnShowKeyboardPicker()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense -- I wasn't giving any thought to the API boundary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

bug(android): Keyman abruptly closes after choosing a keyboard from the keyboard picker menu
3 participants