Commits

Peter Hansen committed 46b888c

add email sample, showing several ways of invoking the Hub email composer one
of which relies on the app.composeEmail() function from Tart which was added in
the previous commit

  • Participants
  • Parent commits a97fdda

Comments (0)

Files changed (8)

File samples/SendEmailSample/LICENSE

+The code in this sample is covered by the main
+BlackBerry-Python Project license (MIT).

File samples/SendEmailSample/app/__init__.py

+'''Entry point for Tart Application on the Python side.'''
+
+# blackberry_tart.py will import this module and execute App().start()
+from .app import App

File samples/SendEmailSample/app/app.py

+import os
+import ctypes as ct
+import json
+
+import tart
+
+from . import navigator_invoke as ni
+
+
+class App(tart.Application):
+    def __init__(self):
+        super().__init__(debug=False)   # set True for some extra debug output
+
+        with open('sharewith/pim/testfile.txt', 'w') as f:
+            f.write('Wheeee! This is my content.\n')
+
+        # install BPS event handler for navigator events
+        from . import navigator_handler
+        navigator_handler.NavigatorHandler(self.bps_dispatcher)
+
+
+    def onUiReady(self):
+        # These need to be absolute paths for the C++ approach (app.composeEmail)
+        # but for some reason not when we're directly invoking via Python.
+        self.paths = [
+            # os.path.abspath('shared/misc/testfile.txt'),
+            # os.path.abspath('/accounts/1000/shared/camera/IMG_00000043.png'),
+            os.path.abspath('sharewith/pim/testfile.txt'),
+            ]
+
+        tart.send('filePathUpdated', paths=self.paths)
+
+
+    def onGetHelp(self):
+        helppath = os.path.join(os.path.dirname(__file__), '../assets/help.html')
+        with open(helppath, encoding='utf-8') as f:
+            tart.send('gotHelp', text=f.read().strip().replace('\n', ' '))
+
+
+    invoke_id = 17
+
+    def onTestInvoke(self, data):
+        print('onTestInvoke', data)
+
+        invoke = self.invoke = ct.POINTER(ni.navigator_invoke_invocation_t)()
+        ni.navigator_invoke_invocation_create(ct.byref(invoke))
+
+        # set invocation properties
+        ni.navigator_invoke_invocation_set_action(invoke, b'bb.action.COMPOSE')
+        ni.navigator_invoke_invocation_set_type(invoke, b'message/rfc822')
+
+        # the id is not the same as actionId, and is retrieved with navigator_event_get_id()
+        self.invoke_id += 1
+        ni.navigator_invoke_invocation_set_id(invoke, str(self.invoke_id).encode('ascii'))
+
+        data = b'data:json:' + data.encode('utf8') + b'\n'
+        ni.navigator_invoke_invocation_set_data(invoke, data, len(data))
+
+        # invoke the target
+        ni.navigator_invoke_invocation_send(invoke)
+
+        # check id
+        invoke_id = ni.navigator_invoke_invocation_get_id(invoke)
+        print('sent invoke id', invoke_id)
+
+        # clean up resources
+        ni.navigator_invoke_invocation_destroy(invoke)
+
+
+# EOF

File samples/SendEmailSample/app/navigator_handler.py

+
+from bb import bps
+import tart
+
+from .navigator_invoke import *
+
+
+class NavigatorHandler:
+    def __init__(self, dispatcher):
+        rc = bps.navigator_request_events(0)
+        if rc == bps.BPS_FAILURE:
+            raise Exception('locale request events failed')
+
+        dispatcher.add_handler(bps.navigator_get_domain(), self.handle_event)
+
+
+    #---------------------------------------------
+    #
+    def handle_event(self, event):
+        '''Handle BPS events for our domain'''
+        code = bps.bps_event_get_code(event)
+
+        # print('NAV: {}, 0x{:x}'.format(code, code))
+
+        if code == bps.NAVIGATOR_INVOKE_TARGET:
+            print('NAVIGATOR_INVOKE_TARGET')
+
+            invoke = navigator_invoke_event_get_invocation(event)
+            print('invoke is', invoke)
+
+            invoke_id = navigator_invoke_invocation_get_id(invoke)
+            print('invoke id', invoke_id)
+
+            target = navigator_invoke_invocation_get_target(invoke)
+            print('target', target)
+
+            action = navigator_invoke_invocation_get_action(invoke)
+            print('action', action)
+
+        elif code == bps.NAVIGATOR_INVOKE_TARGET_RESULT:
+            print('NAVIGATOR_INVOKE_TARGET_RESULT')
+
+            # this retrieves the id set with navigator_invoke_invocation_set_id()
+            print('nav id', bps.navigator_event_get_id(event))
+
+            print('nav err', bps.navigator_event_get_err(event))
+
+
+            # print('target', navigator_invoke_event_get_target(event))
+            # print('target type', navigator_invoke_event_get_target_type(event))
+            # print('group id', navigator_invoke_event_get_group_id(event))
+            # print('dname', navigator_invoke_event_get_dname(event))
+
+
+
+# EOF

File samples/SendEmailSample/app/navigator_invoke.py

+'''Wrappers for libbps routines'''
+
+import ctypes
+from ctypes import (c_int, c_int64, c_char_p, c_void_p, c_size_t,
+    POINTER, Structure)
+
+from bb._wrap import _func, _register_funcs
+from bb.bps import _dll, bps_event_t
+
+
+#-------------------------------------
+class navigator_invoke_invocation_t(Structure):
+    _fields_ = []
+
+class navigator_invoke_query_t(Structure):
+    _fields_ = []
+
+class navigator_invoke_query_result_action_t(Structure):
+    _fields_ = []
+
+class navigator_invoke_query_result_target_t(Structure):
+    _fields_ = []
+
+class navigator_invoke_viewer_t(Structure):
+    _fields_ = []
+
+NAVIGATOR_INVOKE_TARGET_TYPE_UNSPECIFIED = 0x00
+NAVIGATOR_INVOKE_TARGET_TYPE_APPLICATION = 0x01
+NAVIGATOR_INVOKE_TARGET_TYPE_CARD         = 0x02
+NAVIGATOR_INVOKE_TARGET_TYPE_VIEWER      = 0x04
+NAVIGATOR_INVOKE_TARGET_TYPE_SERVICE     = 0x08
+NAVIGATOR_INVOKE_TARGET_TYPE_SELF        = 0x10
+navigator_invoke_target_type_t = c_int
+
+NAVIGATOR_INVOKE_QUERY_ACTION_TYPE_UNSPECIFIED = 0
+NAVIGATOR_INVOKE_QUERY_ACTION_TYPE_MENU        = 1
+NAVIGATOR_INVOKE_QUERY_ACTION_TYPE_ALL         = 2
+navigator_invoke_query_action_type_t = c_int
+
+NAVIGATOR_INVOKE_PERIMETER_TYPE_UNSPECIFIED = 0
+NAVIGATOR_INVOKE_PERIMETER_TYPE_PERSONAL    = 1
+NAVIGATOR_INVOKE_PERIMETER_TYPE_ENTERPRISE  = 2
+navigator_invoke_perimeter_type_t = c_int
+
+NAVIGATOR_INVOKE_FILE_TRANSFER_MODE_UNSPECIFIED = 0
+NAVIGATOR_INVOKE_FILE_TRANSFER_MODE_PRESERVE    = 1
+NAVIGATOR_INVOKE_FILE_TRANSFER_MODE_COPY_RO     = 2
+NAVIGATOR_INVOKE_FILE_TRANSFER_MODE_COPY_RW     = 3
+NAVIGATOR_INVOKE_FILE_TRANSFER_MODE_LINK        = 4
+navigator_invoke_file_transfer_mode_t = c_int
+
+navigator_invoke_invocation_create = _func(c_int, POINTER(POINTER(navigator_invoke_invocation_t)))
+navigator_invoke_invocation_destroy = _func(c_int, POINTER(navigator_invoke_invocation_t))
+navigator_invoke_invocation_set_id = _func(c_int, POINTER(navigator_invoke_invocation_t), c_char_p)
+navigator_invoke_invocation_set_target = _func(c_int, POINTER(navigator_invoke_invocation_t), c_char_p)
+navigator_invoke_invocation_set_source = _func(c_int, POINTER(navigator_invoke_invocation_t), c_char_p)
+navigator_invoke_invocation_set_action = _func(c_int, POINTER(navigator_invoke_invocation_t), c_char_p)
+navigator_invoke_invocation_set_type = _func(c_int, POINTER(navigator_invoke_invocation_t), c_char_p)
+navigator_invoke_invocation_set_uri = _func(c_int, POINTER(navigator_invoke_invocation_t), c_char_p)
+navigator_invoke_invocation_set_file_transfer_mode = _func(c_int, POINTER(navigator_invoke_invocation_t), navigator_invoke_file_transfer_mode_t)
+navigator_invoke_invocation_set_data = _func(c_int, POINTER(navigator_invoke_invocation_t), c_void_p, c_int)
+navigator_invoke_invocation_set_perimeter = _func(c_int, POINTER(navigator_invoke_invocation_t), navigator_invoke_perimeter_type_t)
+navigator_invoke_invocation_set_metadata = _func(c_int, POINTER(navigator_invoke_invocation_t), c_char_p)
+navigator_invoke_invocation_set_target_type_mask = _func(c_int, POINTER(navigator_invoke_invocation_t), c_int)
+navigator_invoke_invocation_get_id = _func(c_char_p, POINTER(navigator_invoke_invocation_t))
+navigator_invoke_invocation_get_target = _func(c_char_p, POINTER(navigator_invoke_invocation_t))
+navigator_invoke_invocation_get_source = _func(c_char_p, POINTER(navigator_invoke_invocation_t))
+navigator_invoke_invocation_get_action = _func(c_char_p, POINTER(navigator_invoke_invocation_t))
+navigator_invoke_invocation_get_type = _func(c_char_p, POINTER(navigator_invoke_invocation_t))
+navigator_invoke_invocation_get_uri = _func(c_char_p, POINTER(navigator_invoke_invocation_t))
+navigator_invoke_invocation_get_file_transfer_mode = _func(c_int, POINTER(navigator_invoke_invocation_t))
+navigator_invoke_invocation_get_data_length = _func(c_int, POINTER(navigator_invoke_invocation_t))
+navigator_invoke_invocation_get_data = _func(c_void_p, POINTER(navigator_invoke_invocation_t))
+navigator_invoke_invocation_get_perimeter = _func(navigator_invoke_perimeter_type_t, POINTER(navigator_invoke_invocation_t))
+navigator_invoke_invocation_get_metadata = _func(c_char_p, POINTER(navigator_invoke_invocation_t))
+navigator_invoke_invocation_get_target_type_mask = _func(c_int, POINTER(navigator_invoke_invocation_t))
+navigator_invoke_event_get_invocation = _func(POINTER(navigator_invoke_invocation_t), POINTER(bps_event_t))
+navigator_invoke_invocation_send = _func(c_int, POINTER(navigator_invoke_invocation_t))
+navigator_invoke_query_create = _func(c_int, POINTER(POINTER(navigator_invoke_query_t)))
+navigator_invoke_query_destroy = _func(c_int, POINTER(navigator_invoke_query_t))
+navigator_invoke_query_set_id = _func(c_int, POINTER(navigator_invoke_query_t), c_char_p)
+navigator_invoke_query_set_action = _func(c_int, POINTER(navigator_invoke_query_t), c_char_p)
+navigator_invoke_query_set_type = _func(c_int, POINTER(navigator_invoke_query_t), c_char_p)
+navigator_invoke_query_set_file_uri = _func(c_int, POINTER(navigator_invoke_query_t), c_char_p)
+navigator_invoke_query_set_target_type_mask = _func(c_int, POINTER(navigator_invoke_query_t), c_int)
+navigator_invoke_query_set_action_type = _func(c_int, POINTER(navigator_invoke_query_t), navigator_invoke_query_action_type_t)
+navigator_invoke_query_set_perimeter = _func(c_int, POINTER(navigator_invoke_query_t), navigator_invoke_perimeter_type_t)
+navigator_invoke_query_get_id = _func(c_char_p, POINTER(navigator_invoke_query_t))
+navigator_invoke_query_get_action = _func(c_char_p, POINTER(navigator_invoke_query_t))
+navigator_invoke_query_get_type = _func(c_char_p, POINTER(navigator_invoke_query_t))
+navigator_invoke_query_get_file_uri = _func(c_char_p, POINTER(navigator_invoke_query_t))
+navigator_invoke_query_get_target_type_mask = _func(c_int, POINTER(navigator_invoke_query_t))
+navigator_invoke_query_get_action_type = _func(navigator_invoke_query_action_type_t, POINTER(navigator_invoke_query_t))
+navigator_invoke_query_get_perimeter = _func(navigator_invoke_perimeter_type_t, POINTER(navigator_invoke_query_t))
+navigator_invoke_query_send = _func(c_int, POINTER(navigator_invoke_query_t))
+navigator_invoke_event_get_query_result_action_count = _func(c_int, POINTER(bps_event_t))
+navigator_invoke_event_get_query_result_action = _func(POINTER(navigator_invoke_query_result_action_t), POINTER(bps_event_t), c_int)
+navigator_invoke_query_result_action_get_name = _func(c_char_p, POINTER(navigator_invoke_query_result_action_t))
+navigator_invoke_query_result_action_get_icon = _func(c_char_p, POINTER(navigator_invoke_query_result_action_t))
+navigator_invoke_query_result_action_get_label = _func(c_char_p, POINTER(navigator_invoke_query_result_action_t))
+navigator_invoke_query_result_action_get_default_target = _func(c_char_p, POINTER(navigator_invoke_query_result_action_t))
+navigator_invoke_query_result_action_get_target_count = _func(c_int, POINTER(navigator_invoke_query_result_action_t))
+navigator_invoke_query_result_action_get_target = _func(POINTER(navigator_invoke_query_result_target_t), POINTER(navigator_invoke_query_result_action_t), c_int)
+navigator_invoke_query_result_target_get_key = _func(c_char_p, POINTER(navigator_invoke_query_result_target_t))
+navigator_invoke_query_result_target_get_icon = _func(c_char_p, POINTER(navigator_invoke_query_result_target_t))
+navigator_invoke_query_result_target_get_label = _func(c_char_p, POINTER(navigator_invoke_query_result_target_t))
+navigator_invoke_query_result_target_get_type = _func(navigator_invoke_target_type_t, POINTER(navigator_invoke_query_result_target_t))
+navigator_invoke_query_result_target_get_perimeter = _func(navigator_invoke_perimeter_type_t, POINTER(navigator_invoke_query_result_target_t))
+navigator_invoke_event_get_target = _func(c_char_p, POINTER(bps_event_t))
+navigator_invoke_event_get_target_type = _func(c_int, POINTER(bps_event_t))
+navigator_invoke_event_get_group_id = _func(c_int64, POINTER(bps_event_t))
+navigator_invoke_event_get_dname = _func(c_char_p, POINTER(bps_event_t))
+navigator_invoke_set_filters = _func(c_int, c_char_p, c_char_p,POINTER(c_char_p), c_size_t)
+navigator_invoke_get_filters = _func(c_int, c_char_p, c_char_p)
+navigator_invoke_event_get_filters_target = _func(c_char_p, POINTER(bps_event_t))
+navigator_invoke_event_get_filters_count = _func(c_int, POINTER(bps_event_t))
+navigator_invoke_event_get_filter = _func(c_char_p, POINTER(bps_event_t), c_int)
+navigator_invoke_uri_to_local_path = _func(c_char_p, c_char_p)
+navigator_invoke_local_path_to_uri = _func(c_char_p, c_char_p)
+
+
+#----------------------------
+# apply argtypes/restype to all functions
+#
+_register_funcs(_dll, globals())
+
+
+# EOF

File samples/SendEmailSample/assets/main.qml

+import bb.cascades 1.0
+import "tart.js" as Tart
+
+NavigationPane {
+    id: root
+
+    property variant to_list: ['BBPyProject@gmail.com']
+    property string subject: 'Test email from Send Email sample.'
+    property string body: 'Hello there, things are great here!'
+    property variant attachment_list: []
+
+    Page {
+        id: mainPage
+
+        Container {
+            verticalAlignment: VerticalAlignment.Center
+
+            Label {
+                id: label
+                horizontalAlignment: HorizontalAlignment.Center
+                multiline: true
+                text: qsTr("Send an email")
+                textStyle.fontSize: FontSize.PercentageValue
+                textStyle.fontSizeValue: 150
+                textStyle.textAlign: TextAlign.Center
+            }
+
+            Button {
+                horizontalAlignment: HorizontalAlignment.Center
+                enabled: attachment_list.length
+
+                text: qsTr("via Python")
+                onClicked: {
+                    Tart.send('testInvoke', {data: email.getData()});
+                }
+            }
+
+            Button {
+                horizontalAlignment: HorizontalAlignment.Center
+                enabled: attachment_list.length
+
+                text: qsTr("via C++")
+                onClicked: {
+                    app.composeEmail(query);
+                }
+            }
+        }
+
+        actions: [
+            // This is a simpler technique but it doesn't allow specifying
+            // all fields, and can't do attachments. There's an alternate
+            // form that can do attachments but no other fields.
+            InvokeActionItem {
+                title: qsTr("Send")
+                ActionBar.placement: ActionBarPlacement.OnBar
+                query {
+                    invokeActionId: 'bb.action.SENDEMAIL'
+                    invokeTargetId: 'sys.pim.uib.email.hybridcomposer'
+                    uri: 'mailto:BBPyProject@gmail.com'
+                }
+            }
+        ]
+    }
+
+    attachedObjects: [
+        InvokeQuery {
+            id: query
+            property bool ready: query.data != ''
+
+            invokeActionId: 'bb.action.COMPOSE'
+            mimeType: 'message/rfc822'
+
+            data: JSON.stringify({
+                to: to_list,
+                subject: subject,
+                body: body,
+                attachment: attachment_list
+            })
+
+            onDataChanged: {
+                print('new query data', data);
+            }
+        }
+        ,
+        QtObject {
+            id: email
+            property bool ready: false
+
+            function getData() {
+                return JSON.stringify({
+                    to: to_list,
+                    subject: subject,
+                    body: body,
+                    attachment: attachment_list
+                });
+            }
+        }
+    ]
+
+    function onFilePathUpdated(data) {
+        print('path updated', data.path);
+        attachment_list = data.paths;
+    }
+
+    onCreationCompleted: {
+        // Tart.debug = true;
+        Tart.init(_tart, Application);
+
+        Tart.register(root);
+
+        Tart.send('uiReady');
+    }
+}

File samples/SendEmailSample/icon.png

Added
New image

File samples/SendEmailSample/tart-project.ini

+[project]
+
+name: SendEmailSample
+title: BB-Tart SendEmail
+id: ca.microcode.bbtart.SendEmail
+version: 0.1.0
+permissions: access_pimdomain_messages
+
+[quickdeploy]
+    destination: SendEmailSample
+
+    filters:
+        include /app
+        include /assets
+        exclude /media