* Show subscription usage info box on subscription groups
* Float the info box over the server list and make it translucent
---------
Co-authored-by: rasa68 <noreply@example.com>
* Redesign subscription group tabs as a floating pill bar with count badges
* Group tabs: dark-mode bar tone, equal-width tabs when they fit, room around selected tab
---------
Co-authored-by: rasa68 <noreply@example.com>
Updating subscriptions from the subscription screen without auto-test replaced
profile GUIDs but did not request a main list refresh. The main screen kept stale
rows, and editing one opened an empty editor because its payloads had been removed.
Co-authored-by: fanymagnet <>
Gradle 9.8.1 fixes three high-severity advisories that affect Gradle
9.0.0 to 9.8.0 (GHSA-mvvg-497x-hmj8, GHSA-xwqc-3h47-hg64,
GHSA-j5m7-59rp-24f5). Kotlin 2.4.21 is a bug-fix release for 2.4.20;
the app's Kotlin compiler output is byte-identical to the 2.4.20 build.
* Azerbaijani localization support
Azerbaijani localization support
* Add Azerbaijani language support in arrays.xml
* Fix
---------
Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
In root mode, DNS (port 53) is marked into the tun and reaches the
core's SOCKS inbound as a UDP relay to the original resolver address
(e.g. 192.168.1.1:53). The core's routing rule
{inboundTag=socks, port=53, outboundTag=dns-out} should intercept it
and resolve via the configured remote DNS (e.g. Cloudflare DoH).
However, when the system DNS server is a private/LAN IP and the user
has a geoip:private → direct routing rule, that rule matches the
destination IP (192.168.1.1) and sends the DNS query straight to the
LAN resolver (ISP DNS) — bypassing dns-out entirely. This causes:
- DNS poisoning for domains the ISP blocks/poisons
- CDN mis-resolution (wrong POP)
- Intermittent 'some sites work, some don't' behavior
Fix: add iptables nat OUTPUT DNAT to rewrite DNS queries destined for
private/LAN CIDRs to the configured remote DNS server (or 1.1.1.1
fallback), mirroring the existing tethered-client DNAT in
buildLanShareSetup. This prevents geoip:private from matching, so
dns-out can resolve via the proxy.
Verified on OnePlus 13R (OxygenOS 16, WildKernel v2.2.0-r4):
- Before: youtube.com → 185.45.5.35 (fake ISP IP), timeout
- After DNAT to 1.1.1.1: youtube.com → 142.251.45.14 (real), 301
- tcpdump confirms response source changes from 192.168.1.1 to
correct resolution through the proxy
Give least-ping and least-load groups their own first member as fallback so missing or failed probes cannot send a matched routing rule through the main profile. Keep least-load on its burst observatory and preserve random and round-robin fallback behavior. Cover group isolation and observatory selection with regression tests.
The test gave RootProcessRunner.run a 250 ms deadline. When sh takes
longer than that to start and finish, the runner hits the deadline,
destroys it and returns -1, and the test fails with
"expected: <0> but was: <-1>". With the sh from Git for Windows this
happened in 6 of 30 runs.
The test's real guard is the 1500 ms elapsed bound against the
background "sleep 5". A 3000 ms deadline leaves that bound alone to
decide the result, so a runner that keeps a finished command open until
its deadline now fails the test as well; with 250 ms it passed.
* Azerbaijani localization
Azerbaijani language support added
* Delete V2rayNG/app/src/main/res/values-az
* Azerbaijani localization support
Azerbaijani translations are now available
* Fix
* Add Azerbaijani language selection
* Update Azerbaijani strings in strings.xml
* Fix string formatting in Azerbaijani resource file
* feat(wireguard): add features from the new core version
- The `local` value is no longer allowed for `remoteDNS`. If this value is passed in the profile, the core will panic while calling `netip.MustParseAddr`.
- Add the `targetStrategy` field to `OutboundBean`.
- The `domainStrategy` field has been removed from `OutSettingsBean`. Any value passed for `domainStrategy` is now ignored.
* update locales
- ru,en - handwritten.
- other - AI-generated.
* Keep fractional numbers when saving JSON array subscriptions
toJsonPretty wrote every Double through toInt(), so 0.4 became 0 and
values above Int.MAX_VALUE were clamped. Write whole numbers as Long
and leave other values as Double.
Fixes#6291
* Add a JsonUtil test for numbers in config arrays
* Update JsonUtil.kt
---------
Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
r30 is the current NDK LTS; r29 is no longer supported. The NDK path
is part of the libhevtun cache key, so the cached libraries are rebuilt
with the new toolchain.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
64cc609 (2.16.0+24) -> d9dca26 (tag 2.18.0), 16 commits. Changes on
the paths the app uses:
- HevSocks5SessionUDP: skip datagrams without an address or with an
empty payload, and set the IPv4 address type for a fixed UDP peer
(c02e3fd)
- lwIP 2a11c14 -> e22c9d2: fix PRETEND handling of IPv6 extension
headers and fragments, validate UDP PRETEND packets before accepting
new sessions
- HevSocks5Tunnel: assert on the stop event write result (c362232)
The rest is tunnel code for Windows, macOS, FreeBSD and NetBSD, an
optional Windows-only tunnel.guid key, mapped DNS, the upstream AAR
build, docs and version bumps. JNI entry points, Android.mk/build.mk
and the config keys the app writes are unchanged.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
JUnit 4 is in maintenance mode, and 4.13.2 (February 2021) is its last
release. Move the unit tests to JUnit Jupiter 6.1.3: the JUnit BOM,
junit-jupiter and the Platform launcher replace junit:junit, and the
unit test tasks run on the JUnit Platform. The test sources change only
mechanically: imports, lifecycle annotations, TemporaryFolder to
@TempDir, and the message argument moved last in three assertions.
Jupiter skips an invalid @Test method, such as one that returns a
value, with only a warning, where JUnit 4 failed the run. Treat
discovery warnings as failures to keep that behavior.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
AGP 9.4.1 is in the AGP range Gradle 9.8.0 was tested with (9.0 to
9.5.0-alpha02). AGP 9.4 requires at least Gradle 9.6.0.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
mockito-kotlin shared the mockitoMockitoInline version key with
mockito-inline, which was discontinued after 5.2.0 because Mockito 5
made the inline mock maker the default in mockito-core. The shared key
kept mockito-kotlin on 5.2.0.
- org.mockito:mockito-inline 5.2.0 -> org.mockito:mockito-core 5.24.0
- org.mockito.kotlin:mockito-kotlin 5.2.0 -> 6.4.0, on its own key
On the test classpath mockito-core moves from 5.7.0 to 5.24.0 and
Byte Buddy from 1.14.9 to 1.17.7, so the unit tests also run on JDK 25.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Increase toast background opacity from 70% to 85% for all theme variants in Theme.kt. This improves readability and keeps toast notifications visually consistent without changing their content or behavior.
Pass the optional failure message from the service layer through `MainServiceEvent` and show it in `MainViewModel` when available. This preserves the existing generic error toast as a fallback while surfacing more specific startup failures to the user.
Refactor toast helpers to route through a shared dispatcher that first tries the app snackbar manager and falls back to Android Toasts on the main thread. This adds a `long` duration option across normal/success/error/info toast APIs and avoids posting to a new Handler for every message by reusing a single main-thread handler.
Move several save-time validation failures from toast messages to field error states in the server and subscription editors. Password validation now marks the relevant server form field, and subscription editing highlights invalid URLs and update intervals before save instead of relying on transient toasts.
Replace toast-only required-field validation with Compose text field error states across routing, server, subscription, and user asset editors. The shared form field now supports `isError` and supporting text so save actions can block invalid input and highlight missing remarks, address, port, and URL values directly in the form.
Replace hard-coded channel labels with dedicated resource names for the main service, subscription updates, and connection checks in all nine supported locales. Resolve names through AppLocaleManager for service and application contexts.
Reuse the existing channel when refreshing its localized name, preserving channel IDs and user-controlled importance, sound, vibration, and visibility. Apply initial defaults only to newly created channels and remove the obsolete hard-coded service-channel name.
Extract channel localization from local/talkback-all-10 without adding its transient-message channel, snackbar fallback, or accessibility announcement infrastructure.
Coil 2 had its last release in July 2024 (2.7.0); Coil 3 is the
maintained line and uses new coordinates, io.coil-kt.coil3, with the
package prefix coil3.
AppIconFetcher now returns ImageFetchResult with drawable.asImage(),
since Coil 3 models images through its own Image type. Fetcher,
Fetcher.Factory, Options, DataSource and the ImageRequest builder chain
keep the same shapes, so the rest is imports.
No network artifact is required: AppListItem is the only AsyncImage call
site and both callers pass icon = null, so every model is the local
appicon: request served by AppIconFetcher.
Coil 3.6.3 declares minSdkVersion 23 and ships no native code, so
neither minSdk 24 nor the four app ABIs are affected.
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Expose the `remoteDNS` option (added in Xray-core 26.9.9) in the wireguard.
- Accepts a comma-separated list of IPv4/IPv6 addresses
- Accepts "local" as a single value to resolve via the device's
local DNS instead of routing lookups through the tunnel
- Falls back to WIREGUARD_LOCAL_REMOTE_DNS when the field is empty
(Xray-core upstream value)
Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>