示例#1
1
def open_url(url):

    if url.startswith("file:"):
        raise Exception("Opening file urls is not supported: " + url)

    try:
        NSURL = autoclass('NSURL')
        UIApplication = autoclass("UIApplication")

        nsurl = NSURL.URLWithString_(objc_str(url))
        UIApplication.sharedApplication().openURL_(nsurl)
    except:
        import traceback
        traceback.print_exc()
        raise
示例#2
0
def ShareActivity(text, title=None):
    if not title:
        title = 'Share Via'
    if platform == 'android':
        try:
            from jnius import autoclass, cast
            AndroidString = autoclass('java.lang.String')
            Uri = autoclass('android.net.Uri')
            # start android intent stuff
            Intent = autoclass('android.content.Intent')
            shareIntent = Intent(Intent.ACTION_SEND)
            shareIntent.setType("text/plain")
            shareIntent.putExtra(Intent.EXTRA_TEXT, AndroidString(text))
            PythonActivity = autoclass('org.renpy.android.PythonActivity')
            theActivity = PythonActivity.mActivity
            chooser_title = cast('java.lang.CharSequence', AndroidString(title))
            theActivity.startActivity(Intent.createChooser(shareIntent, chooser_title))
        except:
            print 'Failed sharing text -- %s' % text
            pass
    elif platform == 'ios':
        from pyobjus import autoclass, objc_str
        ObjcClass = autoclass('ShareViewControllerINDR')
        o_instance = ObjcClass.alloc().init()
        if not title:
            title = ''
        o_instance.aTitle = objc_str(title)
        o_instance.aApp = objc_str('link')
        o_instance.aURL = objc_str(text)
        o_instance.shareImagePost()
    else:
        print 'Sharing: %s -- %s' % (title, text)
示例#3
0
    def run(self):
        panel = None
        if self.mode in ("open", "dir", "dir_and_files"):
            panel = NSOpenPanel.openPanel()

            panel.setCanChooseDirectories_(self.mode != "open")
            panel.setCanChooseFiles_(self.mode != "dir")

            if self.multiple:
                panel.setAllowsMultipleSelection_(True)
        elif self.mode == "save":
            panel = NSSavePanel.savePanel()
        else:
            assert False, self.mode

        panel.setCanCreateDirectories_(True)
        panel.setShowsHiddenFiles_(self.show_hidden)

        if self.title:
            panel.setTitle_(objc_str(self.title))

        # Mac OS X does not support wildcards unlike the other platforms.
        # This tries to convert wildcards to "extensions" when possible,
        # ans sets the panel to also allow other file types, just to be safe.
        if self.filters:
            filthies = []
            for f in self.filters:
                if type(f) == str:
                    f = (None, f)
                for s in f[1:]:
                    if not self.use_extensions:
                        if s.strip().endswith("*"):
                            continue
                    pystr = s.strip().split("*")[-1].split(".")[-1]
                    filthies.append(objc_str(pystr))

            ftypes_arr = objc_arr(*filthies)
            # todo: switch to allowedContentTypes
            panel.setAllowedFileTypes_(ftypes_arr)
            panel.setAllowsOtherFileTypes_(not self.use_extensions)

        if self.path:
            url = NSURL.fileURLWithPath_(self.path)
            panel.setDirectoryURL_(url)

        if panel.runModal():
            selection = None
            if self.mode == "save" or not self.multiple:
                selection = [panel.filename().UTF8String()]
            else:
                filename = panel.filenames()
                selection = [
                    filename.objectAtIndex_(x).UTF8String()
                    for x in range(filename.count())
                ]
            self._handle_selection(selection)
            return selection
        return None
示例#4
0
    def share_clicked(self, touch):
        from modules.core.android_utils import LogTestFairy
        action = touch.key
        Logger.info('ShareDialog: %s clicked' % action)
        LogTestFairy('Share post on %s' % action)
        from modules.core.tempstorage import get_temp_folder_prefix
        fname = get_temp_folder_prefix() + 'share.jpg'
        Logger.info('ShareDialog: Storing image to %s' % fname)
        try:
            self.item.texture.save(fname)
        except:
            Logger.info('ShareDialog: failed Storing %s' % fname)
            self.dispatch('on_close')
            return

        # make sure everyone can access it
        Logger.info('ShareDialog: Done Storing %s' % fname)

        provider = self.get_key_in_providers(action)

        from kivy import platform
        if platform == 'ios':
            from pyobjus import autoclass, objc_str
            ObjcClass = autoclass('ShareViewControllerINDR')
            self.o_instance = ObjcClass.alloc().init()
            self.o_instance.aTitle = objc_str(SHARE_LINK)
            self.o_instance.aFileName = objc_str(fname)
            self.o_instance.aApp = objc_str(action)
            self.o_instance.aURL = objc_str('http://insiderr.com')
            self.o_instance.shareImagePost()

        elif platform == 'android':
            from jnius import autoclass, cast
            AndroidString = autoclass('java.lang.String')
            Uri = autoclass('android.net.Uri')
            # start android intent stuff
            File = autoclass('java.io.File')
            sharedFile = File(fname)
            phototUri = Uri.fromFile(sharedFile)
            Intent = autoclass('android.content.Intent')
            shareIntent = Intent(Intent.ACTION_SEND)
            shareIntent.setType("image/*")
            shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            if provider:
                shareIntent.setPackage(provider)
            if 'facebook' in action:
                shareIntent.putExtra(Intent.EXTRA_TEXT, AndroidString("http://insiderr.com"))
            else:
                shareIntent.putExtra(Intent.EXTRA_TEXT, AndroidString(SHARE_LINK))
            shareIntent.putExtra("sms_body", AndroidString(SHARE_LINK))
            shareIntent.putExtra(Intent.EXTRA_STREAM, cast('android.os.Parcelable', phototUri))
            PythonActivity = autoclass('org.renpy.android.PythonActivity')
            theActivity = PythonActivity.mActivity
            chooser_title = cast('java.lang.CharSequence', AndroidString('Share Via'))
            theActivity.startActivity(Intent.createChooser(shareIntent, chooser_title))

        self.dispatch('on_close')
        LogTestFairy('Shared post successfully')
示例#5
0
文件: filechooser.py 项目: kivy/plyer
    def run(self):
        panel = None
        if self.mode in ("open", "dir"):
            panel = NSOpenPanel.openPanel()
        else:
            panel = NSSavePanel.savePanel()

        panel.setCanCreateDirectories_(True)

        panel.setCanChooseDirectories_(self.mode == "dir")
        panel.setCanChooseFiles_(self.mode != "dir")
        panel.setShowsHiddenFiles_(self.show_hidden)

        if self.title:
            panel.setTitle_(objc_str(self.title))

        if self.mode != "save" and self.multiple:
            panel.setAllowsMultipleSelection_(True)

        # Mac OS X does not support wildcards unlike the other platforms.
        # This tries to convert wildcards to "extensions" when possible,
        # ans sets the panel to also allow other file types, just to be safe.
        if len(self.filters) > 0:
            filthies = []
            for f in self.filters:
                if type(f) == str:
                    if not self.use_extensions:
                        if f.strip().endswith("*"):
                            continue
                        pystr = f.strip().split("*")[-1].split(".")[-1]
                    filthies.append(objc_str(pystr))
                else:
                    for _ in f[1:]:
                        if not self.use_extensions:
                            if f.strip().endswith("*"):
                                continue
                            pystr = f.strip().split("*")[-1].split(".")[-1]
                        filthies.append(objc_str(pystr))

            ftypes_arr = objc_arr(filthies)
            panel.setAllowedFileTypes_(ftypes_arr)
            panel.setAllowsOtherFileTypes_(not self.use_extensions)

        if self.path:
            url = NSURL.fileURLWithPath_(self.path)
            panel.setDirectoryURL_(url)

        if panel.runModal():
            selection = None
            if self.mode == "save" or not self.multiple:
                selection = [panel.filename().UTF8String()]
            else:
                selection = [i.UTF8String() for i in panel.filenames()]
            self._handle_selection(selection)
            return selection
        return None
示例#6
0
    def run(self):
        panel = None
        if self.mode in ("open", "dir"):
            panel = NSOpenPanel.openPanel()
        else:
            panel = NSSavePanel.savePanel()

        panel.setCanCreateDirectories_(True)

        panel.setCanChooseDirectories_(self.mode == "dir")
        panel.setCanChooseFiles_(self.mode != "dir")
        panel.setShowsHiddenFiles_(self.show_hidden)

        if self.title:
            panel.setTitle_(objc_str(self.title))

        if self.mode != "save" and self.multiple:
            panel.setAllowsMultipleSelection_(True)

        # Mac OS X does not support wildcards unlike the other platforms.
        # This tries to convert wildcards to "extensions" when possible,
        # ans sets the panel to also allow other file types, just to be safe.
        if len(self.filters) > 0:
            filthies = []
            for f in self.filters:
                if type(f) == str:
                    if not self.use_extensions:
                        if f.strip().endswith("*"):
                            continue
                        pystr = f.strip().split("*")[-1].split(".")[-1]
                    filthies.append(objc_str(pystr))
                else:
                    for i in f[1:]:
                        if not self.use_extensions:
                            if f.strip().endswith("*"):
                                continue
                            pystr = f.strip().split("*")[-1].split(".")[-1]
                        filthies.append(objc_str(pystr))

            ftypes_arr = objc_arr(filthies)
            panel.setAllowedFileTypes_(ftypes_arr)
            panel.setAllowsOtherFileTypes_(not self.use_extensions)

        if self.path:
            url = NSURL.fileURLWithPath_(self.path)
            panel.setDirectoryURL_(url)

        if panel.runModal_():
            if self.mode == "save" or not self.multiple:
                return [panel.filename().UTF8String()]
            else:
                return [i.UTF8String() for i in panel.filenames()]
        return None
示例#7
0
    def _notify(self, **kwargs):
        title = kwargs.get('title', '')
        message = kwargs.get('message', '')
        app_name = kwargs.get('app_name', '')
        # app_icon, timeout, ticker are not supported (yet)

        notification = NSUSERNOTIFICATION.alloc().init()
        notification.setTitle_(objc_str(title))
        notification.setSubtitle_(objc_str(app_name))
        notification.setInformativeText_(objc_str(message))

        usrnotifctr = NSUSERNOTIFICATIONCENTER.defaultUserNotificationCenter()
        usrnotifctr.setDelegate_(self)
        usrnotifctr.deliverNotification_(notification)
示例#8
0
    def _notify(self, **kwargs):
        title = kwargs.get('title', '')
        message = kwargs.get('message', '')
        app_name = kwargs.get('app_name', '')
        # app_icon, timeout, ticker are not supported (yet)

        notification = NSUserNotification.alloc().init()
        notification.setTitle_(objc_str(title))
        notification.setSubtitle_(objc_str(app_name))
        notification.setInformativeText_(objc_str(message))

        usrnotifctr = NSUserNotificationCenter.defaultUserNotificationCenter()
        usrnotifctr.setDelegate_(self)
        usrnotifctr.deliverNotification_(notification)
示例#9
0
 def test_dict(self):
     o_dict = objc_dict({
         'first_key': objc_i(2345),
         'second_key': objc_d(4.54)
     })
     self.assertEqual(
         o_dict.objectForKey_(objc_str('first_key')).intValue(), 2345)
示例#10
0
 def _get_key(self, servicename, key, **kwargs):
     ret = NSUserDefaults.standardUserDefaults().stringForKey_(
         objc_str(key))
     if ret is not None:
         return ret.UTF8String()
     else:
         return ret 
示例#11
0
def Toast(text, length_long=False):
    if platform == 'android':
        from android.runnable import run_on_ui_thread
        @run_on_ui_thread
        def ui_toaster(text, length_long=False):
            from jnius import autoclass, cast
            global ToastObject
            if not ToastObject:
                ToastObject = autoclass('android.widget.Toast')
            context = autoclass('org.renpy.android.PythonActivity').mActivity
            duration = ToastObject.LENGTH_LONG if length_long else ToastObject.LENGTH_SHORT
            String = autoclass('java.lang.String')
            c = cast('java.lang.CharSequence', String(text))
            t = ToastObject.makeText(context, c, duration)
            t.show()

        ui_toaster(text, length_long)
    elif platform == 'ios':
        global ToastObject
        from pyobjus import autoclass, objc_str
        if not ToastObject:
            oToasObject = autoclass('ToastINSD')
            ToastObject = oToasObject.alloc().init()
        ToastObject.aText = objc_str(text)
        ToastObject.showToastBar()

    else:
        print 'TOAST: %s' % text
示例#12
0
 def create_webview(self, *args):
     from pyobjus import autoclass, objc_str
     ObjcClass = autoclass('ObjcClassINSD')
     self.o_instance = ObjcClass.alloc().init()
     self.o_instance.aParam1 = objc_str(self._url)
     self.o_instance.openWebView()
     self._localserve.dispatch('on_webview')
示例#13
0
 def _get_key(self, servicename, key, **kwargs):
     ret = NSUserDefaults.standardUserDefaults().stringForKey_(
         objc_str(key))
     if ret is not None:
         return ret.UTF8String()
     else:
         return ret
示例#14
0
 def create_webview(self, *args):
     from pyobjus import autoclass, objc_str
     ObjcClass = autoclass('ObjcClassINSD')
     self.o_instance = ObjcClass.alloc().init()
     self.o_instance.aParam1 = objc_str(self._url)
     self.o_instance.openWebView()
     self._localserve.dispatch('on_webview')
示例#15
0
    def download_file(self, url):
        """ Download the specified file in place it in the destination. """
        NSURL = autoclass('NSURL')
        oc_url = NSURL.URLWithString_(objc_str(url))

        # Tasks are intialised in a paused state
        self.task = self.session.downloadTaskWithURL_(oc_url)
        self.task.resume()
示例#16
0
 def app_update_ios(self):
     from pyobjus import objc_str
     url = appix_itunes_url
     UIApplication = autoclass("UIApplication")
     NSURL = autoclass("NSURL")
     objc_str = objc_str
     nsurl = NSURL.alloc().initWithString_(objc_str(url))
     UIApplication.sharedApplication().openURL_(nsurl)
示例#17
0
文件: delegate.py 项目: kivy/pyobjus
    def request_connection(self):
        # This method request connection to an invalid URL so the
        # connection_didFailWithError_ protocol method will be triggered.
        url = NSURL.URLWithString_(objc_str("abc"))
        request = NSURLRequest.requestWithURL_(url)
        # Converts the Python delegate object to Objective C delegate instance
        # simply by calling the objc_delegate() function.
        connection = NSURLConnection.connectionWithRequest_delegate_(request, self)

        return connection
示例#18
0
 def request_connection(self):
     self.delegate_called = False
     # This method request connection to an invalid URL so the
     # connection_didFailWithError_ protocol method will be triggered.
     url = NSURL.URLWithString_(objc_str('abc'))
     request = NSURLRequest.requestWithURL_(url)
     # Converts the Python delegate object to Objective C delegate instance
     # simply by calling the objc_delegate() function.
     connection = NSURLConnection.connectionWithRequest_delegate_(
         request, self)
示例#19
0
    def _speak(self, **kwargs):
        message = kwargs.get('message')

        if (not self.voice):
            self._set_locale()

        utterance = \
            AVSpeechUtterance.speechUtteranceWithString_(objc_str(message))

        utterance.voice = self.voice
        self.synth.speakUtterance_(utterance)
示例#20
0
文件: tts.py 项目: 1060460048/plyer
    def _speak(self, **kwargs):
        message = kwargs.get('message')

        if(not self.voice):
            self._set_locale()

        utterance = \
            AVSpeechUtterance.speechUtteranceWithString_(objc_str(message))

        utterance.voice = self.voice
        self.synth.speakUtterance_(utterance)
    def _send(self, **kwargs):
        recipient = kwargs.get('recipient')
        subject = kwargs.get('subject')
        text = kwargs.get('text')

        uri = "mailto:"
        if recipient:
            uri += str(recipient)
        if subject:
            uri += "?" if not "?" in uri else "&"
            uri += "subject="
            uri += quote(str(subject))
        if text:
            uri += "?" if not "?" in uri else "&"
            uri += "body="
            uri += quote(str(text))

        nsurl = NSURL.alloc().initWithString_(objc_str(uri))

        UIApplication.sharedApplication().openURL_(nsurl)
示例#22
0
    def _send(self, **kwargs):
        '''
        This method provides sending messages to recipients.

        Expects 2 parameters in kwargs:
            - recipient: String type
            - message: String type

        Opens a mesage interface with recipient and message information.
        '''
        recipient = kwargs.get('recipient')
        message = kwargs.get('message')
        url = "sms:"
        if recipient:
            # Apple has not supported multiple recipients yet.
            url += str(recipient)
        if message:
            # Apple has to supported it yet.
            pass

        nsurl = NSURL.alloc().initWithString_(objc_str(url))
        UIApplication.sharedApplication().openURL_(nsurl)
示例#23
0
文件: sms.py 项目: kiok46/plyer
    def _send(self, **kwargs):
        """
        This method provides sending messages to recipients.

        Expects 2 parameters in kwargs:
            - recipient: String type
            - message: String type

        Opens a mesage interface with recipient and message information.
        """
        recipient = kwargs.get("recipient")
        message = kwargs.get("message")
        url = "sms:"
        if recipient:
            # Apple has not supported multiple recipients yet.
            url += str(recipient)
        if message:
            # Apple has to supported it yet.
            pass

        nsurl = NSURL.alloc().initWithString_(objc_str(url))
        UIApplication.sharedApplication().openURL_(nsurl)
示例#24
0
 def _set_key(self, servicename, key, value, **kwargs):
     NSUserDefaults.standardUserDefaults().setObject_forKey_(
         objc_str(value), objc_str(key))
示例#25
0
 def _set_locale(self, locale="en-US"):
     self.voice = AVSpeechSynthesisVoice.voiceWithLanguage_(
         objc_str(locale))
示例#26
0
    def _makecall(self, **kwargs):
        tel = kwargs.get('tel')
        url = "tel://" + tel
        nsurl = NSURL.alloc().initWithString_(objc_str(url))

        UIApplication.sharedApplication().openURL_(nsurl)
示例#27
0
 def _set_key(self, servicename, key, value, **kwargs):
     NSUserDefaults.standardUserDefaults().setObject_forKey_(
         objc_str(value), objc_str(key))
示例#28
0
    def share_clicked(self, touch):
        from modules.core.android_utils import LogTestFairy
        action = touch.key
        Logger.info('ShareDialog: %s clicked' % action)
        LogTestFairy('Share post on %s' % action)
        from modules.core.tempstorage import get_temp_folder_prefix
        fname = get_temp_folder_prefix() + 'share.jpg'
        Logger.info('ShareDialog: Storing image to %s' % fname)
        try:
            self.item.texture.save(fname)
        except:
            Logger.info('ShareDialog: failed Storing %s' % fname)
            self.dispatch('on_close')
            return

        # make sure everyone can access it
        Logger.info('ShareDialog: Done Storing %s' % fname)

        provider = self.get_key_in_providers(action)

        from kivy import platform
        if platform == 'ios':
            from pyobjus import autoclass, objc_str
            ObjcClass = autoclass('ShareViewControllerINDR')
            self.o_instance = ObjcClass.alloc().init()
            self.o_instance.aTitle = objc_str(SHARE_LINK)
            self.o_instance.aFileName = objc_str(fname)
            self.o_instance.aApp = objc_str(action)
            self.o_instance.aURL = objc_str('http://insiderr.com')
            self.o_instance.shareImagePost()

        elif platform == 'android':
            from jnius import autoclass, cast
            AndroidString = autoclass('java.lang.String')
            Uri = autoclass('android.net.Uri')
            # start android intent stuff
            File = autoclass('java.io.File')
            sharedFile = File(fname)
            phototUri = Uri.fromFile(sharedFile)
            Intent = autoclass('android.content.Intent')
            shareIntent = Intent(Intent.ACTION_SEND)
            shareIntent.setType("image/*")
            shareIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            if provider:
                shareIntent.setPackage(provider)
            if 'facebook' in action:
                shareIntent.putExtra(Intent.EXTRA_TEXT,
                                     AndroidString("http://insiderr.com"))
            else:
                shareIntent.putExtra(Intent.EXTRA_TEXT,
                                     AndroidString(SHARE_LINK))
            shareIntent.putExtra("sms_body", AndroidString(SHARE_LINK))
            shareIntent.putExtra(Intent.EXTRA_STREAM,
                                 cast('android.os.Parcelable', phototUri))
            PythonActivity = autoclass('org.renpy.android.PythonActivity')
            theActivity = PythonActivity.mActivity
            chooser_title = cast('java.lang.CharSequence',
                                 AndroidString('Share Via'))
            theActivity.startActivity(
                Intent.createChooser(shareIntent, chooser_title))

        self.dispatch('on_close')
        LogTestFairy('Shared post successfully')
示例#29
0
 def test_dict(self):
     o_dict = objc_dict({'first_key': objc_i(2345), 'second_key': objc_d(4.54)})
     self.assertEqual(o_dict.objectForKey_(objc_str('first_key')).intValue(), 2345)
示例#30
0
class BackgroundTransfer(object):
    """
    Main worker class for handling background transfers
    """
    identifier = objc_str('Kivy Background Transfer')

    def __init__(self):
        super(BackgroundTransfer, self).__init__()
        load_framework(INCLUDE.Foundation)

        # Load the configuration required for background sessions
        ns_config = autoclass('NSURLSessionConfiguration')
        self.config = ns_config.backgroundSessionConfigurationWithIdentifier_(
            self.identifier)

        # Load the session using the config and this class as the delegate
        session = autoclass('NSURLSession')
        self.session = session.sessionWithConfiguration_delegate_delegateQueue_(
            self.config, self, None)

        self.task = None
        # Note the policy restriction on HTTP as mentioned in the doc string
        # Use HTTPS to make you life easier :-) 
        self.download_file('http://kivy.org/logos/kivy-logo-black-256.png')

    def download_file(self, url):
        """ Download the specified file in place it in the destination. """
        NSURL = autoclass('NSURL')
        oc_url = NSURL.URLWithString_(objc_str(url))

        # Tasks are intialised in a paused state
        self.task = self.session.downloadTaskWithURL_(oc_url)
        self.task.resume()

    def close_session(self):
        """ Close the session. This is required to prevent memory leaks after
        all the downloads have completed.

        https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSession_class/#//apple_ref/occ/instm/NSURLSession/downloadTaskWithURL:
        """
        self.session.finishTasksAndInvalidate()

    @protocol('NSURLSessionDownloadDelegate')
    def URLSession_downloadTask_didWriteData_totalBytesWritten_totalBytesExpectedToWrite_(self, *args):
        Logger.info(
            "background_transfer.py: Protocol method "
            "URLSession_downloadTask_didWriteData_totalBytesWritten_"
            "totalBytesExpectedToWrite_ with {0}".format(args))

    @protocol('NSURLSessionDownloadDelegate')
    def URLSession_downloadTask_didFinishDownloadingToURL_(self, *args):
        Logger.info(
            "background_transfer.py: Protocol method "
            "URLSession_downloadTask_didFinishDownloadingToURL_ "
            "with {0}".format(args))
        if len(args) > 2:
            ns_url = args[2]
            Logger.info(
                'Downloaded file is {0}.\nYou need to move this before the '
                'function returns.'.format(ns_url.fileSystemRepresentation))
        self.close_session()

    @protocol('NSURLSessionDownloadDelegate')
    def URLSession_downloadTask_didResumeAtOffset_expectedTotalBytes_(self,
                                                                      *args):
        Logger.info(
            "background_transfer.py: Protocol method "
            "URLSession_downloadTask_didResumeAtOffset_expectedTotalBytes_"
            " with {0}".format(args))

    @protocol('NSURLSessionTaskDelegate')
    def URLSession_task_didCompleteWithError_(self, *args):
        """
        Although not technically part of the required delegate class, this
        delegate catches errors preventing the main delegate from functioning.
        """
        Logger.info(
            "background_transfer.py: Protocol method "
            "URLSession_task_didCompleteWithError_"
            "with {0}".format(args))

        if len(args) > 2:
            ns_err = args[2]
            if ns_err is not None:
                Logger.info('background_transfer: Error {}'.format(
                     ns_err.description().cString()))
示例#31
0
文件: tts.py 项目: 1060460048/plyer
 def _set_locale(self, locale="en-US"):
     self.voice = AVSpeechSynthesisVoice.voiceWithLanguage_(objc_str(locale))
示例#32
0
 def test_string(self):
     self.assertEqual(objc_str('some string').UTF8String(), 'some string')
示例#33
0
 def test_string(self):
     self.assertEqual(objc_str('some string').UTF8String(), 'some string')