diff --git a/podcasts-gtk/src/main.rs b/podcasts-gtk/src/main.rs index f802e48..122ede3 100644 --- a/podcasts-gtk/src/main.rs +++ b/podcasts-gtk/src/main.rs @@ -87,6 +87,7 @@ mod headerbar; mod window; mod manager; +mod search_provider; mod settings; mod static_resource; mod utils; diff --git a/podcasts-gtk/src/meson.build b/podcasts-gtk/src/meson.build index 63d0739..cdc455b 100644 --- a/podcasts-gtk/src/meson.build +++ b/podcasts-gtk/src/meson.build @@ -53,6 +53,7 @@ podcasts_sources = files( 'i18n.rs', 'main.rs', 'manager.rs', + 'search_provider.rs', 'settings.rs', 'utils.rs' ) diff --git a/podcasts-gtk/src/search_provider.rs b/podcasts-gtk/src/search_provider.rs new file mode 100644 index 0000000..0326115 --- /dev/null +++ b/podcasts-gtk/src/search_provider.rs @@ -0,0 +1,122 @@ +// search_provider.rs +// +// Copyright 2019 Felix Häcker +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// SPDX-License-Identifier: GPL-3.0-or-later + +use crossbeam_channel::Sender; +use gtk::prelude::*; +use podcasts_data::dbqueries; +use search_provider::{ResultMetadata, SearchProvider as SP}; + +use std::str::FromStr; +use std::sync::Arc; + +use crate::app::Action; +use crate::config::APP_ID; + +#[derive(Debug, Clone)] +pub(crate) struct SearchProvider { + search_provider: Arc, + window: gtk::ApplicationWindow, + sender: Sender, +} + +impl SearchProvider { + pub(crate) fn new(window: gtk::ApplicationWindow, sender: Sender) -> SearchProvider { + let search_provider = SP::new( + APP_ID.to_string(), + "/org/gnome/Podcasts/SearchProvider".to_string(), + ); + + let sp = SearchProvider { + search_provider, + window, + sender, + }; + + sp.setup_callbacks(); + sp + } + + fn setup_callbacks(&self) { + let window = self.window.clone(); + let sender = self.sender.clone(); + self.search_provider + .connect_activate_result(move |sp_id, _, timestamp| { + // Show window + window.present_with_time(timestamp); + window.show_all(); + window.present(); + + // Get episode + let mut id = sp_id.clone().split("_").collect::>(); + let title: &str = id.pop().unwrap(); + let show_id: i32 = std::str::FromStr::from_str(id.pop().unwrap()).unwrap(); + let episode = dbqueries::get_episode_from_pk(title, show_id).unwrap(); + + // Play episode + sender + .send(Action::InitEpisode(episode.rowid())) + .expect("Action channel blew up somehow"); + // TODO: Sometimes it's necessary to download the episode first. Otherwise it cannot be played. + // TODO: Greyout title state doesn't get applied. + }); + + self.search_provider + .connect_get_initial_result_set(|terms| { + let term = terms.join(""); + Self::search(term) + }); + + self.search_provider + .connect_get_subsearch_result_set(|_, terms| { + let term = terms.join(""); + Self::search(term) + }); + + self.search_provider.connect_get_result_metas(|sp_ids| { + let mut metas = Vec::new(); + for sp_id in sp_ids { + let id = sp_id.clone().split("_").collect::>(); + let show_id: i32 = FromStr::from_str(id[0]).unwrap(); + + let episode = dbqueries::get_episode_from_pk(id[1], show_id).unwrap(); + + let meta = ResultMetadata::new( + sp_id, + episode.title(), + "folder-documents-symbolic", + episode.description().unwrap_or(""), + ); + metas.insert(0, meta); + } + metas + }); + } + + fn search(term: String) -> Vec { + let episodes = dbqueries::search_episodes(&term) + .expect("Could not search for episodes (search provider)"); + + let mut sp_ids = Vec::new(); + for episode in episodes { + let sp_id = format!("{}_{}", episode.show_id(), episode.title()); + sp_ids.insert(0, sp_id); + } + sp_ids + } +} diff --git a/podcasts-gtk/src/window.rs b/podcasts-gtk/src/window.rs index 481464c..2bf845a 100644 --- a/podcasts-gtk/src/window.rs +++ b/podcasts-gtk/src/window.rs @@ -29,6 +29,7 @@ use crossbeam_channel::{unbounded, Receiver, Sender}; use crate::app::{Action, PdApplication}; use crate::headerbar::Header; +use crate::search_provider::SearchProvider; use crate::settings::{self, WindowGeometry}; use crate::stacks::Content; use crate::utils; @@ -149,6 +150,9 @@ impl MainWindow { glib::Continue(true) }); + // Setup GNOME Shell search provider integration + let search_provider = SearchProvider::new(window.clone(), sender.clone()); + Self { app: app.clone(), window,