diff --git a/README.md b/README.md index e80275c..c947630 100644 --- a/README.md +++ b/README.md @@ -194,34 +194,29 @@ FreeProxy project. See `THIRD_PARTY_NOTICES.md` and - ARM64 Android DragonTCP core build: PASS. - APK resource/DEX/native packaging: PASS. - APK signature verification (v3): PASS. -- Supplied APK uses the same signing certificate as the previous Lite/v7 APK and has versionCode 9, so it can be installed as an in-place update. +- Supplied APK uses the same signing certificate as the previous Lite/v7 APK and has versionCode 10, so it can be installed as an in-place update. A physical Android device is still required to validate the final VpnService path against a real mobile network. -## v9 dark UI update +## v10 split UI update -This release changes only the Android UI/service state handling. The embedded -DragonTCP Go transport core is byte-for-byte identical to the previous Lite -release. +This release changes only the Android UI. The embedded DragonTCP transport +core and the Lite server are unchanged from the working Lite release. UI changes: -- permanent dark theme independent of the Android system theme -- fixed header with CONNECTED / CONNECTING / STOPPING / DISCONNECTED state -- connection and transport settings are grouped into compact dark cards -- the settings area and Live Log have independent scrolling regions -- Live Log appends new lines in-place and no longer calls - `fullScroll(FOCUS_DOWN)`, so log updates do not move the settings screen -- automatic log following happens only while the user is already near the - bottom; scrolling upward pauses auto-follow -- CONNECT is immediately disabled/greyed while connecting or connected -- STOP is enabled only while DragonTCP is active -- the service independently rejects duplicate CONNECT commands, so a double - tap cannot restart a working tunnel -- connection state is broadcast to the Activity, so buttons and the status - chip update without rebuilding the screen +- Live Log is a true sibling pane below the settings pane; it is never drawn + over the settings. +- The settings pane is explicitly clipped to its own region. +- The settings scrollbar is hidden, so no scrollbar can appear behind the log. +- Live Log keeps its own internal scroll area and receives appended lines + without rebuilding the Activity. +- A visible divider separates settings from Live Log. +- CONNECT remains disabled while connecting/connected and STOP remains state-aware. +- The visible XOR wording was removed from the Android UI. Transport behavior + itself is unchanged. +- The Activity explicitly uses `adjustResize` when the keyboard opens. -The supplied APK is versionCode 9 / versionName `9.0-lite-ui` and is signed -with the same DragonTCP VPN development certificate used by the preceding -Lite APK, allowing an in-place update. +The APK is versionCode 10 / versionName `10.0-lite-split-ui` and is signed with +the same certificate as the previous Lite UI build. diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 0f5a458..1bb5d5f 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="10" + android:versionName="10.0-lite-split-ui"> @@ -19,7 +19,8 @@ + android:screenOrientation="portrait" + android:windowSoftInputMode="adjustResize"> diff --git a/android/src/com/dragontcp/client/MainActivity.java b/android/src/com/dragontcp/client/MainActivity.java index 737f32d..5c8d1bb 100644 --- a/android/src/com/dragontcp/client/MainActivity.java +++ b/android/src/com/dragontcp/client/MainActivity.java @@ -19,6 +19,7 @@ import android.view.ViewGroup; import android.view.Window; import android.widget.Button; import android.widget.EditText; +import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; @@ -121,8 +122,10 @@ public class MainActivity extends Activity { root.setOrientation(LinearLayout.VERTICAL); root.setBackgroundColor(BG); root.setPadding(dp(16), dp(14), dp(16), dp(12)); + root.setClipChildren(true); + root.setClipToPadding(true); - // Fixed header: never moves when logs are appended. + // Header is outside both scrolling regions. LinearLayout header = new LinearLayout(this); header.setOrientation(LinearLayout.HORIZONTAL); header.setGravity(Gravity.CENTER_VERTICAL); @@ -131,7 +134,7 @@ public class MainActivity extends Activity { LinearLayout heading = new LinearLayout(this); heading.setOrientation(LinearLayout.VERTICAL); TextView title = text("DragonTCP Lite", 25, TEXT, true); - TextView subtitle = text("Adaptive XOR tunnel over TCP/53", 12, MUTED, false); + TextView subtitle = text("Adaptive tunnel over TCP/53", 12, MUTED, false); subtitle.setPadding(0, dp(2), 0, 0); heading.addView(title); heading.addView(subtitle); @@ -144,11 +147,30 @@ public class MainActivity extends Activity { header.addView(statusChip); root.addView(header); - // Only the settings area scrolls. The log panel below has its own independent scroll. + // The remaining screen is an explicit vertical split. The settings pane and + // Live Log pane are siblings; neither is drawn over the other. + LinearLayout split = new LinearLayout(this); + split.setOrientation(LinearLayout.VERTICAL); + split.setClipChildren(true); + split.setClipToPadding(true); + root.addView(split, new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + 0, + 1f + )); + + FrameLayout settingsPane = new FrameLayout(this); + settingsPane.setBackgroundColor(BG); + settingsPane.setClipChildren(true); + settingsPane.setClipToPadding(true); + ScrollView settingsScroll = new ScrollView(this); settingsScroll.setFillViewport(false); - settingsScroll.setClipToPadding(false); + settingsScroll.setClipToPadding(true); + settingsScroll.setClipChildren(true); settingsScroll.setOverScrollMode(View.OVER_SCROLL_IF_CONTENT_SCROLLS); + // Avoid a scrollbar visually continuing into the separate log pane. + settingsScroll.setVerticalScrollBarEnabled(false); LinearLayout settings = new LinearLayout(this); settings.setOrientation(LinearLayout.VERTICAL); @@ -157,6 +179,10 @@ public class MainActivity extends Activity { ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT )); + settingsPane.addView(settingsScroll, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + )); LinearLayout connectionCard = card("CONNECTION"); server = addField(connectionCard, "Server", "Server IP or hostname", "", false, false); @@ -177,7 +203,7 @@ public class MainActivity extends Activity { timeout = addFieldToRow(timing, "Timeout (s)", "2", "2", true, false, 0.42f); transportCard.addView(timing); - TextView fixed = text("1 poller • Start = Max chunk • XOR 0xAD always on", 11, MUTED, false); + TextView fixed = text("1 poller • Start = Max chunk", 11, MUTED, false); fixed.setPadding(dp(2), dp(6), dp(2), dp(2)); transportCard.addView(fixed); settings.addView(transportCard, cardParams()); @@ -202,15 +228,28 @@ public class MainActivity extends Activity { note.setPadding(dp(4), dp(2), dp(4), dp(6)); settings.addView(note); - root.addView(settingsScroll, new LinearLayout.LayoutParams( + // Give settings the upper portion of the available body. This FrameLayout + // clips its ScrollView, making it impossible for settings to draw behind logs. + split.addView(settingsPane, new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, 0, - 1f + 0.58f )); - // Fixed log panel. New lines can never change the position/size of the settings area. + View divider = new View(this); + divider.setBackgroundColor(BORDER); + LinearLayout.LayoutParams dividerParams = new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + dp(1) + ); + dividerParams.setMargins(dp(6), dp(7), dp(6), dp(7)); + split.addView(divider, dividerParams); + + // Live Log is a true sibling below settings, not an overlay. LinearLayout logPanel = new LinearLayout(this); logPanel.setOrientation(LinearLayout.VERTICAL); + logPanel.setClipChildren(true); + logPanel.setClipToPadding(true); logPanel.setBackground(roundRect(CARD, 16, BORDER, 1)); logPanel.setPadding(dp(10), dp(8), dp(10), dp(10)); @@ -225,6 +264,8 @@ public class MainActivity extends Activity { logScroll = new ScrollView(this); logScroll.setFillViewport(true); logScroll.setFocusable(false); + logScroll.setClipChildren(true); + logScroll.setClipToPadding(true); logScroll.setOverScrollMode(View.OVER_SCROLL_IF_CONTENT_SCROLLS); logScroll.setBackground(roundRect(LOG_BG, 11, Color.rgb(30, 38, 49), 1)); @@ -244,9 +285,11 @@ public class MainActivity extends Activity { 0, 1f )); - root.addView(logPanel, new LinearLayout.LayoutParams( + + split.addView(logPanel, new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, - dp(238) + 0, + 0.42f )); clear.setOnClickListener(v -> { diff --git a/core/dragontcp-client b/core/dragontcp-client new file mode 100644 index 0000000..035b7f7 Binary files /dev/null and b/core/dragontcp-client differ diff --git a/core/dragontcp-server b/core/dragontcp-server new file mode 100644 index 0000000..07bf58e Binary files /dev/null and b/core/dragontcp-server differ