Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
8b385ae
adds a basic `OfonoBridge` class to interact with Ofono through D-Bus
monsieurh Dec 20, 2017
e1a63fb
#50 restart ofono-bridge development
monsieurh Jan 7, 2018
f43ea67
#50 adds udev rule for SIM800L
monsieurh Jan 13, 2018
1994095
#50 WIP ofono-bridge
monsieurh Jan 13, 2018
0413ddf
Merge remote-tracking branch 'origin/devel' into ofono_bridge
monsieurh Jan 13, 2018
ff3d603
fixes duplicate entry in requirements.txt
monsieurh Jan 13, 2018
b5d29b0
adds some safeguards
monsieurh Jan 13, 2018
67c2baa
fixes logger import
monsieurh Jan 15, 2018
0966d0e
refactors '/sim900_0' as a member variable
monsieurh Jan 15, 2018
12d27f9
refactors the modem check as a method `_check_default_modem`
monsieurh Jan 15, 2018
41ff44c
updates `self._bus` hack explanation
monsieurh Jan 15, 2018
e1f101b
remove development leftover
monsieurh Jan 15, 2018
f085e7b
better exception handling
monsieurh Jan 15, 2018
2a084a6
replaces `print` by `logger.info`
monsieurh Jan 15, 2018
08dc9e8
now raises an exception when an interface is not found
monsieurh Jan 15, 2018
da2fb47
`ConversationManager` now recursively create log folder
monsieurh Jan 15, 2018
1650127
Merge branch 'devel' into ofono_bridge
CRImier Jun 15, 2018
0cf96f2
Adding a default log level - fixing tests
CRImier Jun 15, 2018
8e50fd7
Movong the ofono libraries into a separate library
CRImier Jun 15, 2018
01e72d3
WIP, debugging ofono
CRImier Jun 16, 2018
1b5c9e5
Latest screenshot are now shown first in screenshot listing app
CRImier Jun 17, 2018
248a30a
Merge branch 'devel' into ofono_bridge
CRImier Dec 19, 2018
54d548f
Thinking about architecture - initial changes
CRImier Dec 21, 2018
49a1ee1
Merge branch 'devel' into ofono_bridge
CRImier Jan 5, 2019
becaef5
Adding the BaseViewMixin, porting UI elements to use it
CRImier Jan 6, 2019
85e74ce
Fixing method's return type in declaration
CRImier Jan 6, 2019
2a40a20
Number input, overlays and value pagination work
CRImier Jan 6, 2019
47350dc
Adding BaseView class and porting everything to it, porting CharArrow…
CRImier Jan 6, 2019
917f7f6
Ported InputScreenView to use BaseView
CRImier Jan 6, 2019
44d6f44
Forgot a small thing
CRImier Jan 6, 2019
b04f185
Broke the launch process, fixing
CRImier Jan 6, 2019
9887af1
moving BaseViewMixin.__init__ - making it possible for the view to pr…
CRImier Jan 6, 2019
85cf72c
More testing and optimizations in the dialer
CRImier Jan 6, 2019
377c146
Adding BaseView functions for the usual character refresh, porting In…
CRImier Jan 6, 2019
1d29e7f
Moving overlays to use update_keymap
CRImier Jan 6, 2019
f92f021
Updating the overlay options for better button usage
CRImier Jan 6, 2019
378593e
Adding a bunch of functions to work with coordinates, adding tests, m…
CRImier Jan 6, 2019
c787dc3
Calculate FunctionOverlay labels depending on len of labels and not n…
CRImier Jan 6, 2019
ec17696
Numpad*Input now backgroundable
CRImier Jan 6, 2019
886a243
More phone app work - status screen ready
CRImier Jan 6, 2019
801fb09
Forgot to remove outdated test, adding
CRImier Jan 6, 2019
3a3500b
Making the Numpad*Input use the keymap for action buttons (as it shou…
CRImier Jan 6, 2019
65e450e
Accidentally changed context names, fixing the hardcoded exclusive co…
CRImier Jan 7, 2019
bb9e456
Adding "start context" function - for custom apps
CRImier Jan 7, 2019
5af4c3c
Merge branch 'devel' into ofono_bridge
CRImier May 20, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions 00-ofono.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
KERNEL=="ttyAMA0", ENV{OFONO_DRIVER}="sim900"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll be shipping this file in our own Debian package, most likely


12 changes: 9 additions & 3 deletions apps/personal/contacts/vcard_converter.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from helpers import setup_logger
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, why? I think you need to revert this particular change, which reverses our "setup_logger" integration work

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(as in, revert the logger-> logging replacements, not the from_string addition)

logger = setup_logger(__name__, "warning")

import vobject

from address_book import Contact
from helpers import setup_logger

logger = setup_logger(__name__, "info")


class VCardContactConverter(object):
Expand Down Expand Up @@ -52,3 +52,9 @@ def from_vcards(contact_card_files):
contacts += VCardContactConverter.parse_vcard_file(file_path)
logger.info("finished : {} contacts loaded", len(contacts))
return [VCardContactConverter.to_zpui_contact(c) for c in contacts]

@classmethod
def from_string(cls, vcard_string):
# type: (str) -> list
# Returns a list of ZPUI contacts from a string in vcard format
return [c for c in vobject.readComponents(vcard_string, ignoreUnreadable=True)]
55 changes: 55 additions & 0 deletions apps/phone/graphics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# This function allows to insert polygons as a list of points whose code is
# laid out in a shape somewhat similar to the polygon's shape. The problem
# with that is - order of points in the polygon list matters when drawing it,
# so we have to reorder the points - otherwise they'll become an untangled mess.
# So, that's what this function does - untangles the polygons according to
# a given mapping.

def untangle_points_by_mapping(points, mapping):
new_points = [p for p in points]
for dest, so in enumerate(mapping):
so = int(so)
new_points[dest] = points[so]
return new_points

# Graphics

arrow = ( # indices:
(3, 0), # 0
(0, 3),(2, 3),(4, 3),(6, 3), # 1 2 3 4
(2, 8),(4, 8) # 5 6
)

mapping = "0436521"

arrow = untangle_points_by_mapping(arrow, mapping)

cross = ( # indices:
(1, 0), (7, 0), # 0 1
(0, 1), (8, 1), # 2 3
(4, 3), # 4
(3, 4), (5, 4), # 5 6
(4, 5), # 7
(0, 7), (8, 7), # 8 9
(1, 8), (7, 8) # 10 11
)

mapping = (0, 4, 1, 3, 6, 9, 11, 7, 10, 8, 5, 2)

cross = untangle_points_by_mapping(cross, mapping)

phone_handset = (
(4, 0), # 0
(8, 1), # 1
(4, 3), # 2
(7, 4), # 3

(2, 11), # 4
(4, 12), # 5
(0, 13), # 6
(3, 15) # 7
)

mapping = "01324576"

phone_handset = untangle_points_by_mapping(phone_handset, mapping)
300 changes: 167 additions & 133 deletions apps/phone/main.py
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,137 +1,171 @@


from helpers import setup_logger

menu_name = "Phone"

from subprocess import call as os_call
from time import sleep
import traceback

from ui import Refresher, Menu, Printer, PrettyPrinter, DialogBox
from ui.experimental import NumberKeypadInputLayer
from helpers import BackgroundRunner, ExitHelper

from phone import Phone, Modem, ATError


logger = setup_logger(__name__, "warning")

i = None
o = None
init = None
phone = None

def answer():
#No context switching as for now, so all we can do is
#answer call is there's an active one
try:
phone.answer()
except ATError:
pass

def hangup():
try:
phone.hangup()
except ATError:
from apps import ZeroApp
from ui import Refresher, NumpadNumberInput, Canvas, MockOutput, FunctionOverlay, \
offset_points, get_bounds_for_points, expand_coords, multiply_points, check_coordinates, \
convert_flat_list_into_pairs as cflip
from ui.base_ui import BaseUIElement
from ui.base_view_ui import BaseView

import graphics
from views import InputScreenView


class InputScreen(NumpadNumberInput):

message = "Input number:"
default_pixel_view = "InputScreenView"
def __init__(self, i, o, *args, **kwargs):
kwargs["message"] = self.message
kwargs["value"] = "00000000000000000000000000000000000000000000000000000000012345"
NumpadNumberInput.__init__(self, i, o, *args, **kwargs)

def generate_views_dict(self):
d = NumpadNumberInput.generate_views_dict(self)
d.update({"InputScreenView":InputScreenView})
return d


class StatusScreen(Refresher):

arrow_x = 15
arrow_y = 40
handset_x = 2
handset_y = 10
arrow_offset = 2
counter = 0
number_frame = (10, 45, "-10", "-1")
number_height = 16
number_font = ("Fixedsys62.ttf", number_height)

def __init__(self, *args, **kwargs):
Refresher.__init__(self, self.show_status, *args, **kwargs)
self.c = Canvas(self.o)
self.prepare_number()
self.predraw_arrow()
self.predraw_cross()
self.predraw_handset()
self.status = {"number":"25250034", "accepted":True, "time":385}

def prepare_number(self):
frame_coords = check_coordinates(self.c, self.number_frame)
nw, nh = get_bounds_for_points(cflip(frame_coords))
self.number_c = Canvas(MockOutput(width=nw, height=nh, device_mode=self.o.device_mode))

def predraw_arrow(self):
aw, ah = get_bounds_for_points(graphics.arrow)
arrow_c = Canvas(MockOutput(width=aw, height=ah, device_mode=self.o.device_mode))
arrow_c.polygon(graphics.arrow, fill="white")
self.arrow_img = arrow_c.get_image()

def predraw_cross(self):
aw, ah = get_bounds_for_points(graphics.cross)
cross_c = Canvas(MockOutput(width=aw, height=ah, device_mode=self.o.device_mode))
cross_c.polygon(graphics.cross, fill="white")
self.cross_img = cross_c.get_image()

def predraw_handset(self):
handset = multiply_points(graphics.phone_handset, 2)
aw, ah = get_bounds_for_points(handset)
handset_c = Canvas(MockOutput(width=aw, height=ah, device_mode=self.o.device_mode))
handset_c.polygon(handset, fill="white")
self.handset_img = handset_c.get_image()

def draw_arrow(self, c, flipped=False):
coords = (self.arrow_x, self.arrow_y, self.arrow_x+self.arrow_img.width, self.arrow_y+self.arrow_img.height)
clear_coords = expand_coords(coords, self.arrow_offset)
c.clear(clear_coords)
img = self.arrow_img if not flipped else self.arrow_img.rotate(180)
c.image.paste(img, (self.arrow_x, self.arrow_y))

def draw_cross(self, c, flipped=False):
coords = (self.arrow_x, self.arrow_y, self.arrow_x+self.cross_img.width, self.arrow_y+self.cross_img.height)
clear_coords = expand_coords(coords, self.arrow_offset)
c.clear(clear_coords)
c.image.paste(self.cross_img, (self.arrow_x, self.arrow_y))

def draw_handset(self, c, flipped=False):
coords = (self.handset_x, self.handset_y, self.handset_x+self.handset_img.width, self.handset_y+self.handset_img.height)
c.image.paste(self.handset_img, (self.handset_x, self.handset_y))

def get_status(self):
states = ["incoming", "outgoing", "missed"]
self.status["state"] = states[self.counter]
self.counter += 1
if self.counter == 3: self.counter = 0
return self.status

def draw_state(self, c, status):
if status["state"] == "incoming":
self.draw_arrow(c, flipped=True)
elif status["state"] == "outgoing":
self.draw_arrow(c)
elif status["state"] == "missed":
self.draw_cross(c)

def draw_text_state(self, c, status):
if not status["accepted"]:
self.c.text(status["state"].capitalize(), (30, 9), font=(self.number_font[0], 16))
else:
status["time"] = status["time"] + 1
time_str = "{:02d}:{:02d}".format(*divmod(status["time"], 60))
self.c.text(time_str, (30, 9), font=(self.number_font[0], 16))

def draw_number(self, c, status):
self.number_c.clear()
self.number_c.centered_text(status["number"], font=self.number_font)
c.image.paste(self.number_c.image, self.number_frame[:2])
c.rectangle(self.number_frame)

def show_status(self):
self.c.clear()
status = self.get_status()
self.draw_number(self.c, status)
self.draw_handset(self.c)
self.draw_state(self.c, status)
self.draw_text_state(self.c, status)
return self.c.get_image()


class PhoneApp(ZeroApp):

menu_name = "Phone"

def __init__(self, *args, **kwargs):
ZeroApp.__init__(self, *args, **kwargs)
self.input_screen = InputScreen(self.i, self.o)
self.insc_overlay = FunctionOverlay({"KEY_F1":"deactivate", "KEY_F2":"backspace", "KEY_ENTER":self.insc_options}, labels=["Cancel", "Menu", "Bckspc"])
self.insc_overlay.apply_to(self.input_screen)
self.status_screen = StatusScreen(self.i, self.o, keymap={"KEY_ANSWER":self.accept_call, "KEY_HANGUP":self.reject_call})

def insc_options(self):
self.switch_to_status_screen()

def get_context(self, c):
self.context = c

def switch_to_input_screen(self, digit=None):
self.input_screen.activate()

def switch_to_status_screen(self, digit=None):
self.status_screen.activate()

def accept_call(self):
self.status_screen.status["accepted"] = True

def reject_call(self):
self.status_screen.status["accepted"] = False

def on_call(self):
self.c.request_switch()
self.show_call_status()

def get_call_status(self):
raise NotImplementedError

def show_call_status(self):
status = self.get_call_status()
pass

def phone_status():
data = []
status = phone.get_status()
callerid = {}
if status["state"] != "idle":
callerid = phone.get_caller_id()
for key, value in status.iteritems():
if value:
data.append("{}: {}".format(key, value))
for key, value in callerid.iteritems():
data.append("{}: {}".format(key, value))
return data

def call(number):
Printer("Calling {}".format(number), i, o, 0)
try:
phone.call(number)
except ATError as e:
PrettyPrinter("Calling fail! "+repr(e), i, o, 0)
logger.error("Function stopped executing")

def call_view():
keymap = {"KEY_ANSWER":[call, "value"]}
NumberKeypadInputLayer(i, o, "Call number", keymap, name="Phone call layer").activate()

def status_refresher():
Refresher(phone_status, i, o).activate()


def init_hardware():
try:
global phone
phone = Phone()
modem = Modem()
phone.attach_modem(modem)
except:
deinit_hardware()
raise

def deinit_hardware():
phone.detach_modem()

def wait_for_connection():
eh = ExitHelper(i).start()
while eh.do_run() and init.running and not init.failed:
sleep(1)
eh.stop()

def check_modem_connection():
if init.running:
return False
elif init.finished:
return True
elif init.failed:
raise Exception("Modem connection failed!")
else:
raise Exception("Phone app init runner is in invalid state! (never ran?)")

def init_app(input, output):
global i, o, init
i = input; o = output
i.set_maskable_callback("KEY_ANSWER", answer)
i.set_nonmaskable_callback("KEY_HANGUP", hangup)
try:
#This not good enough - either make the systemctl library system-wide or add more checks
os_call(["systemctl", "stop", "serial-getty@ttyAMA0.service"])
except Exception as e:
logger.exception(e)
init = BackgroundRunner(init_hardware)
init.run()

def offer_retry(counter):
do_reactivate = DialogBox("ync", i, o, message="Retry?").activate()
if do_reactivate:
PrettyPrinter("Connecting, try {}...".format(counter), i, o, 0)
init.reset()
init.run()
wait_for_connection()
callback(counter)

def callback(counter=0):
try:
counter += 1
status = check_modem_connection()
except:
if counter < 3:
PrettyPrinter("Modem connection failed =(", i, o)
offer_retry(counter)
else:
PrettyPrinter("Modem connection failed 3 times", i, o, 1)
else:
if not status:
PrettyPrinter("Connecting...", None, o, 0)
wait_for_connection()
callback(counter)
else:
contents = [["Status", status_refresher],
["Call", call_view]]
Menu(contents, i, o).activate()
def on_start(self):
self.switch_to_input_screen()
Loading