use std::time::Duration;
use std::{ffi::NulError, path::Path};
use iced::{Event, Subscription, event, time, window};
pub use permission_flow::{
AppPath, NewControllerError, Permission, PermissionAuthorizationState,
PermissionFlowController, PermissionStatusError, StartFlowOptions, StartPermissionFlowError,
StopPermissionFlowError,
};
pub struct PermissionFlowButton {
controller: PermissionFlowController,
options: StartFlowOptions,
authorization_state: PermissionAuthorizationState,
}
impl PermissionFlowButton {
pub fn new(permission: Permission, app_path: &str) -> Result<Self, PermissionFlowButtonError> {
let app_path = AppPath::try_from(app_path)?;
Self::with_options(StartFlowOptions::new(permission, app_path))
}
pub fn new_from_path(
permission: Permission,
app_path: &Path,
) -> Result<Self, PermissionFlowButtonError> {
let app_path = AppPath::try_from(app_path)?;
Self::with_options(StartFlowOptions::new(permission, app_path))
}
pub fn with_options(options: StartFlowOptions) -> Result<Self, PermissionFlowButtonError> {
let controller = PermissionFlowController::new()?;
let authorization_state = options.permission().authorization_state()?;
Ok(Self {
controller,
options,
authorization_state,
})
}
pub fn press(&self) -> Result<(), StartPermissionFlowError> {
self.controller.start_flow(self.options.clone())
}
pub fn start_flow(&self) -> Result<(), StartPermissionFlowError> {
self.press()
}
pub fn stop_current_flow(&self) -> Result<(), StopPermissionFlowError> {
self.controller.stop_current_flow()
}
pub fn refresh(&mut self) -> Result<(), PermissionStatusError> {
self.authorization_state = self.options.permission().authorization_state()?;
Ok(())
}
pub fn refresh_on_window_event(
&mut self,
event: &window::Event,
) -> Result<bool, PermissionStatusError> {
if matches!(event, window::Event::Focused) {
self.refresh()?;
Ok(true)
} else {
Ok(false)
}
}
pub fn refresh_on_event(&mut self, event: &Event) -> Result<bool, PermissionStatusError> {
match event {
Event::Window(event) => self.refresh_on_window_event(event),
_ => Ok(false),
}
}
pub fn subscription(&self) -> Subscription<PermissionFlowButtonMessage> {
self.subscription_with_interval(Duration::from_secs(1))
}
pub fn subscription_with_interval(
&self,
interval: Duration,
) -> Subscription<PermissionFlowButtonMessage> {
Subscription::batch([
event::listen_with(|event, _status, _window| match event {
Event::Window(_) => Some(PermissionFlowButtonMessage::RuntimeEvent(event)),
_ => None,
}),
time::every(interval).map(|_| PermissionFlowButtonMessage::RefreshTick),
])
}
pub fn update(
&mut self,
message: &PermissionFlowButtonMessage,
) -> Result<bool, PermissionStatusError> {
match message {
PermissionFlowButtonMessage::RefreshTick => {
self.refresh()?;
Ok(true)
}
PermissionFlowButtonMessage::RuntimeEvent(event) => self.refresh_on_event(event),
}
}
pub fn button_state(&self) -> PermissionFlowButtonState {
PermissionFlowButtonState::from(self.authorization_state)
}
pub fn authorization_state(&self) -> PermissionAuthorizationState {
self.authorization_state
}
pub fn options(&self) -> &StartFlowOptions {
&self.options
}
}
#[derive(Debug, Clone)]
pub enum PermissionFlowButtonMessage {
RefreshTick,
RuntimeEvent(Event),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum PermissionFlowButtonState {
Granted,
GrantAccess,
OpenSettings,
Checking,
}
impl PermissionFlowButtonState {
pub fn from_authorization_state(state: PermissionAuthorizationState) -> Self {
match state {
PermissionAuthorizationState::Granted => Self::Granted,
PermissionAuthorizationState::NotGranted => Self::GrantAccess,
PermissionAuthorizationState::Unknown => Self::OpenSettings,
PermissionAuthorizationState::Checking => Self::Checking,
}
}
pub fn label(self) -> &'static str {
match self {
Self::Granted => "Granted",
Self::GrantAccess => "Grant Access",
Self::OpenSettings => "Open Settings",
Self::Checking => "Checking...",
}
}
pub fn is_granted(self) -> bool {
matches!(self, Self::Granted)
}
}
impl From<PermissionAuthorizationState> for PermissionFlowButtonState {
fn from(state: PermissionAuthorizationState) -> Self {
Self::from_authorization_state(state)
}
}
#[derive(Debug, thiserror::Error)]
pub enum PermissionFlowButtonError {
#[error(transparent)]
InvalidAppPath(#[from] NulError),
#[error(transparent)]
NewController(#[from] NewControllerError),
#[error(transparent)]
PermissionStatus(#[from] PermissionStatusError),
}
#[cfg(test)]
mod tests {
use super::{PermissionAuthorizationState, PermissionFlowButtonState};
#[test]
fn granted_state_maps_to_granted_button_state() {
let state = PermissionFlowButtonState::from_authorization_state(
PermissionAuthorizationState::Granted,
);
assert_eq!(state, PermissionFlowButtonState::Granted);
assert!(state.is_granted());
}
#[test]
fn not_granted_state_maps_to_call_to_action() {
let state = PermissionFlowButtonState::from_authorization_state(
PermissionAuthorizationState::NotGranted,
);
assert_eq!(state, PermissionFlowButtonState::GrantAccess);
assert!(!state.is_granted());
}
}