Check integer range bounds, current values, and options as XML integer
text before parsing them as floats. Also require each parsed value to
round-trip to the same integer and reject no-option unit-step ranges
outside float's consecutive exact integer window.
Make integer slider eligibility reject fractional bounds, current
values, or options before choosing the slider UI. Those forms now use
the numeric text input instead of relying on slider rounding.
Treat empty or missing ranged numeric values as ineligible for slider
rendering. The existing numeric datatype handling on text fields keeps
these fields editable without implying the minimum value is selected.
Compute slider prerequisites before mutating the recycled view holder.
If an invalid field reaches the slider holder, fail fast instead of
leaving a partially rebound view.
Cover rebinding one holder across incompatible slider step sizes,
including a forced draw, to verify the old step-size reset is not
needed.
WebXDC preview and message rows were constructing page objects without a
lifecycle owner that released the backing ZipFile. Keep ZipFile
ownership inside active sessions, close previews after metadata and icon
reads, and make session removal close pages explicitly.
Avoid constructing long-lived WebxdcPage instances during message
binding. Use cached file metadata for the initial button label, refresh
the manifest title in the background, and load icons through the
existing file attachment executor.
When the legacy telecom settings component is missing on Android 17,
Cheogram falls back to the telecomui activity. Android can still deliver
an early DIALLER_INTEGRATION callback for the failed legacy launch
before the real fallback result arrives.
Use a separate fallback request code and ignore that bogus legacy
callback while the telecomui result is still pending. Keep the final
phone account settings launch deferred until the fallback flow returns
so the deeper telecomui screen stays first in the task.
Persist the request code and in-flight flag across savedInstanceState so
the guard survives if Android kills and recreates the activity mid-flow,
matching the existing pending-intent persistence.
Amolith
created
a06294a
Catch ActivityNotFoundException by trying new name
Click to expand commit body
Seems com.android.server.telecom.settings.EnableAccountPreferenceActivity was
named com.android.server.telecomui.settings.EnableAccountPreferenceActivity in
Android 17 beta: https://issuetracker.google.com/issues/476293144
Add logging around the onboarding dialler-integration flow so we can see which
system activity branch runs and what ACTION_CHANGE_PHONE_ACCOUNTS resolves to
on-device.
b87aa34
Fix contract in Bookmark and Contact compareTo
Click to expand commit body
Null-guard getDisplayName() and use compareTo instead of equals so
the return value satisfies anti-symmetry, previously violated by
examples like this:
```
- A: displayName "alice", jid z@example.com
- B: displayName "Alice", jid a@example.com
- C: displayName "alice", jid m@example.com
```
Phillip Davis
created
546e460
Merge branch 'no-such-method-in-geohelper' of https://git.secluded.site/cheogram-android
Click to expand commit body
* 'no-such-method-in-geohelper' of https://git.secluded.site/cheogram-android:
Don't rely on newer URLEncoder overload
`onTextDeleted` is called from a single-threaded executor. the zero-arg
overload of `storeNextMessage` reads from the `EditMessage` component,
but `onTextDeleted` should obviously never read a non-null message, so
there's no reason to coordinate it with the UI thread.
c9e4f68
Return POSITION_NONE in `destroyItem` for null
Click to expand commit body
Can't provoke the race, but my theory about
https://todo.sr.ht/~singpolyma/soprani.ca/599 is that it's caused by `o
== page1.get()` (or `page2.get()`) evaluating to `true` when
`page1.get() == null`, causing the item to stick around in the dataset,
e.g., during device rotation or some other situation where the view is
destroyed, and breaking ViewPager's internal bookkeeping
Phillip Davis
created
fef5a1a
add SafeViewPager to guard ViewPager crash
Click to expand commit body
Android's ViewPager caches the active pointer id from ACTION_DOWN and
later calls findPointerIndex(mActivePointerId) on MOVE events. When
the OS delivers a MOVE whose pointer set no longer contains that id
(a known multi-touch race), findPointerIndex returns -1 and getX(-1)
throws IllegalArgumentException from native code, crashing the app:
java.lang.IllegalArgumentException: invalid pointerIndex -1 for
MotionEvent { action=MOVE, ... }
at android.view.MotionEvent.nativeGetAxisValue(Native Method)
at android.view.MotionEvent.getX(MotionEvent.java:2655)
at androidx.viewpager.widget.ViewPager.onInterceptTouchEvent(ViewPager.java:2087)
SafeViewPager wraps onInterceptTouchEvent and onTouchEvent in a
try/catch and returns false on the exception, letting children handle
the event and letting the pager recover on the next clean DOWN.
Subsequent commits route the three ViewPager instances in the app
through this class.
A couple of places have implemented basically this exact workaround,
e.g., https://github.com/signalapp/Signal-Android/blob/main/app/src/main/java/org/thoughtcrime/securesms/components/viewpager/HackyViewPager.java
Old Android releases do not provide the URLEncoder overload that takes a
Charset, so a received geo URI could crash as soon as its notification
label was built. Go back to the older UTF-8 overload so the same label
encoding does not depend on an API that's missing at runtime.
Amolith
created
7ceed70
Merge branch 'investigate-activity-not-found' of https://git.secluded.site/cheogram-android
Click to expand commit body
* 'investigate-activity-not-found' of https://git.secluded.site/cheogram-android:
Show toast when can't open privacy policy
Stephen Paul Weber
created
fc9ba35
Merge branch 'handle-no-doc-activity-found' of https://git.secluded.site/cheogram-android
Click to expand commit body
* 'handle-no-doc-activity-found' of https://git.secluded.site/cheogram-android:
Handle missing backup location picker
manually checked that the delete query works even when the rows are in
the SqliteBlobTooBigException range
Phillip Davis
created
b849489
fix: guard destroyItem against null entries in ViewPager mItems
Click to expand commit body
`null` was to `ConversationPage` in `destroyItem` when
`ViewPager.dataSetChanged`'s POSITION_NONE branch cleaned up a dormant
null in `mItems`. The null originates from `instantiateItem`'s
bounds-check branch (`position-2 >= sessions.size() -> return null`),
which ViewPager stores via `addNewItem` and later passes back to
`destroyItem` during cleanup.
Deterministically tested fix by the first call to `instantiateItem`
should return `null`, after which a `notifyDataSetChanged` via
tapping another command. Resulted in same crash as the report.
Phillip Davis
created
d1c1405
onPageSelected should ignore other Conversations
Catch ActivityNotFoundException when launching the backup location
picker. Devices without an OPEN_DOCUMENT_TREE handler now get the
existing no_application_found toast instead of crashing from the
preference tap.
`Message.getLinks` wasn't broken, but I thought it was at first, and
then I had already written the test, so here it is.
Phillip Davis
created
2655f22
fix: pagers leak listeners on wide-screen devices
Click to expand commit body
On wide screens, Android re-uses the ViewPager + TabView
in between conversations, which means `Conversation.onDestroyView`
doesn't get called, which results in `mCurrentTab` getting set for
previously opened chats. This manifested as [0]
[0]: https://todo.sr.ht/~singpolyma/soprani.ca/554
the stack trace manifested as:
```
Version: Cheogram debug-local+free
Manufacturer: OnePlus
Device: OnePlus6
Timestamp: 2026-04-01 21:27:32 -0400
java.lang.IndexOutOfBoundsException: Index 1 out of bounds for length 1
at jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at jdk.internal.util.Preconditions.checkIndex(Preconditions.java:266)
at java.util.Objects.checkIndex(Objects.java:385)
at java.util.ArrayList.get(ArrayList.java:434)
at eu.siacs.conversations.ui.ManageAccountActivity.lambda$onCreate$3(ManageAccountActivity.java:140)
at eu.siacs.conversations.ui.ManageAccountActivity.$r8$lambda$fcEx2bGWBsQPp04OkU82pOGEvUg(Unknown Source:0)
at eu.siacs.conversations.ui.ManageAccountActivity$$ExternalSyntheticLambda0.onItemClick(D8$$SyntheticClass:0)
at android.widget.AdapterView.performItemClick(AdapterView.java:330)
at android.widget.AbsListView.performItemClick(AbsListView.java:1274)
at android.widget.AbsListView.onKeyUp(AbsListView.java:3511)
at android.widget.ListView.commonKey(ListView.java:2498)
at android.widget.ListView.onKeyUp(ListView.java:2371)
at android.view.KeyEvent.dispatch(KeyEvent.java:3471)
at android.view.View.dispatchKeyEvent(View.java:16521)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1982)
at android.widget.ListView.dispatchKeyEvent(ListView.java:2346)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1987)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1987)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1987)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1987)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1987)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1987)
at com.android.internal.policy.DecorView.superDispatchKeyEvent(DecorView.java:462)
at com.android.internal.policy.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1971)
at android.app.Activity.dispatchKeyEvent(Activity.java:4557)
at androidx.core.app.ComponentActivity.superDispatchKeyEvent(ComponentActivity.kt:96)
at androidx.core.view.KeyEventDispatcher.dispatchKeyEvent(KeyEventDispatcher.java:85)
at androidx.core.app.ComponentActivity.dispatchKeyEvent(ComponentActivity.kt:110)
at androidx.appcompat.app.AppCompatActivity.dispatchKeyEvent(AppCompatActivity.java:604)
at androidx.appcompat.view.WindowCallbackWrapper.dispatchKeyEvent(WindowCallbackWrapper.java:59)
at androidx.appcompat.app.AppCompatDelegateImpl$AppCompatWindowCallback.dispatchKeyEvent(AppCompatDelegateImpl.java:3397)
at com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:376)
at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:8034)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:7873)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7259)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7316)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7282)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:7448)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7290)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:7505)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7263)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7316)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7282)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7290)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7263)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7316)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7282)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:7481)
at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:7721)
at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:5053)
at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:4430)
at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:4421)
at android.view.inputmethod.InputMethodManager.-$$Nest$mfinishedInputEvent(Unknown Source:0)
at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:5030)
at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:181)
at android.os.MessageQueue.nativePollOnce(Native Method)
at android.os.MessageQueue.next(MessageQueue.java:387)
at android.os.Looper.loopOnce(Looper.java:189)
at android.os.Looper.loop(Looper.java:317)
at android.app.ActivityThread.main(ActivityThread.java:8934)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:591)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:911)
```
notice that this required tab navigation, as the footer doesn't seem
visible, although maybe possible to hit it with your thumb idk. this is
especially tricky when the footer is invisible, i.e., when there aren't
any PSTN gateways configured
Phillip Davis
created
9ddb808
fix `isBlockedMediaSha1` crash on malformed SHA1
Phillip Davis
created
794554f
Fix locale-dependent SQL in truncatedAttributesColumn()
Click to expand commit body
String.format with %d uses the default locale's numeral system.
On Persian (fa) locale this produces ۶۵۵۳۴ instead of 65534,
causing an SQLite error.
Extract the expression into truncatedAttributesColumn() so it
can be tested under a non-default locale, and replace
String.format with concatenation which always uses ASCII digits.
Phillip Davis
created
7ee1d24
UnifiedPush: add support for link activity