feat:参照研听的基础工程

This commit is contained in:
jingyun
2026-06-18 15:02:28 +08:00
commit 364fc837b3
367 changed files with 16495 additions and 0 deletions
@@ -0,0 +1,176 @@
import 'package:device_info_plus/device_info_plus.dart';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:package_info_plus/package_info_plus.dart';
import '../../../common/config/shorebird_service.dart';
import '../../../common/network/auth_interceptor.dart';
import '../../../common/network/dio_provider.dart';
import '../../../common/router/app_router.dart';
import '../../../common/services/device_header_service.dart';
import '../../../common/storage/storage_keys.dart';
import '../../../common/storage/storage_service.dart';
import '../../../common/theme/theme_controller.dart';
class DebugInfoPage extends ConsumerWidget {
const DebugInfoPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final appConfig = ref.watch(appConfigProvider);
final dio = ref.watch(dioProvider);
final themeMode = ref.watch(themeModeControllerProvider);
return Scaffold(
appBar: AppBar(
leading: IconButton(
onPressed: () {
if (context.canPop()) {
context.pop();
} else {
context.go(AppRoutes.debug);
}
},
icon: const Icon(Icons.arrow_back_ios_new),
),
title: const Text('Info'),
),
body: FutureBuilder<_DebugInfoSnapshot>(
future: _loadSnapshot(
apiBaseUrl: appConfig.apiBaseUrl,
themeMode: themeMode.name,
dio: dio,
),
builder: (context, snapshot) {
final text = snapshot.data?.text ?? 'loading...';
return Padding(
padding: const EdgeInsets.all(16),
child: SelectableText(text, style: const TextStyle(fontSize: 12)),
);
},
),
);
}
}
Future<_DebugInfoSnapshot> _loadSnapshot({
required String apiBaseUrl,
required String themeMode,
required Dio dio,
}) async {
await DeviceHeaderService.to.ensureInitialized();
final packageInfo = await PackageInfo.fromPlatform();
final deviceInfo = await _readDeviceInfo();
final storage = StorageService.to;
final jwt = await loadJwt();
final commonHeaders = DeviceHeaderService.to.commonHeaders();
final platformHeaders = DeviceHeaderService.to.platformIdentityHeaders();
final headers = <String, dynamic>{
...commonHeaders,
...platformHeaders,
...dio.options.headers,
};
final gitVersion = const String.fromEnvironment(
'GITVERSION',
defaultValue: 'unknown',
);
final patch = ShorebirdService.instance.currentPatch;
final jwtLine = jwt?.isNotEmpty == true ? 'Bearer <redacted>' : '';
final text = StringBuffer()
..writeln('[build]')
..writeln('app_name: ${packageInfo.appName}')
..writeln('mode: ${kReleaseMode ? 'release' : 'debug'}')
..writeln('package_name: ${packageInfo.packageName}')
..writeln('version: ${packageInfo.version}')
..writeln('build_number: ${packageInfo.buildNumber}')
..writeln('theme_mode: $themeMode')
..writeln('platform: ${defaultTargetPlatform.name}')
..writeln('is_web: $kIsWeb')
..writeln()
..writeln('[environment]')
..writeln('api_base: $apiBaseUrl')
..writeln(
'connect_timeout_ms: ${dio.options.connectTimeout?.inMilliseconds}',
)
..writeln(
'receive_timeout_ms: ${dio.options.receiveTimeout?.inMilliseconds}',
)
..writeln('response_type: ${dio.options.responseType.name}')
..writeln('git_version: $gitVersion')
..writeln()
..writeln('[storage]')
..writeln('privacy_agreed: ${storage.getBool(storagePrivacyAgreed)}')
..writeln('ios_startup_seen: ${storage.getBool(storageIosStartupSeen)}')
..writeln('device_id: ${DeviceHeaderService.to.deviceId}')
..writeln('android_id: ${storage.getString(storageAndroidId) ?? ''}')
..writeln('device_token: ${storage.getString(storageDeviceToken) ?? ''}')
..writeln('android_oaid: ${storage.getString(storageAndroidOaid) ?? ''}')
..writeln('android_imei: ${storage.getString(storageAndroidImei) ?? ''}')
..writeln('android_mac: ${storage.getString(storageAndroidMac) ?? ''}')
..writeln('ios_idfa: ${storage.getString(storageIosIdfa) ?? ''}')
..writeln('ios_paid: ${storage.getString(storageIosPaid) ?? ''}')
..writeln()
..writeln('[auth]')
..writeln('jwt: $jwtLine')
..writeln()
..writeln('[headers]')
..writeln('common: $commonHeaders')
..writeln('platform: $platformHeaders')
..writeln('dio: $headers')
..writeln()
..writeln('[shorebird]')
..writeln('current_patch: ${patch?.number ?? 'none'}')
..writeln('patch: ${patch ?? 'none'}')
..writeln()
..writeln('[device]')
..writeln(deviceInfo);
return _DebugInfoSnapshot(text.toString());
}
Future<String> _readDeviceInfo() async {
try {
if (!kIsWeb &&
defaultTargetPlatform == TargetPlatform.android &&
!StorageService.to.getBool(storagePrivacyAgreed)) {
return 'privacy policy not accepted; device info not read';
}
final plugin = DeviceInfoPlugin();
if (kIsWeb) {
final info = await plugin.webBrowserInfo;
return info.data.toString();
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
final info = await plugin.androidInfo;
return info.data.toString();
case TargetPlatform.iOS:
final info = await plugin.iosInfo;
return info.data.toString();
case TargetPlatform.macOS:
final info = await plugin.macOsInfo;
return info.data.toString();
case TargetPlatform.windows:
final info = await plugin.windowsInfo;
return info.data.toString();
case TargetPlatform.linux:
final info = await plugin.linuxInfo;
return info.data.toString();
case TargetPlatform.fuchsia:
return 'fuchsia';
}
} catch (error) {
return 'unavailable: $error';
}
}
class _DebugInfoSnapshot {
const _DebugInfoSnapshot(this.text);
final String text;
}