Skip to content

Commit

Permalink
Merge pull request #2075 from freakboy3742/audit-app
Browse files Browse the repository at this point in the history
[widget Audit] toga.App
  • Loading branch information
mhsmith authored Oct 31, 2023
2 parents 9021f91 + 207b440 commit 0b7b091
Show file tree
Hide file tree
Showing 168 changed files with 5,116 additions and 2,927 deletions.
57 changes: 3 additions & 54 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,8 @@ jobs:
# The $(ls ...) shell expansion is done in the Github environment;
# the value of TOGA_INSTALL_COMMAND will be a literal string,
# without any shell expansions to perform
TOGA_INSTALL_COMMAND="python -m pip install ../$(ls core/dist/toga_core-*.whl)[dev] ../$(ls dummy/dist/toga_dummy-*.whl)" tox -e py-core
cd core
mv .coverage .coverage.${{ matrix.platform }}.${{ matrix.python-version }}
TOGA_INSTALL_COMMAND="python -m pip install ../$(ls core/dist/toga_core-*.whl)[dev] ../$(ls dummy/dist/toga_dummy-*.whl)" tox -e py
mv core/.coverage core/.coverage.${{ matrix.platform }}.${{ matrix.python-version }}
- name: Store coverage data
uses: actions/upload-artifact@v3.1.3
with:
Expand Down Expand Up @@ -128,64 +127,14 @@ jobs:
cd core
python -m coverage combine
python -m coverage html --skip-covered --skip-empty
python -m coverage report --rcfile ../pyproject.toml # --fail-under=100
python -m coverage report --rcfile ../pyproject.toml --fail-under=100
- name: Upload HTML report if check failed.
uses: actions/upload-artifact@v3.1.3
with:
name: html-coverage-report
path: core/htmlcov
if: ${{ failure() }}

backend:
runs-on: ${{ matrix.runs-on }}
needs: [package, core]
strategy:
matrix:
backend: [ "android", "cocoa", "gtk", "iOS", "web", "winforms" ]
include:
- runs-on: ubuntu-latest
- python-version: "3.8" # Should be env.min_python_version (https://github.com/actions/runner/issues/480)
- pre-command:

- backend: cocoa
runs-on: macos-latest

- backend: gtk
pre-command: |
sudo apt update -y
sudo apt install -y pkg-config python3-dev libgirepository1.0-dev libcairo2-dev gir1.2-gtk-3.0
- backend: iOS
runs-on: macos-latest

- backend: winforms
runs-on: windows-latest
# Py3.9 is the first Python version for which
# a wheel of pythonnet isn't available on PyPI.
python-version: "3.9"
steps:
- uses: actions/checkout@v4.1.1
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4.7.1
with:
python-version: ${{ matrix.python-version }}
- name: Get packages
uses: actions/download-artifact@v3.0.2
with:
name: ${{ needs.package.outputs.artifact-name }}
- name: Install dev dependencies
run: |
${{ matrix.pre-command }}
# We don't actually want to install toga-core;
# we just want the dev extras so we have a known version of tox
python -m pip install ./core[dev]
- name: Test
run: |
# The $(ls ...) shell expansion is done in the Github environment;
# the value of TOGA_INSTALL_COMMAND will be a literal string,
# without any shell expansions to perform
TOGA_INSTALL_COMMAND="python -m pip install ../$(ls core/dist/toga_core-*.whl)[dev] ../$(ls dummy/dist/toga_dummy-*.whl) ../$(ls ${{ matrix.backend }}/dist/toga_${{ matrix.backend }}-*.whl)" tox -e py-${{ matrix.backend }}
testbed:
runs-on: ${{ matrix.runs-on }}
needs: core
Expand Down
159 changes: 94 additions & 65 deletions android/src/toga_android/app.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import asyncio
import sys

from android.graphics.drawable import BitmapDrawable
from android.media import RingtoneManager
from android.view import Menu, MenuItem
from java import dynamic_proxy
from org.beeware.android import IPythonApp, MainActivity

import toga
from android.graphics.drawable import Drawable
from android.media import RingtoneManager
from android.view import Menu, MenuItem
from toga.command import Group
from toga.command import GROUP_BREAK, SECTION_BREAK, Command, Group

from .libs import events
from .window import Window
Expand Down Expand Up @@ -41,18 +41,19 @@ def onResume(self):
print("Toga app: onResume")

def onPause(self):
print("Toga app: onPause")
print("Toga app: onPause") # pragma: no cover

def onStop(self):
print("Toga app: onStop")
print("Toga app: onStop") # pragma: no cover

def onDestroy(self):
print("Toga app: onDestroy")
print("Toga app: onDestroy") # pragma: no cover

def onRestart(self):
print("Toga app: onRestart")
print("Toga app: onRestart") # pragma: no cover

def onActivityResult(self, requestCode, resultCode, resultData):
# TODO #1798: document and test this somehow
def onActivityResult(self, requestCode, resultCode, resultData): # pragma: no cover
"""Callback method, called from MainActivity when an Intent ends.
:param int requestCode: The integer request code originally supplied to startActivityForResult(),
Expand All @@ -75,94 +76,86 @@ def onActivityResult(self, requestCode, resultCode, resultData):
print("No intent matching request code {requestCode}")

def onConfigurationChanged(self, new_config):
pass
pass # pragma: no cover

def onOptionsItemSelected(self, menuitem):
consumed = False
try:
cmd = self.menuitem_mapping[menuitem.getItemId()]
consumed = True
if cmd.action is not None:
cmd.action(menuitem)
except KeyError:
print("menu item id not found in menuitem_mapping dictionary!")
return consumed
itemid = menuitem.getItemId()
if itemid == Menu.NONE:
# This method also fires when opening submenus
return False
else:
self.menuitem_mapping[itemid].action(None)
return True

def onPrepareOptionsMenu(self, menu):
menu.clear()
itemid = 0
itemid = 1 # 0 is the same as Menu.NONE.
groupid = 1
menulist = {} # dictionary with all menus
self.menuitem_mapping.clear()

# create option menu
for cmd in self._impl.interface.commands:
if cmd == toga.SECTION_BREAK or cmd == toga.GROUP_BREAK:
if cmd == SECTION_BREAK or cmd == GROUP_BREAK:
groupid += 1
continue
if cmd in self._impl.interface.main_window.toolbar:
continue # do not show toolbar commands in the option menu (except when overflowing)

grouppath = cmd.group.path
if grouppath[0] != Group.COMMANDS:
# only the Commands group (and its subgroups) are supported
# other groups should eventually go into the navigation drawer
# Toolbar commands are added below.
if cmd in self._impl.interface.main_window.toolbar:
continue

if cmd.group.key in menulist:
menugroup = menulist[cmd.group.key]
else:
# create all missing submenus
parentmenu = menu
for group in grouppath:
groupkey = group.key
groupkey = ()
for section, order, text in cmd.group.key:
groupkey += ((section, order, text),)
if groupkey in menulist:
menugroup = menulist[groupkey]
else:
if group.text == toga.Group.COMMANDS.text:
if len(groupkey) == 1 and text == Group.COMMANDS.text:
# Add this group directly to the top-level menu
menulist[groupkey] = menu
menugroup = menu
else:
itemid += 1
order = Menu.NONE if group.order is None else group.order
# Add all other groups as submenus
menugroup = parentmenu.addSubMenu(
Menu.NONE, itemid, order, group.text
) # groupId, itemId, order, title
groupid, Menu.NONE, Menu.NONE, text
)
menulist[groupkey] = menugroup
parentmenu = menugroup

# create menu item
itemid += 1
order = Menu.NONE if cmd.order is None else cmd.order
menuitem = menugroup.add(
Menu.NONE, itemid, order, cmd.text
) # groupId, itemId, order, title
menuitem = menugroup.add(groupid, itemid, Menu.NONE, cmd.text)
menuitem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_NEVER)
menuitem.setEnabled(cmd.enabled)
self.menuitem_mapping[
itemid
] = cmd # store itemid for use in onOptionsItemSelected
self.menuitem_mapping[itemid] = cmd
itemid += 1

# create toolbar actions
if self._impl.interface.main_window:
if self._impl.interface.main_window: # pragma: no branch
for cmd in self._impl.interface.main_window.toolbar:
if cmd == toga.SECTION_BREAK or cmd == toga.GROUP_BREAK:
if cmd == SECTION_BREAK or cmd == GROUP_BREAK:
groupid += 1
continue
itemid += 1
order = Menu.NONE if cmd.order is None else cmd.order
menuitem = menu.add(
Menu.NONE, itemid, order, cmd.text
) # groupId, itemId, order, title
menuitem.setShowAsActionFlags(
MenuItem.SHOW_AS_ACTION_IF_ROOM
) # toolbar button / item in options menu on overflow

menuitem = menu.add(groupid, itemid, Menu.NONE, cmd.text)
# SHOW_AS_ACTION_IF_ROOM is too conservative, showing only 2 items on
# a medium-size screen in portrait.
menuitem.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS)
menuitem.setEnabled(cmd.enabled)
if cmd.icon:
icon = Drawable.createFromPath(str(cmd.icon._impl.path))
if icon:
menuitem.setIcon(icon)
else:
print("Could not create icon: " + str(cmd.icon._impl.path))
self.menuitem_mapping[
itemid
] = cmd # store itemid for use in onOptionsItemSelected
menuitem.setIcon(
BitmapDrawable(
self.native.getResources(), cmd.icon._impl.native
)
)
self.menuitem_mapping[itemid] = cmd
itemid += 1

# Display the menu.
return True


Expand All @@ -185,8 +178,17 @@ def create(self):
# Call user code to populate the main window
self.interface._startup()

def open_document(self, fileURL):
print("Can't open document %s (yet)" % fileURL)
self.interface.commands.add(
# About should be the last item in the menu, in a section on its own.
Command(
lambda _: self.interface.about(),
f"About {self.interface.formal_name}",
section=sys.maxsize,
),
)

def create_menus(self):
self.native.invalidateOptionsMenu() # Triggers onPrepareOptionsMenu

def main_loop(self):
# In order to support user asyncio code, start the Python/Android cooperative event loop.
Expand All @@ -200,7 +202,21 @@ def set_main_window(self, window):
pass

def show_about_dialog(self):
self.interface.factory.not_implemented("App.show_about_dialog()")
message_parts = []
if self.interface.version is not None:
message_parts.append(
f"{self.interface.formal_name} v{self.interface.version}"
)
else:
message_parts.append(self.interface.formal_name)

if self.interface.author is not None:
message_parts.append(f"Author: {self.interface.author}")
if self.interface.description is not None:
message_parts.append(f"\n{self.interface.description}")
self.interface.main_window.info_dialog(
f"About {self.interface.formal_name}", "\n".join(message_parts)
)

def beep(self):
uri = RingtoneManager.getActualDefaultRingtoneUri(
Expand All @@ -210,9 +226,16 @@ def beep(self):
ringtone.play()

def exit(self):
pass # pragma: no cover

def get_current_window(self):
return self.interface.main_window._impl

def set_current_window(self, window):
pass

async def intent_result(self, intent):
# TODO #1798: document and test this somehow
async def intent_result(self, intent): # pragma: no cover
"""Calls an Intent and waits for its result.
A RuntimeError will be raised when the Intent cannot be invoked.
Expand All @@ -234,6 +257,12 @@ async def intent_result(self, intent):
except AttributeError:
raise RuntimeError("No appropriate Activity found to handle this intent.")

def enter_full_screen(self, windows):
pass

def exit_full_screen(self, windows):
pass

def hide_cursor(self):
pass

Expand Down
5 changes: 1 addition & 4 deletions android/src/toga_android/colors.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from travertino.colors import NAMED_COLOR, TRANSPARENT

from android.graphics import Color
from travertino.colors import TRANSPARENT

CACHE = {TRANSPARENT: Color.TRANSPARENT}

Expand All @@ -9,8 +8,6 @@ def native_color(c):
try:
color = CACHE[c]
except KeyError:
if isinstance(c, str):
c = NAMED_COLOR[c]
color = Color.argb(
int(c.rgba.a * 255), int(c.rgba.r), int(c.rgba.g), int(c.rgba.b)
)
Expand Down
5 changes: 4 additions & 1 deletion android/src/toga_android/command.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from org.beeware.android import MainActivity


class Command:
def __init__(self, interface):
self.interface = interface
self.native = []

def set_enabled(self, value):
pass
MainActivity.singletonThis.invalidateOptionsMenu()
3 changes: 1 addition & 2 deletions android/src/toga_android/dialogs.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from abc import ABC

from java import dynamic_proxy

from android import R
from android.app import AlertDialog
from android.content import DialogInterface
from java import dynamic_proxy


class OnClickListener(dynamic_proxy(DialogInterface.OnClickListener)):
Expand Down
4 changes: 2 additions & 2 deletions android/src/toga_android/fonts.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from pathlib import Path

from org.beeware.android import MainActivity

from android import R
from android.graphics import Typeface
from android.util import TypedValue
from org.beeware.android import MainActivity

from toga.fonts import (
_REGISTERED_FONT_CACHE,
BOLD,
Expand Down
3 changes: 1 addition & 2 deletions android/src/toga_android/images.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from pathlib import Path

from java.io import FileOutputStream

from android.graphics import Bitmap, BitmapFactory
from java.io import FileOutputStream


class Image:
Expand Down
Loading

0 comments on commit 0b7b091

Please sign in to comment.