Provide common account and service status tracking via Account

Similarly to ClientService, add a `current_status` property that denotes
the account's operational state, so that clients can just set a notify
on that to be informed of all account status changes. Keep the property
updated by watching for changes to the client service's status property.
This commit is contained in:
Michael Gratton 2018-12-30 11:32:03 +11:00 committed by Michael James Gratton
parent a637a6b63a
commit 20447c814f
5 changed files with 126 additions and 63 deletions

View file

@ -27,32 +27,73 @@ public abstract class Geary.Account : BaseObject {
internal const uint AUTH_ATTEMPTS_MAX = 3;
/**
/**
* Denotes the account's current status.
*
* @see Account.current_status
* @see ClientService.current_status
*/
[Flags]
public enum Status {
/**
* The account is currently online and operating normally.
*
* This flags will be set when the account's {@link incoming}
* service's {@link ClientService.current_status} is {@link
* ClientService.Status.CONNECTED}.
*/
ONLINE,
/**
* One or of the account's services is degraded.
*
* This flag will be set when one or both of its services has
* encountered a problem. Consult the {@link
* ClientService.current_status} to determine which and the
* exact problem.
*/
SERVICE_PROBLEM;
/** Determines if the {@link ONLINE} flag is set. */
public bool is_online() {
return (this & ONLINE) == ONLINE;
}
/** Determines if the {@link SERVICE_PROBLEM} flag is set. */
public bool has_service_problem() {
return (this & SERVICE_PROBLEM) == SERVICE_PROBLEM;
}
}
/**
* The account's current configuration.
*/
public AccountInformation information { get; protected set; }
/**
* Determines if this account appears to be online.
* The account's current status.
*
* This property is true if the account is to the best of the
* engine's knowledge is online, i.e. it is enabled, has been able
* to connect to the remote incoming mail server, and so on. Some
* network problems are not immediately obvious however, and so at
* times the value of this property may be inaccurate. At best it
* should be treated as a heuristic.
* This property's value is set based on the {@link
* ClientService.current_status} of the account's {@link incoming}
* and {@link outgoing} services. if
*
* @see ClientService.current_status
*/
public abstract bool is_online { get; protected set; }
public Status current_status { get; protected set; default = 0; }
/**
* The service manager for the incoming email service.
*/
public abstract ClientService incoming { get; }
public ClientService incoming { get; private set; }
/**
* The service manager for the outgoing email service.
*/
public abstract ClientService outgoing { get; }
public ClientService outgoing { get; private set; }
public Geary.ProgressMonitor search_upgrade_monitor { get; protected set; }
public Geary.ProgressMonitor db_upgrade_monitor { get; protected set; }
@ -174,11 +215,22 @@ public abstract class Geary.Account : BaseObject {
Gee.Map<Geary.EmailIdentifier, Geary.EmailFlags> map);
protected Account(AccountInformation information) {
protected Account(AccountInformation information,
ClientService incoming,
ClientService outgoing) {
this.information = information;
this.incoming = incoming;
this.outgoing = outgoing;
this.id = "%s[%s]".printf(
information.id, information.service_provider.to_value()
);
incoming.notify["current-status"].connect(
on_service_status_notify
);
outgoing.notify["current-status"].connect(
on_service_status_notify
);
}
/**
@ -478,4 +530,17 @@ public abstract class Geary.Account : BaseObject {
);
}
private void on_service_status_notify() {
Status new_status = 0;
if (incoming.current_status != UNKNOWN &&
incoming.current_status != UNREACHABLE) {
new_status |= ONLINE;
}
if (incoming.current_status.is_error() ||
outgoing.current_status.is_error()) {
new_status |= SERVICE_PROBLEM;
}
this.current_status = new_status;
}
}

View file

@ -25,6 +25,7 @@ public abstract class Geary.ClientService : BaseObject {
* Denotes the service's current status.
*
* @see ClientService.current_status
* @see Account.current_status
*/
public enum Status {
@ -109,6 +110,18 @@ public abstract class Geary.ClientService : BaseObject {
);
}
/**
* Determines the current status is an error condition.
*
* Returns true if not offline or connected.
*/
public bool is_error() {
return (
this != OFFLINE &&
this != CONNECTED
);
}
public string to_value() {
return ObjectUtils.to_enum_nick<Status>(typeof(Status), this);
}
@ -144,6 +157,8 @@ public abstract class Geary.ClientService : BaseObject {
* (e.g. online/offline state may not be fully known, and hence
* the value of this property reflects the engine's current
* understanding of the service's status, not necessarily reality.
*
* @see Account.current_status
*/
public Status current_status { get; protected set; default = OFFLINE; }

View file

@ -25,14 +25,6 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
Geary.SpecialFolderType.ARCHIVE,
};
public override bool is_online { get; protected set; default = false; }
/** Returns the IMAP client service. */
public override ClientService incoming { get { return this.imap; } }
/** Returns the SMTP client service. */
public override ClientService outgoing { get { return this.smtp; } }
/** Service for incoming IMAP connections. */
public Imap.ClientService imap { get; private set; }
@ -64,26 +56,32 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
ImapDB.Account local,
Endpoint incoming_remote,
Endpoint outgoing_remote) {
base(config);
Imap.ClientService imap = new Imap.ClientService(
config,
config.incoming,
incoming_remote
);
Smtp.ClientService smtp = new Smtp.ClientService(
config,
config.outgoing,
outgoing_remote
);
base(config, imap, smtp);
this.local = local;
this.local.contacts_loaded.connect(() => { contacts_loaded(); });
this.imap = new Imap.ClientService(
config, config.incoming, incoming_remote
);
this.imap.min_pool_size = IMAP_MIN_POOL_SIZE;
this.imap.notify["current-status"].connect(
imap.min_pool_size = IMAP_MIN_POOL_SIZE;
imap.notify["current-status"].connect(
on_imap_status_notify
);
this.imap = imap;
this.smtp = new Smtp.ClientService(
config,
config.outgoing,
outgoing_remote,
new Outbox.Folder(this, this.local)
);
this.smtp.email_sent.connect(on_email_sent);
this.smtp.report_problem.connect(notify_report_problem);
smtp.outbox = new Outbox.Folder(this, local);
smtp.email_sent.connect(on_email_sent);
smtp.report_problem.connect(notify_report_problem);
this.smtp = smtp;
this.sync = new AccountSynchronizer(this);
@ -994,11 +992,9 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
private void on_imap_status_notify() {
if (this.open) {
if (this.imap.current_status == CONNECTED) {
this.is_online = true;
this.remote_ready_lock.blind_notify();
update_remote_folders();
} else {
this.is_online = false;
this.remote_ready_lock.reset();
this.refresh_folder_timer.reset();
}

View file

@ -23,7 +23,7 @@ internal class Geary.Smtp.ClientService : Geary.ClientService {
/** Folder used for storing and retrieving queued mail. */
public Outbox.Folder outbox { get; private set; }
public Outbox.Folder? outbox { get; internal set; default = null; }
/** Progress monitor indicating when email is being sent. */
public ProgressMonitor sending_monitor {
@ -47,10 +47,8 @@ internal class Geary.Smtp.ClientService : Geary.ClientService {
public ClientService(AccountInformation account,
ServiceInformation service,
Endpoint remote,
Outbox.Folder outbox) {
Endpoint remote) {
base(account, service, remote);
this.outbox = outbox;
}
/**

View file

@ -19,7 +19,7 @@ public class Geary.MockAccount : Account, MockObject {
public class MockContactStore : ContactStore {
internal MockContactStore() {
}
public override async void
@ -60,34 +60,23 @@ public class Geary.MockAccount : Account, MockObject {
}
public override bool is_online { get; protected set; default = false; }
public override ClientService incoming {
get { return this.incoming; }
}
private ClientService _incoming;
public override ClientService outgoing {
get { return this._outgoing; }
}
private ClientService _outgoing;
protected Gee.Queue<ExpectedCall> expected {
get; set; default = new Gee.LinkedList<ExpectedCall>();
}
public MockAccount(AccountInformation config) {
base(config);
this._incoming = new MockClientService(
config,
config.incoming,
new Endpoint(config.incoming.host, config.incoming.port, 0, 0)
);
this._outgoing = new MockClientService(
config,
config.outgoing,
new Endpoint(config.outgoing.host, config.outgoing.port, 0, 0)
base(config,
new MockClientService(
config,
config.incoming,
new Endpoint(config.incoming.host, config.incoming.port, 0, 0)
),
new MockClientService(
config,
config.outgoing,
new Endpoint(config.outgoing.host, config.outgoing.port, 0, 0)
)
);
}