blob: 0e6051f173c2d67d66e08d1d310583fa3c2d3d16 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/app/app_metrics_app_state_agent.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#import "ios/chrome/app/application_delegate/app_state.h"
#import "ios/chrome/browser/metrics/ios_profile_session_durations_service.h"
#import "ios/chrome/browser/metrics/ios_profile_session_durations_service_factory.h"
#import "ios/chrome/browser/ui/main/scene_state.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface AppMetricsAppStateAgent () <SceneStateObserver>
// Observed app state.
@property(nonatomic, weak) AppState* appState;
@end
@implementation AppMetricsAppStateAgent
#pragma mark - AppStateAgent
- (void)setAppState:(AppState*)appState {
// This should only be called once!
DCHECK(!_appState);
_appState = appState;
[appState addObserver:self];
}
#pragma mark - AppStateObserver
- (void)appState:(AppState*)appState sceneConnected:(SceneState*)sceneState {
[sceneState addObserver:self];
}
- (void)appState:(AppState*)appState
didTransitionFromInitStage:(InitStage)previousInitStage {
if (previousInitStage == InitStageSafeMode) {
// Log session start if the app is already foreground
if (self.appState.foregroundScenes.count > 0) {
[self handleSessionStart];
}
}
}
#pragma mark - SceneStateObserver
- (void)sceneState:(SceneState*)sceneState
transitionedToActivationLevel:(SceneActivationLevel)level {
if (self.appState.initStage <= InitStageSafeMode) {
return;
}
if (level >= SceneActivationLevelForegroundInactive &&
self.appState.lastTimeInForeground.is_null()) {
[self handleSessionStart];
} else if (level <= SceneActivationLevelBackground) {
// Do not consider the app as brackgrounded when there are still scenes on
// the foreground.
if (self.appState.foregroundScenes.count > 0) {
return;
}
if (self.appState.lastTimeInForeground.is_null()) {
// This method will be called multiple times, once per scene, if multiple
// scenes go background simulatneously (for example, if two windows were
// in split screen and the user swiped to go home). Only log the session
// duration once. This also makes sure that the first scene that ramps up
// to foreground doesn't end the session.
return;
}
[self handleSessionEnd];
DCHECK(self.appState.lastTimeInForeground.is_null());
}
}
#pragma mark - private
- (void)handleSessionStart {
self.appState.lastTimeInForeground = base::TimeTicks::Now();
IOSProfileSessionDurationsService* psdService = [self psdService];
if (psdService) {
psdService->OnSessionStarted(self.appState.lastTimeInForeground);
}
}
- (void)handleSessionEnd {
DCHECK(!self.appState.lastTimeInForeground.is_null());
base::TimeDelta duration =
base::TimeTicks::Now() - self.appState.lastTimeInForeground;
UMA_HISTOGRAM_LONG_TIMES("Session.TotalDuration", duration);
UMA_HISTOGRAM_CUSTOM_TIMES("Session.TotalDurationMax1Day", duration,
base::TimeDelta::FromMilliseconds(1),
base::TimeDelta::FromHours(24), 50);
IOSProfileSessionDurationsService* psdService = [self psdService];
if (psdService) {
psdService->OnSessionEnded(duration);
}
self.appState.lastTimeInForeground = base::TimeTicks();
}
- (IOSProfileSessionDurationsService*)psdService {
if (!self.appState.mainBrowserState) {
return nil;
}
return IOSProfileSessionDurationsServiceFactory::GetForBrowserState(
self.appState.mainBrowserState);
}
@end