Is InputMethodService.onUpdateSelection asynchronous? - android

I am making a custom IME. I am trying to track when the user manually changes the selection region of text (as opposed to the selection changing as a result of actions from the IME itself, like commitText).
To do this, I am keeping track of where the IME expects the selection position to be, and compares it to the actual selection position from onUpdateSelection.
For example, to commit text I use:
private fun commitTextToInputConnection(text: String)
{
moveExpectedSelection(text.length)
service.currentInputConnection.commitText(text, 1)
}
However, if I do:
commitTextToInputConnection("Test1")
commitTextToInputConnection("Test2")
I find that, in order, the following sequence occurs:
1 - ExpectedSelectionPosition updates for "Test1"
2 - ExpectedSelectionPosition updates for "Test2"
3 - onUpdateSelection for "Test1" is called
4 - onUpdateSelection for "Test2" is called
Obviously, this order is incorrect, and leads to my IME having an incorrect ExpectedSelectionPosition.
The strangest thing is that, for some Activities, the sequence of ExpectedSelectionPosition updates and onUpdateSelection calls always occur in the correct order. For other Activities, they consistently occur in the same (wrong) order.
What is going on here? I'm guessing commitText, etc, must be asynchronous, leading to this race condition, but this is not mentioned at all in the documentation.
Are there are any work-arounds to this issue? Or, are there any other ways I can listen exclusively for changes in text selection triggered manually by the user, and not the IME?

The solution was to use InputConnection.beginBatchEdit and InputConnection.endBatchEdit.
Any changes made to the currentInputConnection within a 'batchEdit' block are lumped together into a single onUpdateSelection call, at the end.
For example, when the following is executed:
service.currentInputConnection.beginBatchEdit()
commitTextToInputConnection("Test1")
commitTextToInputConnection("Test2")
service.currentInputConnection.endBatchEdit()
onUpdateSelection is called only once, after all the changes in the block have been made.

Related

android paging 3 line after adapter.submitData doesn't get executed

I am currently learning Paging 3 library. I have an app that has shimmer placeholder for showing while loading the data. When i get the data i want to hide shimmer layout and show recycler view.
lifecycleScope.launch {
quoteViewModel.getQuotes2().collectLatest {
binding.shimmerLayout.visibility = View.GONE // this line gets executed when view created and it causes shimmer layout to hide
binding.homeRecyclerView.visibility = View.VISIBLE
homeAdapter.submitData(it)
Log.d("mytag", "after submit data") // this line isn't executed
}
}
Problem here is the line after the submitData doesn't get executed.I searched and saw the reason was because submitData never returns so it doesn't get called. But i didn't find any example code. Can someone please show me an example code for solving this?
The logic i want to execute after submitData is to hide shimmerLayout and show recyclerView, but because that logic is before submit data, they are executed immediately, therefore shimmerLayout doesn't seem when loading data.
As you mentioned, it is true that the line after submitData() won't be executed since it doesn't return. What is it exactly that you are wanting to achieve using Paging 3 with it here?
I had a similar doubt where my recyclerview was not updating data fetched from Paging 3 because I was using collect operator. Dustin Lam, the author of Paging 3 library answered me here to replace the collect with collectLatest and that had fixed the issue for me.
In your case, you are already using collectLatest. One quick fix can be to put the submitData at the very end of collectLatest since that is what causes the issue.
As mention in the collectLatest definition,
Terminal flow operator that collects the given flow with a provided action. The crucial difference from collect is that when the original flow emits a new value then the action block for the previous value is cancelled.
Note that replacing collect with collectLatest fixed the issue for my case only because initially my StateFlow was emitting an Initial state, but when the data was ready, it emitted a Success state that wasn't collected by the collect operator since the submitData never returns, but collectLatest forces the previous action block to be cancelled and new value to be collected.
I'm unsure of a solution to your problem yet but either edit your question with the exact use case or in case you do not find any alternative to it, you can directly ask Dustin Lam about it like I did :)
I tried with: submitData(lifecycle, it) and it started to stop onto the lines after the Submit.
Here says: Caution: The submitData() method suspends and does not return until either the PagingSource is invalidated or the adapter's refresh method is called. This means that code after the submitData() call might execute much later than you intend.

Updating an UI upon changes

I am building an app that displays a bunch of information that the user can edit, and I am having trouble keeping the UI updating the data displaid so it is consistent with the new edits done at runtime.
I have implemented updateUI methods that basically look like:
void updateUI(){
((TextView) fieldDisplay).setText(fieldCurrentValue);
...
}
I know I can run this method in things like an AsyncTask or similar stuff that makes it execute continuously. But Im concerned about efficency since its not really necesary to update the UI all the time, but only when the user inputs a value wich is not that often.
What would be the best approach to this?
EDIT:
The question is how to make sure the an UI element shows the current value of a field, regardless of how or why that field is updated.
My case specifically is the with this. Im trying to make an RPG character sheet, and I have like a bazillion Skills, wich are affected by another lot of fields, such as Characteristics, Modifiers, Categories...
The application should behave so, whenever any of the many fields affects it changes, it should display the new value.
Now, since keeping track of what field affects what is part of the problem, if could update whenever any field whatsoever changes, but I dont know how to do that.

Is this going to be a huge If/Then statement?

I am trying to implement a series of notifications based on radio button options the user selects. The notifications date and time will set depending on both radio button options and the user selected date.
for example the user selects Option 1, 2 and 3 along with Jan 1st 2017 and 12 notifications are set every couple of days/weeks depending on said options.
Before I get too far into this, am I just looking at a complex if then statement to set these notifications or am i missing another solution?
When your if-else statements get too long you might be needing a switch statement instead. You said there are twelve notification options so a switch could do this just fine.
https://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html
Remember to separate each option in a modular fashion using different classes, or methods so that you just have to call each block of code.
A lot of switcing or if/else can also be a symptom of you trying to express intrinsically polymorphic code. You are essentially replicating the vtable the compiler would do for you. Following separating business logic from view (rendering) logic -- ie some form of MVC -- you could associate a subclass with each radio button to which the button delegates the logic when it is pressed (btw you might not necessarily need separate subclasses). The controller of each radio button may have a reference to some other object which stores the final frequency of days/hours/minutes, etc for each to call the alarm. So as each radio button is pressed, it delegates to its controller which in turn interprets the value of the radio button and in turn calls into the alarm controller, incrementing / decrementing some value using some appropriate API.
I know it might sound like over-engineering but it is quite simple to set up and it is likely that you will minimally need that alarm controller for other details (which also makes it a lot easier to test; you definitely do not want to shove business logic in your views (activities) as it makes testing a nightmare). So if you wish to simplify this, you could forego each button's individual controller and instead have each button directly invoke the alarm controller API if you prefer to update it.

Events not firing in Google Analytics via Google Tag Manager

I have been searching high and low for an answer on this and I am completly dumbfounded.
I am implementing simple click and page tracking in my Android app using GA, running this through GTM. All my "Screens" are visible in realtime in GA but I can't get "Events" to appear at all.
Well actually I can but the behaviour seems very bizarre. If I do not include a "Label" and a "Value" I can see the events appear. However if I add them (either as just a constant or a data layer variable) all events stop. I have confirmed the variables I want in "Label" and "Value" are coming through as I made a container with those values as "Category" and "Action" and could see them as expected in real time.
This leads me to think the app side implementation is perfectly fine but there is an issue with my tag in GTM. (Obviously not the Trigger as that too works when expected).
Ideally I would like to do something like this (the variables are data layer variables):
But this doesn't work. I see no Events.
The Event Value should be a number, not a string. Shuffle the fields, for example - Action - Click on: {{GTM - Click Target}}, Label - {{GTM - Click Value}}, and leave the value empty, this will fix your problem.
Make sure, you have correctly setted up Click listener.
Enable when defines when is listener available and where is applied to the all DOM elements.
Fire ON defines conditions, so in your case it could be {{event}} equals gtm.click or {{event}} equals gtm.linkClick .
This is the most common pitfall when setting listeners

How to perform Redo Undo operation in EditText

I want to know is there any method or any link or tutorial to perform redo undo operation in Android edittext. If any one knows than please let me know.
Quick note on the Antti-Brax/Divers(Kidinov) solution. It works great, except if you try to use it with a TextView post-API 23, you'll run into problems, because guess-what, Google actually added a hidden UndoManager (android.content.UndoManager) and didn't document it or make it obvious it was there. But if you have a hard/bluetooth keyboard in Marshmallow or Nougat and hit ^Z or SHIFT-^Z, you'll get undo/redo.
The problem comes if you're already using Antti-Brax's class with an EditText, and you also hook it to ^Z and shift-^Z, you'll run into problems with anyone using a hard keyboard. Namely the ^Z will trigger BOTH the native and Antti-Brax's undo, leading to two undos simultaneously, which isn't good. And after a few of them, you'll probably get a Spannable out of bounds crash.
A possible solution I found is to subclass the TextView/TextEdit/whatever and intercept the undo/redo calls from the TextView so they don't run as follows:
#Override
public boolean onTextContextMenuItem(int id) {
int ID_UNDO, ID_REDO;
try {
ID_UNDO = android.R.id.undo;
ID_REDO = android.R.id.redo;
} catch (Resources.NotFoundException e) {
ID_UNDO = 16908338; // 0x1020032
ID_REDO = 16908339; // 0x1020033
}
return !((id == ID_UNDO) || (id == ID_REDO)) && super.onTextContextMenuItem(id);
}
Those magic id numbers were found here, and are used only as a backup if the android.R.id.undo values aren't found. (it also might be reasonable to assume that if the values aren't there the feature isn't there, but anyway...)
This is not the best solution because both undo trackers are still there and both are running in the background. But at least you won't trigger both of them simultaneously with ^Z. It's the best I could think to do until this gets officially documented and the getUndoManager() methods of TextView is no longer hidden...
Why they made a feature you can't turn off (or even know if it was there or not) "live" in released Android I can't say.
I just opened an issue on Android's issue tracker if anyone wants to follow this.
There is an implementation of undo/redo for Android EditText in
http://credentiality-android-scripting.googlecode.com/hg/android/ScriptingLayerForAndroid/src/com/googlecode/android_scripting/activity/ScriptEditor.java
The code works but does not handle configuration changes properly. I am working on a fix and will post here when it is complete.
My Google search was :-
android edittext onTextChanged undo
I know this is an old question, but as there is no accepted answer, and this is an issue I've tackled myself from many angles, I'd like to add my solution in case it helps anyone. My answer is probably most relevant to large (1,000words+) volumes of text editing apps that require this feature.
The simplest way to resolve this problem is to make periodic copies of all text on screen, save it to an array and call setText() every time the Undo method is called. This makes for a reliable system, but it isn't ideal for large (i.e. 1,000words+) text editing apps. This is because it:
Is wasteful. It could be that only one word changes in a two thousand word document, so that's one thousand, nine hundred and ninety nine words needlessly committed to memory.
Can lead to performance issues, as some low-tier hardware struggles with rendering large amounts of text. On some of my test devices, this method can lead to freezes of a few seconds whenever Undo is called.
The solution I currently use is comparatively complex, but I've published the results in a library here.
Essentially, this library saves a copy of text as soon as a user begins typing, and then another copy of text once they've stopped typing for a set amount of time (in my case, two seconds). The two text strings are then compared, and the altered section of text returned, the indexes where the alterations occured, and details on whether or not the change was an addition of new text, a deletion, or a replacement of old text with new text.
The net result is that only the necessary text is saved, and when Undo is called, there is only a local delete(), replace() or insert() call, which makes for much faster operations on large text fields.
Here is the undo/redo implementation that was linked to from Gary Phillips' answer extracted into a reusable and universal undo/redo plugin for any widget that descends from a TextView. I added some code for persisting the undo history.
http://code.google.com/p/android/issues/detail?id=6458#c123
Hope this helps.
To preserve EditText Styling with regards to undo:
You can create an ArrayList<EditText> or ArrayList<String> (String containing html text) to store your last 10 (for example) actions. So ArrayList [0] would contain html text from your first action and ArrayList [9] would contain html text from your very last action. Each time the user taps "undo" in your app, you would apply ArrayList [size()-1] to your EditText and then remove ArrayList [size()-1] from your Array.

Categories

Resources