more work on send/receive, added screenshots and more readme

This commit is contained in:
Hemish 2023-03-30 12:16:31 +05:30
parent c967d47010
commit 060c08f85f
10 changed files with 132 additions and 37 deletions

View File

@ -1,25 +1,37 @@
# Pe
![Main View](/screenshots/main_view.png?raw=true "Main View")
![Pin Entry Dialog](/screenshots/pin_entry.png?raw=true "Pin Entry Dialog")
![Transaction View](/screenshots/transaction_view.png?raw=true "Transaction View")
Pe is a [libadwaita](https://https://gitlab.gnome.org/GNOME/libadwaita) and [ModemManager](https://www.freedesktop.org/wiki/Software/ModemManager/) based UPI client for Linux Desktops and Linux Mobile phones, written in Python.
# Backend
This client utilises ```*99#``` service publically available on Airtel, Vi, BSNL (not Jio), provided by NCPI.
As it uses the ```*99#``` USSD service, it requires a modem connected. While linux phones already have built-in modem, some laptops also have inbuilt WAN or SIM ports. You may also connect Linux-compatible modems to your computer.
It is prone to failing, because of the nature of USSD service, and network strengths.
# Disclaimer
The developer is not reponsible for any loss of money or any complications. So, try on your own risk.
Pe provides two binaries ```pe``` and ```pe-cli```. By default, launnching the app through flatpak would launch pe which is the graphical app. But you can optionally run the ```pe-cli``` binary, which provides a command line interface.
As the app is currently in development, I have not implemented a lot of error handling. The app would not even start in absence of modem, if built with normal profile. There exists a testing profile, which does a dry run, and does not utilise any modem, and is there only for testing the GUI.
# How to use
Pe provides two binaries ```pe``` and ```pe-cli```. By default, launnching the app through flatpak would launch pe which is the graphical app. But you can optionally run the ```pe-cli``` binary, which provides a command line interface..
# Additional system requirements
As it uses the ```*99#``` USSD service, it requires a modem connected. While linux phones already have built-in modem, some laptops also have inbuilt WAN or SIM ports. You may also connect Linux-compatible modems to your computer.
ModemManager service is already installed and enabled by default in distros like Ubuntu, Fedora. But, it may not be installed in other distros. You need to install ModemManager and enable it in your distro (that doesn't always require systemd, if you are using an init system other than systemd, you can still launch the service through your respective init system).
# Development notes
It was GTK4/Libadwaita learning project for me, so I have fairly documented the GUI code.
As the app is currently in development, I have not implemented a lot of error handling. The app would not even start in absence of modem, if built with normal profile. There exists a testing profile, which does a dry run, and does not utilise any modem, and is there only for testing the GUI
The GUI code is separated from backend code. So, in case in future, we come forward to use some backend other than USSD, just utils.py can be replaced to provide the same functions.
# How to run
The most easy way to run this app, is using GNOME Builder, or using Flatpak extension for VS Code or VSCodium. I have provided two flatpak manifests, one for normal profile and one for testing profile.
Though you can also build the project locally, without Flatpak by:
@ -32,9 +44,25 @@ sudo meson install
(testing mode)
```meson build
```
meson build
cd build
sudo meson install
```
(normal mode)
(normal mode)
# What works currently in GUI
- Fetching balance
# What works currently in CLI
- Completing a requested payments
- Fetching a payment request
- Creating a payment request to UPI ID, phone number, saved beneficiary
- Sending payment to UPI ID, phone number, saved beneficiary, bank account (IFSC code and account number)
- Fetching balance
- Determining number of ditgits in PIN
# More plans
- Processing upi:// links and automating handling them, which are contained in a QR code
- Directly processing a QR code image (integrating camera to directly scan is not planned, users would be required to use any QR app like 'Decoder' to scan code, and open the link via this app, which would then be handled)

View File

@ -1,7 +1,7 @@
{
"id" : "net.hemish.pe",
"runtime" : "org.gnome.Platform",
"runtime-version" : "43",
"runtime-version" : "44",
"sdk" : "org.gnome.Sdk",
"command" : "pe",
"finish-args" : [

BIN
screenshots/main_view.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
screenshots/pin_entry.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,12 +1 @@
# Address widgets to be used in Transaction View
# like UPI ID entry row, phone number entry row
from gi.repository import Gtk, Adw
# a common widget for UPI ID entry row and phone number entry row
class NormalWidget(Adw.EntryRow):
def __init__(self, name, title, input_purpose):
super().__init__()
self.set_name(name)
self.set_title(title)
self.set_input_purpose(input_purpose)

View File

@ -1,7 +1,7 @@
from gi.repository import Gtk, Adw
class PinEntry(Adw.MessageDialog):
def __init__(self, parent, ok_callback, cancel_callback = None, digits = None):
def __init__(self, parent, ok_callback, cancel_callback = None, ok_label = "OK", digits = None):
super().__init__()
self.set_heading("Enter PIN")

View File

@ -69,7 +69,7 @@ template ApplicationWindow : Adw.ApplicationWindow {
centering-policy: strict;
name: "Pe";
Gtk.Button transaction_view_back_button {
Gtk.Button {
tooltip-text: "Go Back";
icon-name: "go-previous-symbolic";
action-name: "win.go_back_to_main_view";
@ -84,17 +84,29 @@ template ApplicationWindow : Adw.ApplicationWindow {
Adw.PreferencesGroup transaction_view_input_group {
Adw.EntryRow transaction_view_amount {
title: "Amount";
input-purpose: number;
tooltip-text: "Amount";
}
Adw.EntryRow transaction_view_id {
title: "UPI ID";
input-purpose: email;
tooltip-text: "UPI ID";
}
Adw.EntryRow transaction_view_remark{
title: "Remark";
input-purpose: free_form;
tooltip-text: "Remark";
}
Adw.EntryRow transaction_view_number {
title: "Phone Number";
input-purpose: number;
tooltip-text: "Phone Number";
}
Adw.EntryRow transaction_view_amount {
title: "Amount";
input-purpose: number;
tooltip-text: "Amount";
}
Adw.EntryRow transaction_view_remark{
title: "Remark";
input-purpose: free_form;
tooltip-text: "Remark";
}
}
Adw.PreferencesGroup {

View File

@ -66,11 +66,13 @@ Gtk.Box main_view_box{
name: "beneficiary";
title: "Saved Beneficiary";
icon-name: "view-list-symbolic";
visible: false;
}
Adw.ActionRow main_view_home_bank_account_row{
name: "bank";
title: "Bank Account";
icon-name: "bank-symbolic";
visible: false;
}
}
}
@ -118,12 +120,14 @@ Gtk.Box main_view_box{
title: "Transactions";
icon-name: "mail-send-receive-symbolic";
child: Adw.ToastOverlay {};
visible: false;
}
Adw.ViewStackPage main_view_profile {
name: "profile";
title: "Profile";
icon-name: 'system-users-symbolic';
visible: false;
child: Gtk.Label {
label: "Page 3";
};

View File

@ -8,8 +8,6 @@ from .pin_entry import PinEntry
# that it can be reused by both, and I dont have to duplicate it in both
from .menu_button import MenuButton
from .address_widgets import NormalWidget
@Gtk.Template(resource_path='/net/hemish/pe/ui/gui.ui')
class ApplicationWindow(Adw.ApplicationWindow):
__gtype_name__ = "ApplicationWindow"
@ -17,9 +15,10 @@ class ApplicationWindow(Adw.ApplicationWindow):
leaflet: Adw.Leaflet = Gtk.Template.Child()
transaction_view_send_receive: Gtk.Button = Gtk.Template.Child()
transaction_view_amount = Gtk.Template.Child()
transaction_view_input_group: Gtk.Box = Gtk.Template.Child()
transaction_view_input_group: Adw.PreferencesGroup = Gtk.Template.Child()
transaction_view_id: Adw.EntryRow = Gtk.Template.Child()
transaction_view_number: Adw.EntryRow = Gtk.Template.Child()
transaction_view_remark = Gtk.Template.Child()
transaction_view_back_button = Gtk.Template.Child()
success_view_status = Gtk.Template.Child()
def __init__(self, *args, **kwargs):
@ -115,24 +114,87 @@ class ApplicationWindow(Adw.ApplicationWindow):
#----------------------------------------#
#------- send/receive handling ----------#
# Called when either send or receive button is pressed in main view
def handle_home_send_receive(self, button: Gtk.Button):
action = button.get_name()
identifier_type = self.main_view_home_options_list.get_selected_row().get_name()
print(identifier_type)
self._handle_send_receive(action, identifier_type)
# Meta send/receive function which can be used by gui, or cli upi:// link passing
def _handle_send_receive(self, action, identifier_type, identifier_raw = None):
# identifier_raw is optional, it may be provided or not
# identifier_type can be one of 'number', 'id', etc.
# while identifer_raw is the actua id or number like 94XXXXXXXX
# First setting label and name of buttons of transaction view
# according to which button of main view is pressed
self.transaction_view_send_receive.set_name(button.get_name())
self.transaction_view_send_receive.set_name(action)
# used button.get_child().get_label() because button has child Adw.ButtonContent
# which has label, and not button
self.transaction_view_send_receive.set_label(button.get_child().get_label())
if action == 'send':
self.transaction_view_send_receive.get_child().set_label("Send")
else:
self.transaction_view_send_receive.get_child().set_label("Receive")
if identifier_type == 'id':
self.transaction_view_input_group.active_identifier = self.transaction_view_id
self.transaction_view_id.show()
self.transaction_view_number.hide()
if identifier_raw != None:
self.transaction_view_id.set_text(identifier_raw)
elif identifier_type == 'number':
self.transaction_view_input_group.active_identifier = self.transaction_view_number
self.transaction_view_id.hide()
self.transaction_view_number.show()
if identifier_raw != None:
self.transaction_view_number.set_text(identifier_raw)
self.leaflet.set_visible_child_name('transaction_view')
# The callback which handles the 'actual' send/receive when either
# of the button is clicked
""" def submit_send_receive(self, button):
service = self.get_application().upi_service
def submit(pin):
if button.name == 'send':
if self.transaction_view_input_group.active_identifier.get_name() == 'id':
service.send_money_to_upi_id
elif self.transaction_view_input_group.active_identifier.get_name() == 'number':
elif button.name == 'receive':
if self.transaction_view_input_group.active_identifier.get_name() == 'id':
elif self.transaction_view_input_group.active_identifier.get_name() == 'number':
amount = self.transaction_view_amount.get_text()
remark = self.transaction_view_remark.get_text()
if remark == '' or remark == None:
remark = 1 """
#----------------------------------------#
#----------------------------------------#
# Is used to make an action, which is then resued in .blp
# files for navigation via back button in headerbar
def go_back_to_main_view(self, *args):
self.root_stack.set_visible_child_name("leaflet")
self.leaflet.set_visible_child_name('main_view')