changeset 2089:0931b5a6213c

core, quick_frontends: android compatibility hacks: a couple of hacks have been used so backend can be launched on android using kivy - config file (sat.conf) is been read on a specific dir, using a detection of Cagou domains in constants.py - fixed crash when using sys.stdout.isatty in log_config.py - Q&D fix for logging encoding issue on android - when android is detected, the path/pattern to load plugins is modified - SRV is not working at the moment on android. If the platform is detected, the host is used directly with default port. A hosts_dir configuration can be used in [android] section to map a specific ip to a host - getRepositoryData in tools/utils return constant string on android. Proper repository data need to be copied during package building. - [quick app] more robust failure handling on asyncConnect error - [quick chat] default to utf-8 when getlocale doesn't return the actual locale.
author Goffi <goffi@goffi.org>
date Sun, 04 Dec 2016 18:16:48 +0100
parents c02f96756d5c
children 52bd463e6fe7
files frontends/src/quick_frontend/quick_app.py frontends/src/quick_frontend/quick_chat.py src/core/constants.py src/core/log_config.py src/core/sat_main.py src/core/xmpp.py src/tools/utils.py
diffstat 7 files changed, 79 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/frontends/src/quick_frontend/quick_app.py	Sun Dec 04 18:16:37 2016 +0100
+++ b/frontends/src/quick_frontend/quick_app.py	Sun Dec 04 18:16:48 2016 +0100
@@ -402,10 +402,22 @@
         if not errback:
             def errback(failure):
                 log.error(_(u"Can't connect profile [%s]") % failure)
-                if failure.module.startswith('twisted.words.protocols.jabber') and failure.condition == "not-authorized":
+                try:
+                    module = failure.module
+                except AttributeError:
+                    module = ''
+                try:
+                    message = failure.message
+                except AttributeError:
+                    message = 'error'
+                try:
+                    fullname = failure.fullname
+                except AttributeError:
+                    fullname = 'error'
+                if module.startswith('twisted.words.protocols.jabber') and failure.condition == "not-authorized":
                     self.launchAction(C.CHANGE_XMPP_PASSWD_ID, {}, profile=profile)
                 else:
-                    self.showDialog(failure.message, failure.fullname, 'error')
+                    self.showDialog(message, fullname, 'error')
         self.bridge.asyncConnect(profile, callback=callback, errback=errback)
 
     def plug_profiles(self, profiles):
--- a/frontends/src/quick_frontend/quick_chat.py	Sun Dec 04 18:16:37 2016 +0100
+++ b/frontends/src/quick_frontend/quick_chat.py	Sun Dec 04 18:16:48 2016 +0100
@@ -122,7 +122,7 @@
         # if the message was sent before today, we print the full date
         timestamp = time.localtime(self.timestamp)
         time_format = u"%c" if timestamp < self.parent.day_change else u"%H:%M"
-        return time.strftime(time_format, timestamp).decode(getlocale()[1])
+        return time.strftime(time_format, timestamp).decode(getlocale()[1] or 'utf-8')
 
     @property
     def avatar(self):
@@ -470,7 +470,7 @@
             self._onHistoryPrinted()
 
         def _historyGetEb(err):
-            log.error(_(u"Can't get history"))
+            log.error(_(u"Can't get history: {}").format(err))
             self._onHistoryPrinted()
 
         self.host.bridge.historyGet(unicode(self.host.profiles[profile].whoami.bare), unicode(target), size, True, filters, profile, callback=_historyGetCb, errback=_historyGetEb)
--- a/src/core/constants.py	Sun Dec 04 18:16:37 2016 +0100
+++ b/src/core/constants.py	Sun Dec 04 18:16:48 2016 +0100
@@ -132,20 +132,32 @@
 
     ## Configuration ##
     if BaseDirectory:  # skipped when xdg module is not available (should not happen in backend)
+        if "org.goffi.cagou.cagou" in BaseDirectory.__file__:
+            # FIXME: hack to make config read from the right location on Android
+            # TODO: fix it in a more proper way
+            BaseDirectory = None
+            DEFAULT_CONFIG = {
+                'local_dir': '/data/data/org.goffi.cagou.cagou/',
+                'media_dir': '/data/data/org.goffi.cagou.cagou/files/media',
+                'pid_dir': '%(local_dir)s',
+                'log_dir': '%(local_dir)s',
+            }
+            CONFIG_FILES = ['/data/data/org.goffi.cagou.cagou/files/' + APP_NAME_FILE + '.conf']
+        else:
 
-        ## Configuration ##
-        DEFAULT_CONFIG = {
-            'media_dir': '/usr/share/' + APP_NAME_FILE + '/media',
-            'local_dir': BaseDirectory.save_data_path(APP_NAME_FILE),
-            'pid_dir': '%(local_dir)s',
-            'log_dir': '%(local_dir)s',
-        }
+            ## Configuration ##
+            DEFAULT_CONFIG = {
+                'media_dir': '/usr/share/' + APP_NAME_FILE + '/media',
+                'local_dir': BaseDirectory.save_data_path(APP_NAME_FILE),
+                'pid_dir': '%(local_dir)s',
+                'log_dir': '%(local_dir)s',
+            }
 
-        # List of the configuration filenames sorted by ascending priority
-        CONFIG_FILES = [realpath(expanduser(path) + APP_NAME_FILE + '.conf') for path in
-                        ['/etc/', '~/', '~/.', '', '.'] +
-                        ['%s/' % path for path in list(BaseDirectory.load_config_paths(APP_NAME_FILE))]
-                       ]
+            # List of the configuration filenames sorted by ascending priority
+            CONFIG_FILES = [realpath(expanduser(path) + APP_NAME_FILE + '.conf') for path in
+                            ['/etc/', '~/', '~/.', '', '.'] +
+                            ['%s/' % path for path in list(BaseDirectory.load_config_paths(APP_NAME_FILE))]
+                           ]
 
 
     ## Plugins ##
--- a/src/core/log_config.py	Sun Dec 04 18:16:37 2016 +0100
+++ b/src/core/log_config.py	Sun Dec 04 18:16:48 2016 +0100
@@ -47,7 +47,11 @@
         super(ConfigureBasic, self).configureColors(colors, force_colors, levels_taints_dict)
         if colors:
             import sys
-            if force_colors or sys.stdout.isatty(): # FIXME: isatty should be tested on each handler, not globaly
+            try:
+                isatty = sys.stdout.isatty()
+            except AttributeError:
+                isatty = False
+            if force_colors or isatty: # FIXME: isatty should be tested on each handler, not globaly
                 # we need colors
                 log.Logger.post_treat = lambda logger, level, message: self.ansiColors(level, message)
         elif force_colors:
@@ -262,6 +266,7 @@
     def configureFormat(self, fmt):
         super(ConfigureStandard, self).configureFormat(fmt)
         import logging
+        import sys
 
         class SatFormatter(logging.Formatter):
             u"""Formatter which manage SàT specificities"""
@@ -287,7 +292,12 @@
                 s = super(SatFormatter, self).format(record)
                 if do_color:
                     s = ConfigureStandard.ansiColors(record.levelname, s)
-                return s
+                if sys.platform == "android":
+                    # FIXME: dirty hack to workaround android encoding issue on log
+                    #        need to be fixed properly
+                    return s.encode('ascii', 'ignore')
+                else:
+                    return s
 
         self.formatterClass = SatFormatter
 
--- a/src/core/sat_main.py	Sun Dec 04 18:16:37 2016 +0100
+++ b/src/core/sat_main.py	Sun Dec 04 18:16:48 2016 +0100
@@ -150,13 +150,20 @@
         ui_contact_list.ContactList(self)
         ui_profile_manager.ProfileManager(self)
         self.initialised.callback(None)
-        log.info(_("Backend is ready"))
+        log.info(_(u"Backend is ready"))
 
     def _import_plugins(self):
         """Import all plugins found in plugins directory"""
-        import sat.plugins
-        plugins_path = os.path.dirname(sat.plugins.__file__)
-        plug_lst = [os.path.splitext(plugin)[0] for plugin in map(os.path.basename, glob(os.path.join(plugins_path, "plugin*.py")))]
+        if sys.platform == "android":
+            # FIXME: android hack to load plugins from proper path
+            # TODO: move this in a separate module and/or use sat.conf to specify path
+            plugins_path = "/data/data/org.goffi.cagou.cagou/files/sat/plugins"
+            plugin_glob = "plugin*.pyo"
+        else:
+            import sat.plugins
+            plugins_path = os.path.dirname(sat.plugins.__file__)
+            plugin_glob = "plugin*.py"
+        plug_lst = [os.path.splitext(plugin)[0] for plugin in map(os.path.basename, glob(os.path.join(plugins_path, plugin_glob)))]
         plugins_to_import = {}  # plugins we still have to import
         for plug in plug_lst:
             plugin_path = 'sat.plugins.' + plug
--- a/src/core/xmpp.py	Sun Dec 04 18:16:37 2016 +0100
+++ b/src/core/xmpp.py	Sun Dec 04 18:16:48 2016 +0100
@@ -34,6 +34,7 @@
 import time
 import calendar
 import uuid
+import sys
 
 
 class SatXMPPClient(wokkel_client.XMPPClient):
@@ -42,6 +43,17 @@
     def __init__(self, host_app, profile, user_jid, password, host=None, port=C.XMPP_C2S_PORT, max_retries=C.XMPP_MAX_RETRIES):
         # XXX: DNS SRV records are checked when the host is not specified.
         # If no SRV record is found, the host is directly extracted from the JID.
+        if sys.platform == "android":
+            # FIXME: temporary hack as SRV is not working on android
+            # TODO: remove this hack and fix SRV
+            log.info(u"FIXME: Android hack, ignoring SRV")
+            host = user_jid.host
+            hosts_map = host_app.memory.getConfig("android", "hosts_dict", {})
+            if host in hosts_map:
+                log.info(u"using {host_to_use} for host {host_ori} as requested in config".format(
+                    host_ori = host,
+                    host_to_use = hosts_map[host]))
+                host = hosts_map[host]
         wokkel_client.XMPPClient.__init__(self, user_jid, password, host or None, port or C.XMPP_C2S_PORT)
         self.factory.clientConnectionLost = self.connectionLost
         self.factory.maxRetries = max_retries
--- a/src/tools/utils.py	Sun Dec 04 18:16:37 2016 +0100
+++ b/src/tools/utils.py	Sun Dec 04 18:16:48 2016 +0100
@@ -25,6 +25,7 @@
 log = getLogger(__name__)
 import datetime
 import time
+import sys
 
 
 def clean_ustr(ustr):
@@ -78,6 +79,9 @@
             - date: ISO 8601 format date
             - tag: latest tag used in hierarchie
     """
+    if sys.platform == "android":
+        # FIXME: workaround to avoid trouble on android, need to be fixed properly
+        return u"Cagou android build"
     from distutils.spawn import find_executable
     import subprocess
     KEYS=("node", "node_short", "branch", "date", "tag")