aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ui/src/main/java/com/wireguard/android/activity (follow)
Commit message (Collapse)AuthorAgeFilesLines
* LogViewerActivity: remove unnecessary ?.Jason A. Donenfeld4 days1-1/+1
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: raise min SDK to 24Harsh Shandilya2025-10-203-29/+7
| | | | | | | | The difference between the usage of 21 and 24 is not big enough to hold on to all the baggage from API 21 Signed-off-by: Harsh Shandilya <me@msfjarvis.dev> Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* TvMainActivity: link to cx file explorerJason A. Donenfeld2025-05-231-1/+4
| | | | | | | | | | | | | | | | | | | Google requires us to link to a specific file manager. We can't just open the app store. They keep rejecting the app otherwise. Annoying. Also, due to Android TV bugs, we also can't link to a prefilled search query of "file manager". Instead we must link to an actual file manager. Unfortunately, none of the open source file manager apps both support the TV interface and can act as a file chooser. So instead we pick what looks like the only decent one that doesn't have any in app purchases. Ugh. I don't like doing this. It's not an endorsement. I don't know much about who makes these. But what choice do we have? So doing this for now. Maybe we'll find some better solution or apply to be a file manager ourselves in the future. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* MainActivity: don't reuse id names across layoutsJason A. Donenfeld2025-05-191-2/+2
| | | | | | | This causes problems on foldables. Reported-by: Josh Dague <josh3736@gmail.com> Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* global: update copyright noticesJason A. Donenfeld2025-05-197-7/+7
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* TunnelCreatorActivity: use proper xml for layoutJason A. Donenfeld2025-05-191-15/+2
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* TunnelToggleActivity: suppress errors from prepare() in lockdown modeJason A. Donenfeld2025-05-161-4/+8
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Exception java.lang.IllegalStateException: Unavailable in lockdown mode at android.os.Parcel.createExceptionOrNull (Parcel.java:3031) at android.os.Parcel.createException (Parcel.java:3007) at android.os.Parcel.readException (Parcel.java:2990) at android.os.Parcel.readException (Parcel.java:2932) at android.net.IVpnManager$Stub$Proxy.prepareVpn (IVpnManager.java:1064) at android.net.VpnService.prepare (VpnService.java:290) at com.wireguard.android.activity.TunnelToggleActivity$onCreate$1.invokeSuspend (TunnelToggleActivity.kt:44) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:8) at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith (DispatchedContinuation.kt:155) at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable (Cancellable.kt:12) at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default (Cancellable.kt:12) at kotlinx.coroutines.CoroutineStart.invoke (CoroutineStart.java:80) at kotlinx.coroutines.AbstractCoroutine.start (AbstractCoroutine.kt:80) at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch (BuildersKt__Builders_common.kt:52) at kotlinx.coroutines.BuildersKt.launch (Builders.kt:52) at kotlinx.coroutines.BuildersKt__Builders_commonKt.launch$default (BuildersKt__Builders_common.kt:52) at kotlinx.coroutines.BuildersKt.launch$default (Builders.kt:52) at com.wireguard.android.activity.TunnelToggleActivity.onCreate (TunnelToggleActivity.kt:14) at android.app.Activity.performCreate (Activity.java:8591) at android.app.Activity.performCreate (Activity.java:8570) at android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1384) at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:4150) at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:4325) at android.app.servertransaction.LaunchActivityItem.execute (LaunchActivityItem.java:101) at android.app.servertransaction.TransactionExecutor.executeCallbacks (TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2574) at android.os.Handler.dispatchMessage (Handler.java:106) at android.os.Looper.loopOnce (Looper.java:226) at android.os.Looper.loop (Looper.java:313) at android.app.ActivityThread.main (ActivityThread.java:8762) at java.lang.reflect.Method.invoke at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:604) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1067) Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: set fitsSystemWindows=true for EdgeToEdge on API 35+Jason A. Donenfeld2025-05-162-13/+9
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* TvMainActivity: use OpenDocument instead of GetContentJason A. Donenfeld2025-05-161-3/+3
| | | | | | This should make it possible to open files on Android TV 14. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* LogViewerActivity: cast array to Any type instead of using union inferenceJason A. Donenfeld2025-05-081-1/+1
| | | | | | | | | | Type argument for reified type parameter 'T' was inferred to the intersection of ['Comparable<*>' & 'Serializable']. Reification of an intersection type results in the common supertype being used. This may lead to subtle issues and an explicit type argument is encouraged. This will become an error in a future release. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* SettingsActivity: manually apply insets for api 35Jason A. Donenfeld2025-05-081-0/+24
| | | | | | | | The forced EdgeToEdge changes don't work well with PreferenceFragmentCompat, because it's basically abandoned by androidx, so apply the insets manually ourselves. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* SettingsActivity: remove unused importsJason A. Donenfeld2025-05-081-2/+0
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* gradle: bump many depsJason A. Donenfeld2023-10-131-1/+1
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: add shortcut for adding quick settings tileJason A. Donenfeld2023-05-101-1/+10
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: reformat all codeJason A. Donenfeld2023-05-056-36/+52
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: collect logs from IO thread onlyJason A. Donenfeld2023-05-051-11/+15
| | | | | | | | | | | | | | | | | | | | | Otherwise the pop() from the producer might causes an OOB read in the consumer: Exception java.lang.ArrayIndexOutOfBoundsException: at androidx.collection.CircularArray.get (CircularArray.java) at com.wireguard.android.activity.LogViewerActivity.rawLogBytes (LogViewerActivity.java) at com.wireguard.android.activity.LogViewerActivity.onCreate$lambda$3 (LogViewerActivity.java:133) at android.view.View.performClick (View.java:6935) at android.view.View$PerformClick.run (View.java:26214) at android.os.Handler.handleCallback (Handler.java:790) at android.os.Handler.dispatchMessage (Handler.java:99) at android.os.Looper.loop (Looper.java:164) at android.app.ActivityThread.main (ActivityThread.java:7000) at java.lang.reflect.Method.invoke at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:441) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1408) Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: LogViewerActivity: cleanup ret valJason A. Donenfeld2023-04-131-2/+1
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: further reduce log viewer memory usageJason A. Donenfeld2023-04-121-2/+1
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: allow vetoing tunnel selection changesJason A. Donenfeld2023-04-053-6/+15
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | These changes can happen at the wrong time, so we need to be able to walk them back. Fixes: Exception java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at androidx.fragment.app.FragmentManager.checkStateLoss (FragmentManager.java:1610) at androidx.fragment.app.FragmentManager.enqueueAction (FragmentManager.java:1650) at androidx.fragment.app.BackStackRecord.commitInternal (BackStackRecord.java:341) at androidx.fragment.app.BackStackRecord.commit (BackStackRecord.java:306) at androidx.fragment.app.FragmentManagerKt.commit$default (FragmentManager.kt:35) at com.wireguard.android.activity.MainActivity.onSelectedTunnelChanged (MainActivity.kt:113) at com.wireguard.android.activity.BaseActivity.setSelectedTunnel (BaseActivity.kt:25) at com.wireguard.android.activity.BaseActivity$onCreate$1.invokeSuspend (BaseActivity.kt:44) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33) at kotlinx.coroutines.internal.DispatchedContinuation.resumeUndispatchedWith (DispatchedContinuation.java:256) at kotlinx.coroutines.DispatchedTaskKt.resume (DispatchedTask.kt:177) at kotlinx.coroutines.CancellableContinuationImpl.parentCancelled$kotlinx_coroutines_core (CancellableContinuationImpl.kt:190) at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined (DispatchedTask.kt:190) at kotlinx.coroutines.DispatchedTaskKt.dispatch (DispatchedTask.kt:161) at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume (CancellableContinuationImpl.kt:397) at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl (CancellableContinuationImpl.kt:431) at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default (CancellableContinuationImpl.kt:420) at kotlinx.coroutines.CancellableContinuationImpl.resumeWith (CancellableContinuationImpl.kt:328) at kotlinx.coroutines.ResumeAwaitOnCompletion.invoke (JobSupport.kt:1413) at kotlinx.coroutines.JobSupport.notifyHandlers (JobSupport.kt:368) at kotlinx.coroutines.JobSupport.notifyCompletion (JobSupport.kt:362) at kotlinx.coroutines.JobSupport.completeStateFinalization (JobSupport.kt:323) at kotlinx.coroutines.JobSupport.finalizeFinishingState (JobSupport.kt:240) at kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath (JobSupport.kt:906) at kotlinx.coroutines.JobSupport.tryMakeCompleting (JobSupport.kt:863) at kotlinx.coroutines.JobSupport.makeCompleting$kotlinx_coroutines_core (JobSupport.kt:806) at kotlinx.coroutines.CompletableDeferredImpl.complete (CompletableDeferredImpl.java:92) at com.wireguard.android.model.TunnelManager$onTunnelsLoaded$1.invokeSuspend (TunnelManager.kt:120) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:106) at android.os.Handler.handleCallback (Handler.java:938) at android.os.Handler.dispatchMessage (Handler.java:99) at android.os.Looper.loop (Looper.java:237) at android.app.ActivityThread.main (ActivityThread.java:8163) at java.lang.reflect.Method.invoke at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:656) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:967) Exception java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at androidx.fragment.app.FragmentManager.checkStateLoss (FragmentManager.java:1610) at androidx.fragment.app.FragmentManager.enqueueAction (FragmentManager.java:1650) at androidx.fragment.app.BackStackRecord.commitInternal (BackStackRecord.java:341) at androidx.fragment.app.BackStackRecord.commit (BackStackRecord.java:306) at androidx.fragment.app.FragmentManagerKt.commit$default (FragmentManager.kt:35) at com.wireguard.android.activity.MainActivity.onSelectedTunnelChanged (MainActivity.kt:113) at com.wireguard.android.activity.BaseActivity.setSelectedTunnel (BaseActivity.kt:25) at com.wireguard.android.activity.BaseActivity$onCreate$1.invokeSuspend (BaseActivity.kt:44) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33) at kotlinx.coroutines.internal.DispatchedContinuation.resumeUndispatchedWith (DispatchedContinuation.java:256) at kotlinx.coroutines.DispatchedTaskKt.resume (DispatchedTask.kt:177) at kotlinx.coroutines.CancellableContinuationImpl.parentCancelled$kotlinx_coroutines_core (CancellableContinuationImpl.kt:190) at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined (DispatchedTask.kt:190) at kotlinx.coroutines.DispatchedTaskKt.dispatch (DispatchedTask.kt:161) at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume (CancellableContinuationImpl.kt:397) at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl (CancellableContinuationImpl.kt:431) at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default (CancellableContinuationImpl.kt:420) at kotlinx.coroutines.CancellableContinuationImpl.resumeWith (CancellableContinuationImpl.kt:328) at kotlinx.coroutines.ResumeAwaitOnCompletion.invoke (JobSupport.kt:1413) at kotlinx.coroutines.JobSupport.notifyHandlers (JobSupport.kt:368) at kotlinx.coroutines.JobSupport.notifyCompletion (JobSupport.kt:362) at kotlinx.coroutines.JobSupport.completeStateFinalization (JobSupport.kt:323) at kotlinx.coroutines.JobSupport.finalizeFinishingState (JobSupport.kt:240) at kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath (JobSupport.kt:906) at kotlinx.coroutines.JobSupport.tryMakeCompleting (JobSupport.kt:863) at kotlinx.coroutines.JobSupport.makeCompleting$kotlinx_coroutines_core (JobSupport.kt:806) at kotlinx.coroutines.CompletableDeferredImpl.complete (CompletableDeferredImpl.java:92) at com.wireguard.android.model.TunnelManager$onTunnelsLoaded$1.invokeSuspend (TunnelManager.kt:120) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:106) at android.os.Handler.handleCallback (Handler.java:938) at android.os.Handler.dispatchMessage (Handler.java:99) at android.os.Looper.loopOnce (Looper.java:210) at android.os.Looper.loop (Looper.java:299) at android.app.ActivityThread.main (ActivityThread.java:8302) at java.lang.reflect.Method.invoke at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:556) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1037) Exception java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState at androidx.fragment.app.FragmentManager.checkStateLoss (FragmentManager.java:1610) at androidx.fragment.app.FragmentManager.ensureExecReady (FragmentManager.java:1711) at androidx.fragment.app.FragmentManager.execPendingActions (FragmentManager.java:1754) at androidx.fragment.app.FragmentManager.popBackStackImmediate (FragmentManager.java:854) at androidx.fragment.app.FragmentManager.popBackStackImmediate (FragmentManager.java:778) at com.wireguard.android.activity.MainActivity.onSelectedTunnelChanged (MainActivity.kt:110) at com.wireguard.android.activity.BaseActivity.setSelectedTunnel (BaseActivity.kt:25) at com.wireguard.android.activity.BaseActivity$onCreate$1.invokeSuspend (BaseActivity.kt:44) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:106) at kotlinx.coroutines.EventLoop.processUnconfinedEvent (EventLoop.common.kt:69) at kotlinx.coroutines.DispatchedTaskKt.runUnconfinedEventLoop (DispatchedTask.kt:204) at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith (DispatchedContinuation.kt:282) at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable (Cancellable.kt:30) at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default (Cancellable.kt:25) at kotlinx.coroutines.CoroutineStart.invoke (CoroutineStart.java:110) at kotlinx.coroutines.AbstractCoroutine.start (AbstractCoroutine.kt:126) at kotlinx.coroutines.BuildersKt.launch$default (Builders.kt) at com.wireguard.android.model.TunnelManager.onTunnelsLoaded (TunnelManager.java:114) at com.wireguard.android.model.TunnelManager.access$onTunnelsLoaded (TunnelManager.java:40) at com.wireguard.android.model.TunnelManager$onCreate$1.invokeSuspend (TunnelManager.kt:104) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run (DispatchedTask.kt:106) at android.os.Handler.handleCallback (Handler.java:942) at android.os.Handler.dispatchMessage (Handler.java:99) at android.os.Looper.loopOnce (Looper.java:226) at android.os.Looper.loop (Looper.java:313) at android.app.ActivityThread.main (ActivityThread.java:8757) at java.lang.reflect.Method.invoke at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:571) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1067) Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: no need to manually recreate activities after night changeJason A. Donenfeld2023-04-054-39/+8
| | | | | | | Since version 1.1.0, setDefaultNightMode now takes care of that. Also, set the initial mode in a blocking fashion to prevent flashing white. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: force dark theme in TV modeJason A. Donenfeld2023-04-041-2/+13
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: redirect to play store if no file manager on tvJason A. Donenfeld2023-04-041-1/+7
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: detect tv framework stubs when getting contentJason A. Donenfeld2023-04-041-1/+24
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: do not OOM when leaving log window open for a whileJason A. Donenfeld2023-04-041-14/+39
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: do not return to detail page when restoring stateJason A. Donenfeld2023-04-041-4/+14
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: add log items in buffered batchesJason A. Donenfeld2023-03-281-23/+34
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: color verbose logs the same as debug logsJason A. Donenfeld2023-03-281-1/+1
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: don't call getColor on already resolved attributeJason A. Donenfeld2023-03-281-1/+1
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: introduce a fresh set of Material3 themes and migrate to themHarsh Shandilya2023-03-271-1/+2
| | | | Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
* ui: migrate to OnBackPressedDispatcherJason A. Donenfeld2023-03-242-10/+27
| | | | | | | This is compatible with Android 13's prediction-based back gesture animation. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: set selected tunnel after creating fragmentsJason A. Donenfeld2023-03-241-8/+3
| | | | | | | | | | I'm not sure why that comment (Samuel's) was there saying it was necessary. Given it's been async for a long while, this wasn't guaranteed anyway. So let's get rid of it and see what happens. Screen rotation seems fine thus far. Cc: Samuel Holland <samuel@sholland.org> Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: switch from using launchWhenCreated to using repeatOnLifecycleJason A. Donenfeld2023-03-241-1/+8
| | | | | | Apparently the former is deprecated. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* global: bump copyright dateJason A. Donenfeld2023-03-218-8/+8
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* global: bump copyright yearJason A. Donenfeld2021-10-298-8/+8
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* tunnel: remove kernel module downloaderJason A. Donenfeld2021-10-291-16/+4
| | | | | | | | Nathan Chance dropped the ball repeatedly and never maintained this in a consistent way that anybody could use. With Android 12 out now, just drop it all together. A bummer, but I don't see much of a choice. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* gradle: upgrade AndroidX dependenciesHarsh Shandilya2021-10-281-2/+2
| | | | Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
* build: upgrade AndroidX dependenciesHarsh Shandilya2021-05-191-1/+1
| | | | Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
* ui: remove hack for broken kotlin compilerJason A. Donenfeld2021-05-061-5/+1
| | | | | | Seems like things are finally fixed. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* DownloadsFileSaver: initialize callback in constructor, not on the flyJason A. Donenfeld2020-10-021-1/+3
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: use commit extension from fragment-ktxHarsh Shandilya2020-09-303-16/+19
| | | | Signed-off-by: Harsh Shandilya <me@msfjarvis.dev>
* tv: hack gridlayoutmanager to fill columns before row if we're not scrollingJason A. Donenfeld2020-09-271-0/+46
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | If we're horizontally scrolling, it makes sense to fill rows before columns. But if it all fits in one page and we don't need to scroll horizontally, it looks ridiculous. So, in this case, rearrange the tiles so that it appears to fill columns before rows. But we don't want things suddenly jumping around, so actually, keep the same ordering as rows-before-columns, but add invisible spaces after certain items, so that the fill area makes it look as though it's columns-before-rows. This winds up being much more visually pleasing. We do this by figuring out this kind of transformation: If we convert this matrix: 0 3 6 1 4 _ 2 5 _ To this one: 0 2 4 6 1 3 5 _ _ _ _ _ For a given index, how many spaces are under it? This changes depending on how many total are in a grid. Going from 3x3 to 4x3, for example, we have: count == 12, index = count == 11, index = 10 count == 10, index = 7,9 count == 9, index = 4,6,8 count == 8, index = 1,3,5,7 count == 7, index = 1,3,5,6! count == 6, index = 1,3,4!,5! count == 5, index = 1,2!,3!,4! count == 4, index = 0!,1!,2!,3! count == 3, index = 0!,1!,2! count == 2, index = 0!,1! count == 1, index = 0! count == 0, index = The '!' means two blanks below, no '!' means one blank below, and no mention means no blanks below. This commit adds code to compute such a table on the fly. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: print proper exception trace from log viewerJason A. Donenfeld2020-09-261-1/+3
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* ui: request intent permissions from hidden activityJason A. Donenfeld2020-09-261-2/+19
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* tv: handle going up directories betterJason A. Donenfeld2020-09-241-25/+40
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* tv: show volume descriptions for file pickerJason A. Donenfeld2020-09-241-4/+4
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* tv: use system picker for API 29+Jason A. Donenfeld2020-09-231-10/+24
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* tv: use our own file pickerJason A. Donenfeld2020-09-231-19/+166
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* tv: escape deletion view with back buttonJason A. Donenfeld2020-09-231-0/+7
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* tv: select first item after toggling deletion modeJason A. Donenfeld2020-09-221-0/+3
| | | | Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
* tv: hook up isFocused as observable propertyJason A. Donenfeld2020-09-221-0/+4
| | | | | | | | This is kind of ridiculous, since the items own state should clearly be queryable, but it doesn't appear to be the case here, so just shuffle it around into kotlin and back. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>