File indexing completed on 2024-04-21 16:10:57
0001 # Copyright 2016 Mycroft AI Inc. 0002 # 0003 # Licensed under the Apache License, Version 2.0 (the "License"); 0004 # you may not use this file except in compliance with the License. 0005 # You may obtain a copy of the License at 0006 # 0007 # http://www.apache.org/licenses/LICENSE-2.0 0008 # 0009 # Unless required by applicable law or agreed to in writing, software 0010 # distributed under the License is distributed on an "AS IS" BASIS, 0011 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 0012 # See the License for the specific language governing permissions and 0013 # limitations under the License. 0014 0015 import subprocess 0016 0017 from adapt.intent import IntentBuilder 0018 from adapt.tools.text.tokenizer import EnglishTokenizer 0019 0020 from mycroft import MycroftSkill, intent_handler 0021 from urllib.parse import quote 0022 from gi.repository import Gio as gio 0023 0024 class ApplicationLauncher(MycroftSkill): 0025 def __init__(self): 0026 super().__init__() 0027 self.appmap = {} 0028 self.kalterlist = ["console"] 0029 0030 def initialize(self): 0031 tokenizer = EnglishTokenizer() 0032 0033 for app in gio.app_info_get_all(): 0034 name = app.get_name().lower() 0035 if name is None: 0036 # Likely an empty .desktop entry, skip it 0037 continue 0038 entry = [app] 0039 tokenized_name = tokenizer.tokenize(name)[0] 0040 0041 if name in self.appmap: 0042 self.appmap[name] += entry 0043 else: 0044 self.appmap[name] = entry 0045 0046 self.register_vocabulary(name, 'Application') 0047 if name != tokenized_name: 0048 self.register_vocabulary(tokenized_name, 'Application') 0049 if tokenized_name in self.appmap: 0050 self.appmap[tokenized_name] += entry 0051 else: 0052 self.appmap[tokenized_name] = entry 0053 0054 @intent_handler(IntentBuilder('LaunchDesktopApplicationIntent') 0055 .require('LaunchKeyword') 0056 .require('Application')) 0057 def handle_launch_desktop_app(self, message): 0058 """Launch a dektop application using Desktop file.""" 0059 app_name = message.data.get('Application') 0060 if app_name in self.kalterlist: 0061 app_name == self.get_alter_name(app_name) 0062 apps = self.appmap.get(app_name) 0063 if apps and len(apps) > 0: 0064 self.log.info('Launching {}'.format(app_name)) 0065 apps[0].launch() 0066 0067 @intent_handler(IntentBuilder('CloseDesktopApplicationIntent') 0068 .require('CloseKeyword').require('Application')) 0069 def handle_close_desktop_app(self, message): 0070 """Close application using killall""" 0071 app_name = message.data.get("Application") 0072 0073 if not self.kill_process_by_name(app_name): 0074 self.log.info( 0075 "Couldn't kill {} try to get executable from desktop file".format( 0076 app_name 0077 ) 0078 ) 0079 apps = self.appmap.get(app_name) 0080 if apps: 0081 app_name = apps[0].get_string("Exec") 0082 if not self.kill_process_by_name(app_name): 0083 self.speak("failed to close {}".format(app_name)) 0084 0085 def kill_process_by_name(self, app_name): 0086 """Kill a process using the least powerful signal that works.""" 0087 self.log.info("Killing {}".format(app_name)) 0088 for signal in ["SIGINT", "SIGQUIT", "SIGTERM", "SIGKILL"]: 0089 if subprocess.call(["killall", "-s", signal, app_name]) == 0: 0090 self.log.info("Killed {} using {}".format(app_name, signal)) 0091 return True 0092 0093 return False 0094 0095 def get_alter_name(self, app_name): 0096 if app_name == "console": 0097 return "konsole" 0098 0099 def create_skill(): 0100 return ApplicationLauncher()