1
0
mirror of git://f0xx.org/android_cast synced 2026-07-29 04:57:40 +03:00
This commit is contained in:
Anton Afanasyeu
2026-05-18 12:17:05 +02:00
parent 5bc968e419
commit 9b1afec032
12 changed files with 123 additions and 39 deletions

View File

@@ -18,6 +18,8 @@ public final class AppPreferences {
private static final String KEY_THEME = "theme_mode"; private static final String KEY_THEME = "theme_mode";
private static final String KEY_LOCALE = "locale_mode"; private static final String KEY_LOCALE = "locale_mode";
private static final String KEY_PLAY_INCOMING_AUDIO = "play_incoming_audio"; private static final String KEY_PLAY_INCOMING_AUDIO = "play_incoming_audio";
private static final String KEY_DEV_RECEIVER_DEBUG_OVERLAY = "dev_receiver_debug_overlay";
private static final String KEY_DEV_GRAB_SESSION_STATS = "dev_grab_session_stats";
private static final String PREFIX_SENDER = "sender_"; private static final String PREFIX_SENDER = "sender_";
private static final String PREFIX_RECEIVER = "receiver_"; private static final String PREFIX_RECEIVER = "receiver_";
@@ -92,6 +94,24 @@ public final class AppPreferences {
prefs(context).edit().putBoolean(KEY_PLAY_INCOMING_AUDIO, play).apply(); prefs(context).edit().putBoolean(KEY_PLAY_INCOMING_AUDIO, play).apply();
} }
/** Developer: metrics HTML overlay on receiver video (default on). */
public static boolean isShowReceiverDebugOverlay(Context context) {
return prefs(context).getBoolean(KEY_DEV_RECEIVER_DEBUG_OVERLAY, true);
}
public static void setShowReceiverDebugOverlay(Context context, boolean show) {
prefs(context).edit().putBoolean(KEY_DEV_RECEIVER_DEBUG_OVERLAY, show).apply();
}
/** Developer: write anonymous session_*.json files (default on). */
public static boolean isGrabSessionStats(Context context) {
return prefs(context).getBoolean(KEY_DEV_GRAB_SESSION_STATS, true);
}
public static void setGrabSessionStats(Context context, boolean grab) {
prefs(context).edit().putBoolean(KEY_DEV_GRAB_SESSION_STATS, grab).apply();
}
public static CastSettings loadSenderDefaults(Context context) { public static CastSettings loadSenderDefaults(Context context) {
CastSettings s = new CastSettings(); CastSettings s = new CastSettings();
applyStoredCastSettings(context, PREFIX_SENDER, s); applyStoredCastSettings(context, PREFIX_SENDER, s);

View File

@@ -56,9 +56,6 @@ public final class CastConfig {
/** Max UDP payload chunk (fits typical WiFi MTU with header). */ /** Max UDP payload chunk (fits typical WiFi MTU with header). */
public static final int UDP_CHUNK_SIZE = 1200; public static final int UDP_CHUNK_SIZE = 1200;
/** Show diagnostics overlay on receiver playback (debug). */
public static final boolean SHOW_CAST_DIAGNOSTICS = true;
/** Max retained session stat JSON files (room for one new file on next session). */ /** Max retained session stat JSON files (room for one new file on next session). */
public static final int SESSION_STATS_MAX_FILES = 20; public static final int SESSION_STATS_MAX_FILES = 20;

View File

@@ -1,15 +1,34 @@
package com.foxx.androidcast; package com.foxx.androidcast;
import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.widget.CheckBox;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
/** Hidden developer settings (opened via version label easter egg). */ /** Hidden developer settings (opened via version label easter egg). */
public class DeveloperSettingsActivity extends AppCompatActivity { public class DeveloperSettingsActivity extends AppCompatActivity {
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(CastLocaleHelper.attach(newBase));
}
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
CastThemeHelper.applyTheme(this);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_developer_settings); setContentView(R.layout.activity_developer_settings);
setTitle(R.string.developer_settings_title); setTitle(R.string.developer_settings_title);
CheckBox debugOverlay = findViewById(R.id.check_receiver_debug_overlay);
CheckBox sessionStats = findViewById(R.id.check_grab_session_stats);
debugOverlay.setChecked(AppPreferences.isShowReceiverDebugOverlay(this));
sessionStats.setChecked(AppPreferences.isGrabSessionStats(this));
debugOverlay.setOnCheckedChangeListener((buttonView, isChecked) ->
AppPreferences.setShowReceiverDebugOverlay(this, isChecked));
sessionStats.setOnCheckedChangeListener((buttonView, isChecked) ->
AppPreferences.setGrabSessionStats(this, isChecked));
} }
} }

View File

@@ -17,7 +17,9 @@ public class MainActivity extends DrawerHostActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
SessionStatsStore.pruneOnAppStart(this); if (AppPreferences.isGrabSessionStats(this)) {
SessionStatsStore.pruneOnAppStart(this);
}
com.foxx.androidcast.network.CastTransportFactory.init(this); com.foxx.androidcast.network.CastTransportFactory.init(this);
PermissionHelper.request(this); PermissionHelper.request(this);

View File

@@ -159,7 +159,9 @@ public class ReceiverCastService extends Service {
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
CastNotifications.createChannels(this); CastNotifications.createChannels(this);
SessionStatsStore.pruneOnAppStart(this); if (AppPreferences.isGrabSessionStats(this)) {
SessionStatsStore.pruneOnAppStart(this);
}
videoDecodeThread = new HandlerThread("VideoDecode"); videoDecodeThread = new HandlerThread("VideoDecode");
videoDecodeThread.start(); videoDecodeThread.start();
videoDecodeHandler = new Handler(videoDecodeThread.getLooper()); videoDecodeHandler = new Handler(videoDecodeThread.getLooper());
@@ -375,12 +377,14 @@ public class ReceiverCastService extends Service {
private void onSenderConnected(String senderName, CastSettings remoteSettings, String videoMime) { private void onSenderConnected(String senderName, CastSettings remoteSettings, String videoMime) {
phase = Phase.CONNECTED; phase = Phase.CONNECTED;
castEnded = false; castEnded = false;
if (sessionStatsRecorder == null) { if (AppPreferences.isGrabSessionStats(this)) {
sessionStatsRecorder = new SessionStatsRecorder("receiver", settings.getTransport()); if (sessionStatsRecorder == null) {
sessionStatsRecorder.setClientCount(1); sessionStatsRecorder = new SessionStatsRecorder("receiver", settings.getTransport());
sessionStatsRecorder.setClientCount(1);
}
sessionStatsRecorder.mergeSettings(remoteSettings);
sessionStatsRecorder.setCodecs(videoMime, "AAC");
} }
sessionStatsRecorder.mergeSettings(remoteSettings);
sessionStatsRecorder.setCodecs(videoMime, "AAC");
negotiatedVideoMime = videoMime; negotiatedVideoMime = videoMime;
remoteCastSettings = remoteSettings; remoteCastSettings = remoteSettings;
streamIdle = false; streamIdle = false;

View File

@@ -23,7 +23,6 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import com.foxx.androidcast.AppPreferences; import com.foxx.androidcast.AppPreferences;
import com.foxx.androidcast.CastConfig;
import com.foxx.androidcast.CastSettings; import com.foxx.androidcast.CastSettings;
import com.foxx.androidcast.DrawerHostActivity; import com.foxx.androidcast.DrawerHostActivity;
import com.foxx.androidcast.CastThemeHelper; import com.foxx.androidcast.CastThemeHelper;
@@ -296,7 +295,7 @@ public class ReceiverPlaybackActivity extends DrawerHostActivity {
if (diagnosticsText == null) { if (diagnosticsText == null) {
return; return;
} }
boolean show = CastConfig.SHOW_CAST_DIAGNOSTICS && streamRendering; boolean show = AppPreferences.isShowReceiverDebugOverlay(this) && streamRendering;
diagnosticsText.setVisibility(show ? View.VISIBLE : View.GONE); diagnosticsText.setVisibility(show ? View.VISIBLE : View.GONE);
if (show) { if (show) {
diagnosticsText.bringToFront(); diagnosticsText.bringToFront();
@@ -304,7 +303,8 @@ public class ReceiverPlaybackActivity extends DrawerHostActivity {
} }
private void refreshDiagnostics() { private void refreshDiagnostics() {
if (diagnosticsText == null || !CastConfig.SHOW_CAST_DIAGNOSTICS || !bound || receiverService == null) { if (diagnosticsText == null || !AppPreferences.isShowReceiverDebugOverlay(this)
|| !bound || receiverService == null) {
return; return;
} }
if (!streamRendering) { if (!streamRendering) {

View File

@@ -153,7 +153,9 @@ public class ScreenCastService extends Service implements
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
SessionStatsStore.pruneOnAppStart(this); if (AppPreferences.isGrabSessionStats(this)) {
SessionStatsStore.pruneOnAppStart(this);
}
CastNotifications.createChannels(this); CastNotifications.createChannels(this);
com.foxx.androidcast.network.CastTransportFactory.init(this); com.foxx.androidcast.network.CastTransportFactory.init(this);
projectionThread = new HandlerThread("CastProjection"); projectionThread = new HandlerThread("CastProjection");
@@ -263,10 +265,12 @@ public class ScreenCastService extends Service implements
CastActiveState.setSenderCasting(true); CastActiveState.setSenderCasting(true);
CastTrayNotifier.refresh(this); CastTrayNotifier.refresh(this);
activeSettings = settings; activeSettings = settings;
sessionStatsRecorder = new SessionStatsRecorder("sender", settings.getTransport()); if (AppPreferences.isGrabSessionStats(this)) {
sessionStatsRecorder.mergeSettings(settings); sessionStatsRecorder = new SessionStatsRecorder("sender", settings.getTransport());
sessionStatsRecorder.setClientCount(peers.size()); sessionStatsRecorder.mergeSettings(settings);
sessionStatsRecorder.setCodecs(settings.getNegotiatedVideoMime(), "AAC"); sessionStatsRecorder.setClientCount(peers.size());
sessionStatsRecorder.setCodecs(settings.getNegotiatedVideoMime(), "AAC");
}
final int projResultCode = resultCode; final int projResultCode = resultCode;
final Intent projData = data != null ? new Intent(data) : null; final Intent projData = data != null ? new Intent(data) : null;
if (calibration) { if (calibration) {

View File

@@ -2,6 +2,7 @@ package com.foxx.androidcast.stats;
import android.content.Context; import android.content.Context;
import com.foxx.androidcast.AppPreferences;
import com.foxx.androidcast.CastSettings; import com.foxx.androidcast.CastSettings;
import com.foxx.androidcast.network.control.NetworkStatsSnapshot; import com.foxx.androidcast.network.control.NetworkStatsSnapshot;
import com.foxx.androidcast.network.transport.TransportLossStats; import com.foxx.androidcast.network.transport.TransportLossStats;
@@ -139,6 +140,9 @@ public final class SessionStatsRecorder {
} }
public void finish(Context context) { public void finish(Context context) {
if (!AppPreferences.isGrabSessionStats(context)) {
return;
}
long endedMs = System.currentTimeMillis(); long endedMs = System.currentTimeMillis();
try { try {
JSONObject root = new JSONObject(); JSONObject root = new JSONObject();

View File

@@ -1,22 +1,36 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center" android:fillViewport="true">
android:orientation="vertical"
android:padding="24dp">
<TextView <LinearLayout
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/developer_settings_title" android:orientation="vertical"
android:textSize="20sp" android:padding="24dp">
android:textStyle="bold" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:text="@string/developer_settings_title"
android:gravity="center" android:textSize="20sp"
android:text="@string/developer_settings_stub" /> android:textStyle="bold" />
</LinearLayout>
<CheckBox
android:id="@+id/check_receiver_debug_overlay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:checked="true"
android:text="@string/dev_show_receiver_debug_overlay" />
<CheckBox
android:id="@+id/check_grab_session_stats"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:checked="true"
android:text="@string/dev_grab_session_stats" />
</LinearLayout>
</ScrollView>

View File

@@ -186,7 +186,8 @@
<string name="licenses_title">Лицензии</string> <string name="licenses_title">Лицензии</string>
<string name="licenses_load_error">Не могу найти факл лицензий</string> <string name="licenses_load_error">Не могу найти факл лицензий</string>
<string name="developer_settings_title">Для разработчиков</string> <string name="developer_settings_title">Для разработчиков</string>
<string name="developer_settings_stub">Additional developer options will appear here.</string> <string name="dev_show_receiver_debug_overlay">Показывать отладочный оверлей на приёмнике</string>
<string name="dev_grab_session_stats">Сохранять анонимную статистику сессий</string>
<string name="label_audio_codec">Кодек для звука</string> <string name="label_audio_codec">Кодек для звука</string>
<string name="codec_libvpx_vp8">libvpx (VP8)</string> <string name="codec_libvpx_vp8">libvpx (VP8)</string>
<string name="codec_libvpx_vp9">libvpx (VP9)</string> <string name="codec_libvpx_vp9">libvpx (VP9)</string>

View File

@@ -186,7 +186,8 @@
<string name="licenses_title">Licenses</string> <string name="licenses_title">Licenses</string>
<string name="licenses_load_error">Could not load license text.</string> <string name="licenses_load_error">Could not load license text.</string>
<string name="developer_settings_title">Developer settings</string> <string name="developer_settings_title">Developer settings</string>
<string name="developer_settings_stub">Additional developer options will appear here.</string> <string name="dev_show_receiver_debug_overlay">Show debug overlay on receiver</string>
<string name="dev_grab_session_stats">Grab anonymous session stats</string>
<string name="label_audio_codec">Voice codec</string> <string name="label_audio_codec">Voice codec</string>
<string name="codec_libvpx_vp8">libvpx (VP8)</string> <string name="codec_libvpx_vp8">libvpx (VP8)</string>
<string name="codec_libvpx_vp9">libvpx (VP9)</string> <string name="codec_libvpx_vp9">libvpx (VP9)</string>

View File

@@ -1,3 +1,21 @@
#!/bin/sh #!/bin/bash
adb shell run-as com.foxx.androidcast ls files/session_stats/ DEVICES_LIST="$(adb devices -l)"
adb shell run-as com.foxx.androidcast cat files/session_stats/session_*.json echo "${DEVICES_LIST}"
filter_out() {
local -n dev_string=${1}
[[ ${dev_string} =~ List* ]] && dev_string=""
return 0
}
echo "${DEVICES_LIST}" | while IFS= read -r DEV; do
SERIAL="$(echo "${DEV}" | awk '{print $1}')"
filter_out SERIAL
[ -z "${SERIAL}" ] && continue
# echo "Device: ${DEV}"
# echo "Serial: ${SERIAL}"
adb -s "${SERIAL}" shell run-as com.foxx.androidcast ls files/session_stats/
adb -s "${SERIAL}" shell run-as com.foxx.androidcast cat files/session_stats/session_*.json
done