Last active
October 23, 2019 16:30
-
-
Save freakboy3742/7beb22c587e57240610777a44af645d8 to your computer and use it in GitHub Desktop.
One app, three platforms...
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android | |
from android.app import AlertDialog | |
from android.graphics import Color | |
from android.graphics.drawable import ColorDrawable | |
from android.os import AsyncTask | |
from android.os import Looper | |
from android.os import Handler | |
from android.util import TypedValue | |
from android.view import View | |
from android.view import MenuItem | |
from android.widget import LinearLayout | |
from android.widget import ArrayAdapter | |
from android.widget import ImageView | |
from android.widget import TextView | |
from android.widget import EditText | |
from android.support.v4.widget import SwipeRefreshLayout | |
from android.util import Log | |
from java.io import BufferedInputStream, BufferedReader, InputStreamReader, OutputStreamWriter | |
from java.net import URL | |
from org.json import JSONArray, JSONObject | |
from com.baoyz.swipemenulistview import SwipeMenuListView, SwipeMenuCreator, SwipeMenuItem | |
class RefreshTask(implements=java.lang.Runnable): | |
def __init__(self, adapter): | |
self.adapter = adapter | |
def run(self) -> void: | |
Log.i("TESTAPP", "BACKGROUND REFRESH DONE TASK") | |
self.adapter.notifyDataSetChanged() | |
Log.i("TESTAPP", "BACKGROUND REFRESH DONE TASK DONE") | |
class UpdateDataTask(implements=java.lang.Runnable): | |
def __init__(self, adapter): | |
self.adapter = adapter | |
def run(self) -> void: | |
url = URL("http://freakboy3742.pythonanywhere.com/api/todo/?format=json") | |
connection = url.openConnection() | |
Log.i("TESTAPP", "GET RESPONSE: %s" % connection.getResponseCode()) | |
reader = BufferedReader(InputStreamReader(connection.getInputStream())) | |
content = "" | |
line = reader.readLine() | |
while line: | |
content = content + '\n' + line | |
line = reader.readLine() | |
json = JSONArray(content) | |
self.adapter.data = [] | |
for i in range(0, json.length()): | |
obj = json.get(i) | |
self.adapter.data.append(obj) | |
handler = Handler(Looper.getMainLooper()) | |
handler.post(RefreshTask(self.adapter)) | |
connection.disconnect() | |
class DeleteItemTask(implements=java.lang.Runnable): | |
def __init__(self, adapter, index): | |
self.adapter = adapter | |
self.index = index | |
def run(self) -> void: | |
Log.i("TESTAPP", "DELETE ITEM %s" % self.index) | |
url = URL("http://freakboy3742.pythonanywhere.com/api/todo/%s/" % self.adapter.data[self.index].get('id')) | |
Log.i("TESTAPP", "DELETE URL %s" % url) | |
connection = url.openConnection() | |
connection.setRequestMethod("DELETE") | |
Log.i("TESTAPP", "DELETE RESPONSE: %s" % connection.getResponseCode()) | |
del self.adapter.data[self.index] | |
handler = Handler(Looper.getMainLooper()) | |
handler.post(RefreshTask(self.adapter)) | |
class AddItemTask(implements=java.lang.Runnable): | |
def __init__(self, adapter, description): | |
self.adapter = adapter | |
self.description = description | |
def run(self) -> void: | |
url = URL("http://freakboy3742.pythonanywhere.com/api/todo/") | |
connection = url.openConnection() | |
connection.setRequestMethod("POST") | |
connection.setDoOutput(True) | |
connection.setRequestProperty("Content-Type", "application/json") | |
writer = OutputStreamWriter(connection.getOutputStream()) | |
writer.write('{"description": "%s", "completed": false}' % self.description) | |
writer.flush() | |
# writer.close() | |
Log.i("TESTAPP", "PUT RESPONSE: %s %s" % (connection.getResponseCode(), connection.getResponseMessage())) | |
reader = BufferedReader(InputStreamReader(connection.getInputStream())) | |
content = "" | |
line = reader.readLine() | |
while line: | |
content = content + '\n' + line | |
line = reader.readLine() | |
self.adapter.data.append(JSONObject(content)) | |
handler = Handler(Looper.getMainLooper()) | |
handler.post(RefreshTask(self.adapter)) | |
connection.disconnect() | |
class DataAdapter(extends=com.baoyz.swipemenulistview.BaseSwipeListAdapter): | |
def __init__(self, context): | |
Log.i("TESTAPP", "INIT DATA ADAPTER " + str(data)) | |
self.context = context | |
self.updateData() | |
def updateData(self): | |
task = UpdateDataTask(self) | |
AsyncTask.execute(task) | |
def deleteItem(self, item): | |
task = DeleteItemTask(self, item) | |
AsyncTask.execute(task) | |
def addItem(self, description): | |
task = AddItemTask(self, description) | |
AsyncTask.execute(task) | |
def getCount(self) -> int: | |
# Log.i("TESTAPP", "GET COUNT") | |
# Log.i("TESTAPP", "DATA SIZE of " + str(self.data)) | |
# Log.i("TESTAPP", "DATA SIZE " + str(len(self.data))) | |
return len(self.data) | |
def getItem(self, position: int) -> java.lang.String: | |
# Log.i("TESTAPP", "GET ITEM " + str(position)) | |
return self.data[position] | |
def getItemId(self, position: int) -> long: | |
# Log.i("TESTAPP", "GET ITEM ID " + str(position)) | |
return position | |
def getView(self, position: int, convertView: android.view.View, parent: android.view.ViewGroup) -> android.view.View: | |
if convertView is None: | |
convertView = View.inflate(self.context, android.R.layout.simple_list_item_1, None) | |
item = self.getItem(position) | |
convertView.setText(item.get('description')) | |
return convertView | |
class RefreshListener(implements=android.support.v4.widget.SwipeRefreshLayout[OnRefreshListener]): | |
def __init__(self, layout: android.support.v4.widget.SwipeRefreshLayout, adapter): | |
self.layout = layout | |
self.adapter = adapter | |
def onRefresh(self) -> void: | |
Log.i("TESTAPP", "REFRESH!!") | |
self.adapter.updateData() | |
self.layout.setRefreshing(False) | |
class ListSwipeMenuCreator(implements=com.baoyz.swipemenulistview.SwipeMenuCreator): | |
def __init__(self, context): | |
self.context = context | |
def create(self, menu: com.baoyz.swipemenulistview.SwipeMenu) -> void: | |
# # Create "Open" item | |
# openItem = SwipeMenuItem(self.context) | |
# openItem.setBackground(ColorDrawable(Color.rgb(0xC9, 0xC9, 0xCE))) | |
# openItem.setWidth(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 90, self.context.getResources().getDisplayMetrics())) | |
# openItem.setTitle("Open") | |
# openItem.setTitleSize(18) | |
# openItem.setTitleColor(Color.WHITE) | |
# menu.addMenuItem(openItem) | |
# create "delete" item | |
deleteItem = SwipeMenuItem(self.context) | |
deleteItem.setBackground(ColorDrawable(Color.rgb(0xF9, 0x3F, 0x25))) | |
deleteItem.setWidth(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 90, self.context.getResources().getDisplayMetrics())) | |
deleteItem.setIcon(android.R.drawable.ic_delete) | |
menu.addMenuItem(deleteItem) | |
class ListMenuItemClickListener(implements=com.baoyz.swipemenulistview.SwipeMenuListView[OnMenuItemClickListener]): | |
def __init__(self, listview, adapter): | |
self.listview = listview | |
self.adapter = adapter | |
def onMenuItemClick(self, position: int, menu: com.baoyz.swipemenulistview.SwipeMenu, index: int) -> bool: | |
Log.i("TESTAPP", "CLICK MENU ITEM %s, action %s" % (position, index)) | |
# if index == 0: | |
# Log.i("TESTAPP", "OPEN item %s" % position) | |
# self.adapter.addItem('New item') | |
# elif index == 1: | |
Log.i("TESTAPP", "DELETE item %s" % position) | |
self.adapter.deleteItem(position) | |
return True | |
class DialogOKClickListener(implements=android.content.DialogInterface[OnClickListener]): | |
def __init__(self, input_field, adapter): | |
self.input_field = input_field | |
self.adapter = adapter | |
def onClick(self, dialog: android.content.DialogInterface, id: int) -> void: | |
Log.i("TESTAPP", "User input: %s" % self.input_field.getText()) | |
self.adapter.addItem(self.input_field.getText()) | |
class DialogCancelClickListener(implements=android.content.DialogInterface[OnClickListener]): | |
def onClick(self, dialog: android.content.DialogInterface, id: int) -> void: | |
Log.i("TESTAPP", "Cancel dialog") | |
dialog.cancel() | |
class MainActivity(extends=android.support.v4.app.FragmentActivity): | |
# /** Called when the activity is first created. */ | |
def onCreate(self, savedInstanceState: android.os.Bundle) -> void: | |
super().onCreate(savedInstanceState) | |
Log.i("TESTAPP", "CREATE APP") | |
adapter = DataAdapter(self) | |
adapter.data = [] | |
adapter.context = self | |
adapter.updateData() | |
self.adapter = adapter | |
listview = SwipeMenuListView(self) | |
listview.setSwipeDirection(SwipeMenuListView.DIRECTION_LEFT) | |
layout = SwipeRefreshLayout(self) | |
layout.setLayoutParams( | |
LinearLayout.LayoutParams( | |
LinearLayout.LayoutParams.MATCH_PARENT, | |
LinearLayout.LayoutParams.WRAP_CONTENT | |
) | |
) | |
listener = RefreshListener(layout, adapter) | |
layout.setOnRefreshListener(listener) | |
listview.setAdapter(adapter) | |
listview.setMenuCreator(ListSwipeMenuCreator(self)) | |
listview.setOnMenuItemClickListener(ListMenuItemClickListener(listview, adapter)) | |
layout.addView(listview) | |
layout.setId(1234) | |
self.layout_id = layout.getId() | |
self.setContentView(layout) | |
def onCreateOptionsMenu(self, menu: android.view.Menu) -> bool: | |
Log.i("TESTAPP", "CREATE OPTIONS MENU %s" % menu) | |
item = menu.add(0, 42, 0, 'Add Item') | |
# item.setIcon(android.R.drawable.ic_input_add) | |
item.setIcon(android.R.drawable.ic_menu_add) | |
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS) | |
return True | |
def onOptionsItemSelected(self, item: android.view.MenuItem) -> bool: | |
item_id = item.getItemId() | |
if item_id == 42: | |
alertDialogBuilder = AlertDialog.Builder(self) | |
layout = LinearLayout(self) | |
layout.setLayoutParams( | |
LinearLayout.LayoutParams( | |
LinearLayout.LayoutParams.MATCH_PARENT, | |
LinearLayout.LayoutParams.MATCH_PARENT | |
) | |
) | |
layout.setPadding( | |
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, self.getResources().getDisplayMetrics()), | |
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, self.getResources().getDisplayMetrics()), | |
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, self.getResources().getDisplayMetrics()), | |
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, self.getResources().getDisplayMetrics()) | |
) | |
layout.setOrientation(LinearLayout.VERTICAL) | |
input_field = EditText(self) | |
input_field.setLayoutParams( | |
LinearLayout.LayoutParams( | |
LinearLayout.LayoutParams.MATCH_PARENT, | |
LinearLayout.LayoutParams.WRAP_CONTENT | |
) | |
) | |
input_field.setHint("What to do...") | |
layout.addView(input_field) | |
alertDialogBuilder.setView(layout) | |
# set dialog message | |
alertDialogBuilder.setCancelable(False) | |
alertDialogBuilder.setPositiveButton("OK", DialogOKClickListener(input_field, self.adapter)) | |
alertDialogBuilder.setNegativeButton("Cancel", DialogCancelClickListener()) | |
# create alert dialog | |
alertDialog = alertDialogBuilder.create() | |
# show it | |
alertDialog.show() | |
return True |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import toga | |
class Example(toga.App): | |
def startup(self): | |
self.list = toga.List( | |
widget_id='todo', | |
source='todo-list', | |
detail='todo-detail', | |
item_class=toga.SimpleListElement, | |
on_item_press=self.remove_entry | |
) | |
self.input = toga.TextInput( | |
widget_id='data', | |
placeholder="new todo", | |
flex=1, margin=5 | |
) | |
container = toga.Container( | |
self.list, | |
toga.Container( | |
self.input, | |
toga.Button('Add', on_press=self.add_entry), | |
flex_direction='row' | |
), | |
flex_direction='column' | |
) | |
self.main_window.title = "Toga demo" | |
self.main_window.content = container | |
def add_entry(self, widget): | |
toga.post(self.list.create_url, { | |
'description': self.input.value(), | |
'completed': False, | |
}) | |
self.list.add(self.input.value()) | |
self.input.clear() | |
def remove_entry(self, widget): | |
if widget.delete_url: | |
toga.delete(widget.delete_url) | |
widget.remove() | |
else: | |
dom.window.alert("Can't delete new item") |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from http.client import HTTPConnection | |
import json | |
import random | |
from rubicon.objc import objc_method | |
from toga_iOS.libs import * | |
class AddItemController(UIViewController): | |
@objc_method | |
def loadView(self) -> None: | |
self.title = 'Add item' | |
self.__dict__['cancelButton'] = UIBarButtonItem.alloc().initWithBarButtonSystemItem_target_action_( | |
UIBarButtonSystemItemCancel, | |
self, | |
get_selector('cancelClicked') | |
) | |
self.navigationController.navigationBar.topItem.leftBarButtonItem = self.__dict__['cancelButton'] | |
self.__dict__['doneButton'] = UIBarButtonItem.alloc().initWithBarButtonSystemItem_target_action_( | |
UIBarButtonSystemItemDone, | |
self, | |
get_selector('doneClicked') | |
) | |
self.navigationController.navigationBar.topItem.rightBarButtonItem = self.__dict__['doneButton'] | |
self.view = UIView.alloc().initWithFrame_(UIScreen.mainScreen().bounds) | |
self.view.setBackgroundColor_(UIColor.whiteColor()) | |
self.__dict__['input'] = UITextField.alloc().init() | |
inputsize = self.__dict__['input'].systemLayoutSizeFittingSize_(CGSize(0, 0)) | |
self.__dict__['input'].setFrame_(NSRect(NSPoint(10, 100), NSSize(300, inputsize.height + 14))) | |
self.__dict__['input'].setBorderStyle_(UITextBorderStyleRoundedRect) | |
self.__dict__['input'].setTranslatesAutoresizingMaskIntoConstraints_(False) | |
self.__dict__['input'].setAutoresizesSubviews_(False) | |
self.__dict__['input'].setPlaceholder_("what to do...") | |
self.__dict__['input'].becomeFirstResponder() | |
self.view.addSubview_(self.__dict__['input']) | |
@objc_method | |
def cancelClicked(self): | |
self.dismissModalViewControllerAnimated_(True) | |
@objc_method | |
def doneClicked(self): | |
self.dismissModalViewControllerAnimated_(True) | |
new_item = self.__dict__['input'].text | |
if new_item: | |
conn = HTTPConnection('freakboy3742.pythonanywhere.com') | |
conn.request( | |
'POST', '/api/todo/', | |
body=json.dumps({ | |
'description': new_item, | |
'completed': False | |
}).encode('utf-8'), | |
headers={ | |
'Content-type': 'application/json', | |
'Content-encoding': 'utf-8', | |
} | |
) | |
r = conn.getresponse() | |
data = json.loads(r.read().decode('utf8')) | |
self.__dict__['tablecontroller'].__dict__['data'].append(data) | |
self.__dict__['tablecontroller'].tableView.reloadData() | |
class TableViewController(UITableViewController): | |
@objc_method | |
def numberOfSectionsInTableView_(self) -> int: | |
return 1 | |
@objc_method | |
def tableView_numberOfRowsInSection_(self, tableView, section: int) -> int: | |
return len(self.__dict__['data']) | |
@objc_method | |
def tableView_cellForRowAtIndexPath_(self, tableView, indexPath): | |
cell = tableView.dequeueReusableCellWithIdentifier_("row") | |
if cell is None: | |
cell = UITableViewCell.alloc().initWithStyle_reuseIdentifier_(UITableViewCellStyleDefault, "row") | |
cell.textLabel.text = self.__dict__['data'][indexPath.item]['description'] | |
return cell | |
@objc_method | |
def tableView_commitEditingStyle_forRowAtIndexPath_(self, tableView, editingStyle: int, indexPath): | |
if editingStyle == UITableViewCellEditingStyleDelete: | |
item = self.__dict__['data'][indexPath.row] | |
conn = HTTPConnection('freakboy3742.pythonanywhere.com') | |
conn.request('DELETE', '/api/todo/%s/' % item['id']) | |
del self.__dict__['data'][indexPath.row] | |
paths = NSArray.alloc().initWithObjects_(indexPath, None) | |
tableView.deleteRowsAtIndexPaths_withRowAnimation_(paths, UITableViewRowAnimationFade) | |
@objc_method | |
def refreshTable(self): | |
conn = HTTPConnection('freakboy3742.pythonanywhere.com') | |
conn.request('GET', '/api/todo/') | |
r = conn.getresponse() | |
data = json.loads(r.read().decode('utf8')) | |
self.__dict__['data'] = data | |
self.refreshControl.endRefreshing() | |
self.tableView.reloadData() | |
@objc_method | |
def addClicked(self): | |
self.__dict__['additemcontroller'] = AddItemController.alloc().init() | |
self.__dict__['additemcontroller'].__dict__['tablecontroller'] = self | |
navigationController = UINavigationController.alloc().initWithRootViewController_(self.__dict__['additemcontroller']) | |
self.presentModalViewController_animated_(navigationController, True) | |
class PythonAppDelegate(UIResponder): | |
# @objc_method | |
# def applicationDidBecomeActive(self) -> None: | |
# print("BECAME ACTIVE") | |
@objc_method | |
def application_didFinishLaunchingWithOptions_(self, application, launchOptions) -> bool: | |
self.__dict__['tablecontroller'] = TableViewController.alloc().init() | |
self.__dict__['tablecontroller'].refreshControl = UIRefreshControl.alloc().init() | |
self.__dict__['tablecontroller'].refreshControl.addTarget_action_forControlEvents_( | |
self.__dict__['tablecontroller'], | |
get_selector('refreshTable'), | |
UIControlEventValueChanged | |
) | |
conn = HTTPConnection('freakboy3742.pythonanywhere.com') | |
conn.request('GET', '/api/todo/') | |
r = conn.getresponse() | |
data = json.loads(r.read().decode('utf8')) | |
self.__dict__['tablecontroller'].__dict__['data'] = data | |
self.__dict__['navcontroller'] = UINavigationController.alloc().initWithRootViewController_(self.__dict__['tablecontroller']) | |
# self.__dict__['navcontroller'].navigationBar.topItem.title = "Hello World" | |
self.__dict__['tablecontroller'].title = "TodoList" | |
self.__dict__['addButton'] = UIBarButtonItem.alloc().initWithBarButtonSystemItem_target_action_( | |
UIBarButtonSystemItemAdd, | |
self.__dict__['tablecontroller'], | |
get_selector('addClicked') | |
) | |
self.__dict__['navcontroller'].navigationBar.topItem.rightBarButtonItem = self.__dict__['addButton'] | |
self.__dict__['window'] = UIWindow.alloc().initWithFrame_(UIScreen.mainScreen().bounds) | |
self.__dict__['window'].rootViewController = self.__dict__['navcontroller'] | |
self.__dict__['window'].makeKeyAndVisible() | |
return True |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import toga | |
class TodoApp(toga.App): | |
def startup(self): | |
self.list = toga.List( | |
widget_id='todo', | |
data=[ | |
{'description': 'item 1'}, | |
{'description': 'item 2'}, | |
{'description': 'item 3'}, | |
], | |
# item_class=toga.SimpleListElement, | |
on_delete=self.remove_entry, | |
on_refresh=self.refresh | |
) | |
self.input = toga.TextInput(placeholder="thing to do...") | |
self.add_item_dialog = toga.Dialog( | |
title="Add item", | |
content=toga.Container( | |
self.input | |
), | |
on_accept=self.add_entry | |
) | |
container = toga.NavigationView( | |
title="Todo List", | |
content=self.list, | |
on_action=self.show_add_dialog | |
) | |
self.main_window.content = container | |
def show_add_dialog(self, widget): | |
self.input.clear() | |
self.show_dialog(self.add_item_dialog) | |
def add_entry(self, widget): | |
if self.input.value: | |
self.list.add({'description': self.input.value}) | |
def remove_entry(self, widget): | |
print("REMOVE ENTRY", widget) | |
def refresh(self, list_widget): | |
print("REFRESH LIST", list_widget) | |
if __name__ == '__main__': | |
app = TodoApp('TodoList', 'org.pybee.todolist') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi!
Could anyone please provide a briefcase setup.py example for android.py?