Commit log

20841d1 Close WebXDC zip files explicitly

Click to expand commit body
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.

Amolith created

ad98a0c Handle Android 17 telecomui callback ordering

Click to expand commit body
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.

Amolith created

f9eb2c5 null-guard onScrollListener

Click to expand commit body
turns out that `android.widget.AbsView` calls the listener immediately
after setting it

Phillip Davis created

1b723c7 Fix NPE in tablet view's `secondary_fragment`

Phillip Davis created

8b06efb Remove unused `scrollToReply`

Click to expand commit body
from cherry-pick of f96f58acd1ce1e1a232991c6be283d8109a48ae7

Phillip Davis created

37c8d5a undo borked DB query from cherry-pick

Phillip Davis created

198b395 Further clean up jump to searched message

Click to expand commit body
(cherry picked from commit e8d62b34e4de5c138869538a0670e06258443358)

Arne created

29af267 Clean up jump to searched message

Click to expand commit body
(cherry picked from commit dfccdce61fb98e847245f5bed3c1945474b1bbd2)

Arne created

2c269ce Remove obsolete ConversationFragment.jumpTo

Phillip Davis created

b83835d yeet monocles-specific things from cherry-pick

Phillip Davis created

66a6bda Initial jump to searched message

Arne created

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

Stephen Paul Weber created

4db03ac fix NPE from reading UI thread data

Click to expand commit body
`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.

Phillip Davis created

7e40b83 Change default to 1MB

Click to expand commit body
To match the long ago change to JMP MMS limit

Stephen Paul Weber created

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

Phillip Davis created

4c4a058 guard against invalid JIDs in MAM query

Phillip Davis created

7e695e5 Don't rely on newer URLEncoder overload

Click to expand commit body
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

Stephen Paul Weber created

a720c89 fix sending oversized `WebxdcUpdate`s

Click to expand commit body
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

Phillip Davis created

16e9fea Do just one way

Stephen Paul Weber created

a6e8fdf Merge branch 'dont-linkify-invalid-urls' of https://git.secluded.site/cheogram-android

Click to expand commit body
* 'dont-linkify-invalid-urls' of https://git.secluded.site/cheogram-android:
  Don't linkify invalid links

Stephen Paul Weber created

7f45ef5 Don't linkify invalid links

Amolith created

9900135 Handle missing backup location picker

Click to expand commit body
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.

Amolith created

36ca290 dont linkify invalid URIs

Click to expand commit body
`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

Phillip Davis created

e1913c5 Show toast when can't open privacy policy

Amolith created

724355b fix crash when clicking invisible footer

Click to expand commit body
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

Daniel Gultsch created

29889be Oops

Stephen Paul Weber created

aa319e4 Support legacy prosody-only name

Stephen Paul Weber created

af5800c Port in isApp logic from Borogove

Stephen Paul Weber created

3408dcc Allow emoji search by direct emoji

Stephen Paul Weber created

3bf4bbb test(entities): MucOptions cache interaction tests

Phillip Davis created

04eb939 test(persistance): cheogramMigrate idempotency

Phillip Davis created

9f5e282 test(persistance): truncated attributes assertion

Phillip Davis created

a4f7fb2 fix(entities): cache MUC occupants in own DB rows

Phillip Davis created

a9d648b Require a SCRAM iteration count of 4096 or higher

Click to expand commit body
The RFC specifies a minimum iteration count of 4096 only as a "SHOULD".
Therefore, requiring 4096 on the client side is technically not fully in
line with the spec. However, in the existing ecosystem, the servers that
we tested all use 4096 or more.

Daniel Gultsch created

3a0803f support latest UP spec wrt pending intent

Click to expand commit body
Previously we used a custom protocol extension to optionally use pending
intents instead of an 'application' extra; nowadays the UnifiedPush spec
supports pending intents officially (and we can stop accepting the
insecure 'application' extra)

Daniel Gultsch created

abe9fe1 limit XML element nesting to 128 levels

Click to expand commit body
beyond that we could theoretically get stack overflow exceptions due to
the recursion in our parser

Daniel Gultsch created

53e8ec1 Always indicate support for channel binding in SASL header

Click to expand commit body
This commit breaks logging in on servers that announce a -PLUS variant for
SCRAM but do not support XEP-0440.

On servers that do not support XEP-0440, we previously decided to use no
channel binding because picking "none" was better than picking an
unsupported one and failing the login.

However, this behaviour also required us to indicate that we did not
support channel binding; otherwise, the server, seeing an unknown binding
mechanism, would fail our login.

This was a decision made for the broadest possible compatibility with the
pre-0440 ecosystem.

Note that the y flag wasn't the only security layer. Conversations also
uses pinning (if you logged in once with -PLUS, it won't fall back) and
XEP-0474 (basically a fancy version of the y flag).

In addition there is a setting in Conversations to always require Channel
Binding. This will also automatically be turned on for conversations.im
and quicksy.im.

It has now been two years since XEP-0440 was released for ejabberd and
Prosody, and our compatibility concerns have shifted: if you want to have
-PLUS on your server, please update the server to support XEP-0440.

Daniel Gultsch created

dc5b902 fix: NPE from unsynchronized MucOptions access

Click to expand commit body
- ConversationGetMucOptionsRaceTest is in its own file to make the
  instrumentation barriers a little easier to see and enforce
  - It simulates basically the following situation:
    i. On app startup, all accounts connect to all MUCs, which
    eventually triggers joinMuc, which calls
    Conversation.resetMucOptions.
    This happens on account's individual XMPP connection Thread.
    ii. At the same time, on the UI thread, we bind
    ConversationsOverviewFragment, which eventually leads to
    ConversationAdapter.onBindViewHolder, which calls getMucOptions.
    iii. Conversation.resetMucOptions is not synchronized, so it can
    run after the null-check in getMucOptions, resulting in a null
    return value.
  - Used AtomicReference instead of synchronizing resetMucOptions
  because joinMuc is run on a directExecutor associated with the
  XmppConnection thread. While the synchronized getMucOptions was
  called right after this in the existing code, the documentation for
  directExecutor has this to say:
  When a ListenableFuture listener is registered to run under
  directExecutor, the listener can execute in any of three possible
  threads:
      - When a thread attaches a listener to a ListenableFuture that's
      already complete, the listener runs immediately in that thread.
      - When a thread attaches a listener to a ListenableFuture that's
      incomplete and the ListenableFuture later completes normally,
      the listener runs in the thread that completes the ListenableFuture.
      - When a listener is attached to a ListenableFuture and the
      ListenableFuture gets cancelled, the listener runs immediately
      in the thread that cancelled the Future.
  - (This is applicable, since directExecutor uses in
  XmppConnectionService are extensively attached to ListenableFuture)
  The docs continue:
  A specific warning about locking: Code that executes user-supplied
  tasks, such as ListenableFuture listeners, should take care not to
  do so while holding a lock. Additionally, as a further line of
  defense, prefer not to perform any locking inside a task that will
  be run under directExecutor: Not only might the wait for a lock
  be long, but if the running thread was holding a lock, the
  listener may deadlock or break lock isolation.
  - Altogether, AtomicReference seems much more aligned with how Google
  intends directExecutor to be used.

Anyway, here's the stacktrace that reported this error:
```
2026-02-20 10:41:09.089  7610-7610  AndroidRuntime          com.cheogram.android                 E  FATAL EXCEPTION: main (Ask Gemini)
                                                                                                    Process: com.cheogram.android, PID: 7610
                                                                                                    java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String eu.siacs.conversations.entities.MucOptions.getName()' on a null object reference
                                                                                                    	at eu.siacs.conversations.entities.Conversation.getName(Conversation.java:1068)
                                                                                                    	at eu.siacs.conversations.entities.Conversation.getAvatarName(Conversation.java:1784)
                                                                                                    	at eu.siacs.conversations.ui.util.AvatarWorkerTask.setContentDescription(AvatarWorkerTask.java:135)
                                                                                                    	at eu.siacs.conversations.ui.util.AvatarWorkerTask.loadAvatar(AvatarWorkerTask.java:105)
                                                                                                    	at eu.siacs.conversations.ui.adapter.ConversationAdapter.onBindViewHolder(ConversationAdapter.java:255)
                                                                                                    	at eu.siacs.conversations.ui.adapter.ConversationAdapter.onBindViewHolder(ConversationAdapter.java:33)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7846)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7953)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6742)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:7013)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6853)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6849)
                                                                                                    	at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2422)
                                                                                                    	at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1722)
                                                                                                    	at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1682)
                                                                                                    	at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:747)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4737)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:4459)
                                                                                                    	at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:5011)
                                                                                                    	at android.view.View.layout(View.java:25626)
                                                                                                    	at android.view.ViewGroup.layout(ViewGroup.java:6460)
                                                                                                    	at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891)
                                                                                                    	at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1729)
                                                                                                    	at android.widget.LinearLayout.onLayout(LinearLayout.java:1638)
                                                                                                    	at android.view.View.layout(View.java:25626)
                                                                                                    	at android.view.ViewGroup.layout(ViewGroup.java:6460)
                                                                                                    	at androidx.coordinatorlayout.widget.CoordinatorLayout.layoutChild(CoordinatorLayout.java:1213)
                                                                                                    	at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayoutChild(CoordinatorLayout.java:899)
                                                                                                    	at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:919)
                                                                                                    	at android.view.View.layout(View.java:25626)
                                                                                                    	at android.view.ViewGroup.layout(ViewGroup.java:6460)
                                                                                                    	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
                                                                                                    	at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
                                                                                                    	at android.view.View.layout(View.java:25626)
                                                                                                    	at android.view.ViewGroup.layout(ViewGroup.java:6460)
                                                                                                    	at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891)
                                                                                                    	at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1729)
                                                                                                    	at android.widget.LinearLayout.onLayout(LinearLayout.java:1638)
                                                                                                    	at android.view.View.layout(View.java:25626)
                                                                                                    	at android.view.ViewGroup.layout(ViewGroup.java:6460)
                                                                                                    	at androidx.drawerlayout.widget.DrawerLayout.onLayout(DrawerLayout.java:1273)
                                                                                                    	at android.view.View.layout(View.java:25626)
                                                                                                    	at android.view.ViewGroup.layout(ViewGroup.java:6460)
                                                                                                    	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
                                                                                                    	at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
                                                                                                    	at android.view.View.layout(View.java:25626)
                                                                                                    	at android.view.ViewGroup.layout(ViewGroup.java:6460)
                                                                                                    	at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
                                                                                                    	at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
                                                                                                    	at android.view.View.layout(View.java:25626)
                                                                                                    	at android.view.ViewGroup.layout(ViewGroup.java:6460)
```

Phillip Davis created

9c165e4 Allow camera/mic in command UI webview

Stephen Paul Weber created