Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Call arbitrary Android intents and get result back #1217

Merged
merged 9 commits into from
May 23, 2021
46 changes: 46 additions & 0 deletions src/android/toga_android/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@
from .libs.activity import IPythonApp, MainActivity
from .window import Window

import asyncio


# `MainWindow` is defined here in `app.py`, not `window.py`, to mollify the test suite.
class MainWindow(Window):
pass


class TogaApp(IPythonApp):
last_intent_requestcode = -1 # always increment before using it for invoking new Intents
running_intents = {} # dictionary for currently running Intents

def __init__(self, app):
super().__init__()
self._interface = app
Expand Down Expand Up @@ -39,6 +44,25 @@ def onDestroy(self):
def onRestart(self):
print("Toga app: onRestart")

def onActivityResult(self, requestCode, resultCode, resultData):
"""
Callback method, called from MainActivity when an Intent ends

:param int requestCode: The integer request code originally supplied to startActivityForResult(),
allowing you to identify who this result came from.
:param int resultCode: The integer result code returned by the child activity through its setResult().
:param Intent resultData: An Intent, which can return result data to the caller (various data can be attached
to Intent "extras").
"""
print("Toga app: onActivityResult, requestCode={0}, resultData={1}".format(requestCode, resultData))
try:
# remove Intent from the list of running Intents,
# and set the result of the intent.
result_future = self.running_intents.pop(requestCode)
result_future.set_result({"resultCode": resultCode, "resultData": resultData})
except KeyError:
print("No intent matching request code {requestCode}")

@property
def native(self):
# We access `MainActivity.singletonThis` freshly each time, rather than
Expand Down Expand Up @@ -92,3 +116,25 @@ def set_on_exit(self, value):

def add_background_task(self, handler):
self.loop.call_soon(wrapped_handler(self, handler), self)

async def intent_result(self, intent):
"""
Calls an Intent and waits for its result.

A RuntimeError will be raised when the Intent cannot be invoked.

:param Intent intent: The Intent to call
:returns: A Dictionary containing "resultCode" (int) and "resultData" (Intent or None)
:rtype: dict
"""
if intent.resolveActivity(self.native.getPackageManager()) is None:
raise RuntimeError('No appropriate Activity found to handle this intent.')
self._listener.last_intent_requestcode += 1
code = self._listener.last_intent_requestcode

result_future = asyncio.Future()
self._listener.running_intents[code] = result_future

self.native.startActivityForResult(intent, code)
await result_future
return result_future.result()
1 change: 1 addition & 0 deletions src/android/toga_android/libs/android.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from rubicon.java import JavaClass, JavaInterface

Color = JavaClass("android/graphics/Color")
Intent = JavaClass("android/content/Intent")
MotionEvent = JavaClass("android/view/MotionEvent")
PorterDuff__Mode = JavaClass("android/graphics/PorterDuff$Mode")
View__OnTouchListener = JavaInterface("android/view/View$OnTouchListener")