Skip to content

Commit

Permalink
feat: support app run time
Browse files Browse the repository at this point in the history
  • Loading branch information
MuZhou233 committed Mar 14, 2024
1 parent 8465b61 commit 332d41e
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 27 deletions.
58 changes: 57 additions & 1 deletion lib/bloc/gebura/gebura_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import '../../ffi/ffi_model.dart';
import '../../l10n/l10n.dart';
import '../../model/gebura_model.dart';
import '../../repo/grpc/api_helper.dart';
import '../../repo/grpc/type_helper.dart';
import '../../repo/local/gebura.dart';

part 'gebura_event.dart';
Expand Down Expand Up @@ -88,11 +89,35 @@ class GeburaBloc extends Bloc<GeburaEvent, GeburaState> {
}
ownedAppInsts.addAll(appInstResp.getData().appInsts);
}
final appInstRunTimes = <InternalID, Duration>{};
for (final appInst in ownedAppInsts) {
final runTimeResp = await _api.doRequest(
(client) => client.sumAppInstRunTime,
SumAppInstRunTimeRequest(
appInstId: appInst.id,
timeAggregation: TimeAggregation(
aggregationType:
TimeAggregation_AggregationType.AGGREGATION_TYPE_OVERALL,
timeRange: toPBTimeRange(
DateTime.now().subtract(const Duration(days: 365 * 10)),
DateTime.now()),
),
),
);
if (runTimeResp.status != ApiStatus.success) {
continue;
}
if (runTimeResp.getData().runTimeGroups.isNotEmpty) {
final group = runTimeResp.getData().runTimeGroups.first;
appInstRunTimes[appInst.id] = fromPBDuration(group.duration);
}
}
emit(GeburaRefreshLibraryState(
state.copyWith(
purchasedAppInfos: resp.getData().appInfos,
ownedApps: ownedApps,
ownedAppInsts: ownedAppInsts,
appInstRunTimes: appInstRunTimes,
),
EventStatus.success,
msg: resp.error,
Expand Down Expand Up @@ -372,11 +397,17 @@ class GeburaBloc extends Bloc<GeburaEvent, GeburaState> {
msg: S.current.applicationExitAbnormally));
return;
}
state.runState![appID] = AppRunState(
final runState = AppRunState(
running: false,
startTime: DateTime.fromMillisecondsSinceEpoch(start * 1000),
endTime: DateTime.fromMillisecondsSinceEpoch(end * 1000),
);
state.runState![appID] = runState;
add(GeburaReportAppRunTimeEvent(
event.appInstID,
runState.startTime!,
runState.endTime!,
));
emit(GeburaRunAppState(
state,
appID,
Expand Down Expand Up @@ -738,6 +769,31 @@ class GeburaBloc extends Bloc<GeburaEvent, GeburaState> {
msg: resp.error));
add(GeburaRefreshLibraryEvent());
}, transformer: droppable());

on<GeburaReportAppRunTimeEvent>((event, emit) async {
emit(GeburaReportAppRunTimeState(state, EventStatus.processing));
final resp = await _api.doRequest(
(client) => client.addAppInstRunTime,
AddAppInstRunTimeRequest(
appInstId: event.appInstID,
timeRange: toPBTimeRange(
event.startTime,
event.endTime,
),
),
);
if (resp.status != ApiStatus.success) {
emit(GeburaReportAppRunTimeState(state, EventStatus.failed,
msg: resp.error));
return;
}
emit(GeburaReportAppRunTimeState(
state,
EventStatus.success,
msg: resp.error,
));
add(GeburaRefreshLibraryEvent());
});
}

LocalAppInstLauncherSetting? getAppLauncherSetting(InternalID id) {
Expand Down
12 changes: 12 additions & 0 deletions lib/bloc/gebura/gebura_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,15 @@ final class GeburaSearchNewAppInfoEvent extends GeburaEvent {

GeburaSearchNewAppInfoEvent(this.query);
}

final class GeburaReportAppRunTimeEvent extends GeburaEvent {
final InternalID appInstID;
final DateTime startTime;
final DateTime endTime;

GeburaReportAppRunTimeEvent(
this.appInstID,
this.startTime,
this.endTime,
);
}
40 changes: 40 additions & 0 deletions lib/bloc/gebura/gebura_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class GeburaState {

late Map<Int64, AppLauncherSetting>? appLauncherSettings;
late Map<InternalID, AppRunState>? runState;
late Map<InternalID, Duration>? appInstRunTimes;

late String? localLibraryState;
late SteamScanResult? localSteamScanResult;
Expand Down Expand Up @@ -45,6 +46,29 @@ class GeburaState {
return result;
}

Duration? getRunTime(Int64 id) {
if (appInstRunTimes == null) {
return null;
}
final insts = getAppInsts(id);
if (insts.isEmpty) {
return null;
}
return insts.values.fold<Duration>(
Duration.zero,
(previousValue, element) {
return previousValue +
element.fold<Duration>(
Duration.zero,
(previousValue, element) {
return previousValue +
(appInstRunTimes![element.id] ?? Duration.zero);
},
);
},
);
}

GeburaState({
this.appInfoMap,
this.purchasedAppInfos,
Expand All @@ -60,6 +84,7 @@ class GeburaState {
this.localSteamAppInsts,
this.importedSteamAppInsts,
this.localSteamLibraryFolders,
this.appInstRunTimes,
});

GeburaState copyWith({
Expand All @@ -77,6 +102,7 @@ class GeburaState {
List<InstalledSteamApps>? localSteamAppInsts,
List<ImportedSteamAppInst>? importedSteamAppInsts,
List<String>? localSteamLibraryFolders,
Map<InternalID, Duration>? appInstRunTimes,
}) {
return GeburaState(
appInfoMap: appInfoMap ?? this.appInfoMap,
Expand All @@ -95,6 +121,7 @@ class GeburaState {
importedSteamAppInsts ?? this.importedSteamAppInsts,
localSteamLibraryFolders:
localSteamLibraryFolders ?? this.localSteamLibraryFolders,
appInstRunTimes: appInstRunTimes ?? this.appInstRunTimes,
);
}

Expand All @@ -113,6 +140,7 @@ class GeburaState {
localSteamAppInsts = other.localSteamAppInsts;
importedSteamAppInsts = other.importedSteamAppInsts;
localSteamLibraryFolders = other.localSteamLibraryFolders;
appInstRunTimes = other.appInstRunTimes;
}
}

Expand Down Expand Up @@ -315,3 +343,15 @@ class GeburaSearchNewAppInfoState extends GeburaState with EventStatusMixin {
@override
final String? msg;
}

class GeburaReportAppRunTimeState extends GeburaState with EventStatusMixin {
GeburaReportAppRunTimeState(GeburaState state, this.statusCode, {this.msg})
: super() {
_from(state);
}

@override
final EventStatus? statusCode;
@override
final String? msg;
}
18 changes: 18 additions & 0 deletions lib/repo/grpc/type_helper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import 'package:fixnum/fixnum.dart';
import 'package:tuihub_protos/google/protobuf/duration.pb.dart' as duration_pb;
import 'package:tuihub_protos/google/protobuf/timestamp.pb.dart';
import 'package:tuihub_protos/librarian/v1/common.pb.dart';

duration_pb.Duration toPBDuration(Duration duration) {
return duration_pb.Duration(seconds: Int64(duration.inSeconds));
}

TimeRange toPBTimeRange(DateTime start, DateTime end) {
return TimeRange()
..startTime = Timestamp.fromDateTime(start)
..duration = toPBDuration(end.difference(start));
}

Duration fromPBDuration(duration_pb.Duration duration) {
return Duration(seconds: duration.seconds.toInt());
}
61 changes: 35 additions & 26 deletions lib/view/pages/gebura/gebura_library_detail.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,22 @@ class GeburaLibraryDetailPage extends StatelessWidget {
.add(GeburaFetchAppLauncherSettingEvent(item.id));
}
}

final runTime = state.getRunTime(item.id.id);
late String runTimeStr;
if (runTime != null) {
if (runTime.inSeconds == 0) {
runTimeStr = '';
} else if (runTime.inSeconds < 100) {
runTimeStr = '${runTime.inSeconds} 秒';
} else if (runTime.inMinutes < 100) {
runTimeStr = '${runTime.inMinutes} 分钟';
} else {
runTimeStr =
'${((runTime.inSeconds.toDouble()) / 3600).toStringAsFixed(1)} 小时';
}
} else {
runTimeStr = '错误';
}
final runState =
state.runState != null ? state.runState![item.id] : null;
return Scaffold(
Expand Down Expand Up @@ -175,15 +190,18 @@ class GeburaLibraryDetailPage extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (PlatformHelper.isWindowsApp())
if (setting != null)
ElevatedButton.icon(
onPressed: () async {
context
.read<GeburaBloc>()
.add(GeburaRunAppEvent(item.id));
},
onPressed: (runState?.running ?? false)
? null
: () async {
context
.read<GeburaBloc>()
.add(GeburaRunAppEvent(item.id));
},
icon: Icon(
setting.type == AppLauncherType.steam
? FontAwesomeIcons.steam
Expand All @@ -200,26 +218,17 @@ class GeburaLibraryDetailPage extends StatelessWidget {
const SizedBox(
width: 24,
),
// Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text('开发商:${item.details.developer}'),
// Text('发行商:${item.details.publisher}'),
// Text('发行日期:${item.details.releaseDate}'),
// ],
// ),
// const SizedBox(
// width: 24,
// ),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'运行状态:${runState?.running ?? false ? '运行中' : '未运行'}'),
Text('启动时间:${runState?.startTime ?? ''}'),
Text('停止时间:${runState?.endTime ?? ''}'),
],
),
if (runTimeStr.isNotEmpty)
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('总运行时间',
style:
Theme.of(context).textTheme.bodySmall),
Text(runTimeStr),
],
),
const Expanded(child: SizedBox()),
_GeburaLibraryDetailAppSettings(item: item),
],
Expand Down

0 comments on commit 332d41e

Please sign in to comment.