blob: 7ac53fcb8093d892db68678fe8b4878f6bcef24f [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/chrome/app/background_refresh/app_refresh_provider.h"
#import "base/cancelable_callback.h"
#import "base/functional/bind.h"
#import "base/notreached.h"
#import "base/task/thread_pool.h"
#import "ios/web/public/thread/web_task_traits.h"
#import "ios/web/public/thread/web_thread.h"
@interface AppRefreshProvider ()
// Key storing the last run time.
@property(nonatomic, readonly) NSString* defaultsKey;
@end
@implementation AppRefreshProvider {
base::CancelableOnceCallback<void()> _task;
SEQUENCE_CHECKER(_sequenceChecker);
}
- (instancetype)init {
if ((self = [super init])) {
_refreshInterval = base::Minutes(30);
}
return self;
}
#pragma mark - Public properties
- (base::Time)lastRun {
DCHECK_CALLED_ON_VALID_SEQUENCE(_sequenceChecker);
NSDate* lastRunDate =
[[NSUserDefaults standardUserDefaults] objectForKey:self.defaultsKey];
// If the provider was never run before, return a time in the distant past.
base::Time lastRunTime = lastRunDate
? base::Time::FromNSDate(lastRunDate)
: base::Time::FromNSDate([NSDate distantPast]);
return lastRunTime;
}
- (void)setLastRun:(base::Time)lastRun {
DCHECK_CALLED_ON_VALID_SEQUENCE(_sequenceChecker);
// Store the last run time.
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:lastRun.ToNSDate() forKey:self.defaultsKey];
}
- (BOOL)isDue {
DCHECK_CALLED_ON_VALID_SEQUENCE(_sequenceChecker);
return base::Time::Now() - self.lastRun > self.refreshInterval;
}
- (scoped_refptr<base::SingleThreadTaskRunner>)taskThread {
return web::GetIOThreadTaskRunner({});
}
#pragma mark - Private properties
- (NSString*)defaultsKey {
CHECK(self.identifier.length > 0)
<< "Subclasses of AppRefreshProvider must provide an identifier.";
return
[@"AppRefreshProvider_lastRun_" stringByAppendingString:self.identifier];
}
#pragma mark - Public methods
// Called on the main thread, runs tasks on (by default) the IO thread.
- (void)handleRefreshWithCompletion:(ProceduralBlock)completion {
DCHECK_CALLED_ON_VALID_SEQUENCE(_sequenceChecker);
id<AppRefreshProviderTask> task = [self task];
_task.Reset(base::BindOnce(^{
[task execute];
}));
__weak __typeof(self) weakSelf = self;
base::OnceCallback callback = base::BindOnce(^{
[weakSelf refreshTaskFinishedWithCompletion:completion];
});
// `_task` has a cancelable wrapper, so `_task.callback()` is the underlying
// OnceCallback to execute.
self.taskThread->PostTaskAndReply(FROM_HERE, _task.callback(),
std::move(callback));
}
// Terminate the running task immediately.
- (void)cancelRefresh {
DCHECK_CALLED_ON_VALID_SEQUENCE(_sequenceChecker);
_task.Cancel();
}
- (id<AppRefreshProviderTask>)task {
DCHECK_CALLED_ON_VALID_SEQUENCE(_sequenceChecker);
NOTREACHED() << "Subclasses of AppRefreshProvider must implement -task";
}
#pragma mark - Private methods
- (void)refreshTaskFinishedWithCompletion:(ProceduralBlock)completion {
DCHECK_CALLED_ON_VALID_SEQUENCE(_sequenceChecker);
self.lastRun = base::Time::Now();
completion();
}
@end