177 lines
6.0 KiB
Dart
177 lines
6.0 KiB
Dart
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;
|
|
}
|