diff --git a/android-plugin/app/src/main/java/top/mobilegl/plugin/NavBar.java b/android-plugin/app/src/main/java/top/mobilegl/plugin/NavBar.java new file mode 100644 index 00000000..cd65f3d6 --- /dev/null +++ b/android-plugin/app/src/main/java/top/mobilegl/plugin/NavBar.java @@ -0,0 +1,79 @@ +package top.mobilegl.plugin; + +import android.app.Activity; +import android.content.Intent; +import android.graphics.Typeface; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +/** + * Bottom navigation bar switching between the dev screens (POST and the render + * server control). The bar only appears in flavors that ship both screens: the + * plugin flavor has no ServerControlActivity, so wrap() returns the content + * unchanged there. Tabs switch by reordering the other activity to the front, + * so each screen keeps its state while the other is open. + */ +public final class NavBar { + public static final int TAB_POST = 0; + public static final int TAB_SERVER = 1; + + private static final int COLOR_BAR = 0xFF1E1E1E; + private static final int COLOR_TAB_ACTIVE = 0xFFEEEEEE; + private static final int COLOR_TAB_INACTIVE = 0xFF9E9E9E; + private static final int BAR_HEIGHT_DP = 56; + + private static final String SERVER_ACTIVITY = "top.mobilegl.plugin.ServerControlActivity"; + + private NavBar() {} + + /** Returns content wrapped with the bottom bar, or content unchanged in plugin builds. */ + public static View wrap(Activity activity, View content, int currentTab) { + final Class serverClass; + try { + serverClass = Class.forName(SERVER_ACTIVITY); + } catch (ClassNotFoundException notInThisFlavor) { + return content; + } + + LinearLayout root = new LinearLayout(activity); + root.setOrientation(LinearLayout.VERTICAL); + root.addView(content, new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, 0, 1f)); + + LinearLayout bar = new LinearLayout(activity); + bar.setOrientation(LinearLayout.HORIZONTAL); + bar.setBackgroundColor(COLOR_BAR); + addTab(activity, bar, "POST", PostActivity.class, currentTab == TAB_POST); + addTab(activity, bar, "Render Server", serverClass, currentTab == TAB_SERVER); + root.addView(bar, new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, dp(activity, BAR_HEIGHT_DP))); + return root; + } + + private static void addTab(final Activity activity, LinearLayout bar, String label, + final Class target, boolean active) { + TextView tab = new TextView(activity); + tab.setText(label); + tab.setGravity(Gravity.CENTER); + tab.setTextSize(TypedValue.COMPLEX_UNIT_SP, 13); + tab.setTypeface(Typeface.MONOSPACE, active ? Typeface.BOLD : Typeface.NORMAL); + tab.setTextColor(active ? COLOR_TAB_ACTIVE : COLOR_TAB_INACTIVE); + if (!active) { + tab.setOnClickListener(v -> { + activity.startActivity( + new Intent(activity, target).addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)); + // Tab switch, not navigation: suppress the slide-in activity transition. + activity.overridePendingTransition(0, 0); + }); + } + bar.addView(tab, new LinearLayout.LayoutParams( + 0, LinearLayout.LayoutParams.MATCH_PARENT, 1f)); + } + + private static int dp(Activity activity, int value) { + return Math.round(value * activity.getResources().getDisplayMetrics().density); + } +} diff --git a/android-plugin/app/src/main/java/top/mobilegl/plugin/PostActivity.java b/android-plugin/app/src/main/java/top/mobilegl/plugin/PostActivity.java index c6910500..55a13898 100644 --- a/android-plugin/app/src/main/java/top/mobilegl/plugin/PostActivity.java +++ b/android-plugin/app/src/main/java/top/mobilegl/plugin/PostActivity.java @@ -15,6 +15,7 @@ import android.view.View; import android.widget.Button; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; +import android.widget.ProgressBar; import android.widget.ScrollView; import android.widget.TextView; @@ -69,6 +70,7 @@ public final class PostActivity extends Activity { private LinearLayout contentLayout; private TextView statusView; + private ProgressBar progressBar; /** * Per-backend bench UI state. Unlike the POST itself (single-flight, latched), @@ -93,11 +95,20 @@ public final class PostActivity extends Activity { scrollView.setBackgroundColor(COLOR_BACKGROUND); scrollView.setFillViewport(true); scrollView.addView(contentLayout); - setContentView(scrollView); + setContentView(NavBar.wrap(this, scrollView, NavBar.TAB_POST)); addText("MobileGL Driver POST", 20, COLOR_TEXT, true, 0); statusView = addText("Running self-test...", 14, COLOR_INFO, false, dp(8)); + progressBar = new ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal); + progressBar.setIndeterminate(true); + LinearLayout.LayoutParams progressParams = new LinearLayout.LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ); + progressParams.topMargin = dp(8); + contentLayout.addView(progressBar, progressParams); + boolean startWorker = false; boolean renderNow = false; synchronized (POST_LOCK) { @@ -298,6 +309,7 @@ public final class PostActivity extends Activity { } private void renderCachedResult() { + progressBar.setVisibility(View.GONE); String json; Throwable failure; synchronized (POST_LOCK) { diff --git a/android-plugin/app/src/trace/AndroidManifest.xml b/android-plugin/app/src/trace/AndroidManifest.xml index 6719a6cf..ccfe5cb3 100644 --- a/android-plugin/app/src/trace/AndroidManifest.xml +++ b/android-plugin/app/src/trace/AndroidManifest.xml @@ -8,17 +8,16 @@ - + - - - - - + android:launchMode="singleTop" + android:theme="@style/Theme.MobileGLPlugin" /> - diff --git a/android-plugin/app/src/trace/java/top/mobilegl/plugin/ServerControlActivity.java b/android-plugin/app/src/trace/java/top/mobilegl/plugin/ServerControlActivity.java index 3094a999..bc3fd8cb 100644 --- a/android-plugin/app/src/trace/java/top/mobilegl/plugin/ServerControlActivity.java +++ b/android-plugin/app/src/trace/java/top/mobilegl/plugin/ServerControlActivity.java @@ -72,7 +72,7 @@ public final class ServerControlActivity extends Activity { scrollView.setBackgroundColor(COLOR_BACKGROUND); scrollView.setFillViewport(true); scrollView.addView(contentLayout); - setContentView(scrollView); + setContentView(NavBar.wrap(this, scrollView, NavBar.TAB_SERVER)); contentLayout.addView(makeText("MobileGL Render Server", 20, COLOR_TEXT, true)); @@ -142,11 +142,23 @@ public final class ServerControlActivity extends Activity { setStatus("Stopped.", COLOR_INFO); } - // Intent extras prefill the form, so adb can drive the screen without the IME: - // --es listen @name --es token T --es env "K=V;K" --ez onscreen true --ez start true - // (--ez stop true instead stops). This mirrors how every other entry point in this - // APK is already driven by extras. - Intent intent = getIntent(); + handleIntent(getIntent()); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + setIntent(intent); + handleIntent(intent); + } + + /** + * Intent extras prefill the form, so adb can drive the screen without the IME: + * --es listen @name --es token T --es env "K=V;K" --ez onscreen true --ez start true + * (--ez stop true instead stops). This mirrors how every other entry point in this + * APK is already driven by extras. + */ + private void handleIntent(Intent intent) { if (intent != null) { String prefillListen = intent.getStringExtra("listen"); if (prefillListen != null) listenField.setText(prefillListen);