diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 0000000..a1baae6 --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,42 @@ +on: [push, pull_request] +name: Android +jobs: + Integration: + strategy: + matrix: + os: + - 'ubuntu-latest' + runs-on: ${{ matrix.os }} + steps: + - name: Setup python + uses: actions/setup-python@v2 + with: + python-version: 3.8 + - uses: actions/checkout@v2 + - uses: actions/cache@v2 + with: + path: | + ~/.buildozer + .buildozer + key: ${{ hashFiles('buildozer.spec') }} + + - name: Setup environment + run: | + pip install buildozer + pip install Cython + - run: buildozer --help + - name: SDK, NDK and p4a download + run: | + sed -i.bak "s/# android.accept_sdk_license = False/android.accept_sdk_license = True/" buildozer.spec + buildozer android p4a -- --help + - name: Install Linux dependencies + if: matrix.os == 'ubuntu-latest' + run: sudo apt -y install automake + - name: buildozer android debug + run: | + touch main.py + buildozer android debug + - uses: actions/upload-artifact@v2 + with: + path: bin/*.apk + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f9a9ead --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.buildozer +bin diff --git a/buildozer.spec b/buildozer.spec index 17d13bc..2b66e69 100644 --- a/buildozer.spec +++ b/buildozer.spec @@ -18,17 +18,21 @@ version.regex = __version__ = '(.*)' version.filename = %(source.dir)s/main.py # requirements of the app -requirements = hostpython2,android,cryptography,pyasn1,pyjnius,pycrypto,twisted,kivy,docutils,pygments,cffi +requirements = android,cryptography,pyasn1,pyjnius,bcrypt,pycrypto,attrs,twisted,kivy,docutils,pygments,cffi # android specific -android.permissions = INTERNET, WAKE_LOCK, CAMERA, VIBRATE, ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION, SEND_SMS, CALL_PRIVILEGED, CALL_PHONE +android.permissions = INTERNET, WAKE_LOCK, CAMERA, VIBRATE, ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION, SEND_SMS, CALL_PRIVILEGED, CALL_PHONE, BLUETOOTH #android.sdk=21 #android.api=22 - +android.accept_sdk_license=True android.wakelock=True -#orientation=all +orientation=portrait +fullscreen=True +p4a.branch = develop + +#presplash.filename= # (str) Android logcat filters to use android.logcat_filters = *:S python:D diff --git a/main.py b/main.py index b7c3798..b0a682b 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,4 @@ -__version__ = '0.1' +__version__ = '0.2' # install_twisted_rector must be called before importing and using the reactor from kivy.support import install_twisted_reactor @@ -10,6 +10,7 @@ from twisted.internet import reactor from twisted.cred import portal, checkers from twisted.conch import manhole, manhole_ssh +from twisted.conch.ssh import keys from kivy.lang import Builder from kivy.uix.floatlayout import FloatLayout from kivy.properties import StringProperty @@ -19,16 +20,97 @@ from kivy.uix.screenmanager import Screen app = None +#+---[RSA 3072]----+ +#|E .o o=.o.=.. | +#|o...=**=o= = | +#|o.o.+*++= + . . | +#|o+ . ....+ . o +.| +#| o ..S. . . +| +#| o .. | +#| . . | +#| . . | +#| .. | +#+----[SHA256]-----+ +private_rsa="""-----BEGIN OPENSSH PRIVATE KEY----- +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 +-----END OPENSSH PRIVATE KEY----- +""" -def getManholeFactory(namespace, **passwords): +public_rsa="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCu2YF61scgIo8RABiwTvhZLmb0k2cdWaSKMn6yGdVbpAYtwbqZ6i9ZxOISrROLz+vtiPmg2DuCF42MoRyTlAlAkQs0iJVBDyIwM5mvACDCkvEqdCJhb5fgSgiqSVE9C+eDdIv24nZHPlD1OLfncivUIEPkn0SmwhSRjQt+IBeMB0yAzM4waci96m364SdJBHQ6YxjF2lFVMtWfqYFu80JZeklM+bmSwGAZ+QvLIt0sg4hcGci4voWu1EIf2SFbbxzHyjFyyzPl4Q4QRjZZXoJG5P6GmKdME9DPOpANtyRN80ZWpQs+VzjR4X1LBref1isolEvfY/1srfVlQZZYm7PLpdJnQP9Z2tkQb4NPMd7hB7yWLY5/sMrR1Fi8+PnrhAlYtG9AjOlSCMD/CRsCMmrWTILNlJ3ZZjFDPXvlQ1nNPuwdFcYeriSHs6hrnuj15tl/TESt6nXvyU6Dgwhys6UrkBmncclXweR8VDk/RQtVQA1zXnSmMKQ1fQCcbNYqTQM=" + +class PythonManhole(manhole.ColoredManhole): + def __init__(self): + glob = { + '__name__':'__main__', + '__doc__':None, + '__package__':None, + '__spec__':None, + '__annotations__':{}, + '__builtins__':__builtins__ + } + + super().__init__(glob) + + def makeConnection(self, cxt): + self.transport = cxt.transport + return super().makeConnection(cxt) + + def lineReceived(self, *args): + # Catch exit() + try: + return super().lineReceived(*args) + except SystemExit: + self.transport.loseConnection() + + +def getManholeFactory(**users): + # Create a terminal realm = manhole_ssh.TerminalRealm() - def getManhole(_): - return manhole.ColoredManhole(namespace) - realm.chainedProtocolFactory.protocolFactory = getManhole + realm.chainedProtocolFactory.protocolFactory = PythonManhole + users = {key: value.encode() for key, value in users.items()} p = portal.Portal(realm) p.registerChecker( - checkers.InMemoryUsernamePasswordDatabaseDontUse(**passwords)) + checkers.InMemoryUsernamePasswordDatabaseDontUse(**users)) + + # Create a factory f = manhole_ssh.ConchFactory(p) + f.publicKeys[b'ssh-rsa'] = keys.Key.fromString(public_rsa) + f.privateKeys[b'ssh-rsa'] = keys.Key.fromString(private_rsa) return f @@ -55,7 +137,7 @@ def get_interface_ip(self, ifname): return socket.inet_ntoa(fcntl.ioctl( s.fileno(), 0x8915, # SIOCGIFADDR - struct.pack('256s', ifname[:15]) + struct.pack('256s', ifname[:15].encode('utf-8')) )[20:24]) @@ -64,7 +146,7 @@ def build(self): global app app = self self.connection = reactor.listenTCP(8000, - getManholeFactory(globals(), admin='kivy')) + getManholeFactory(admin='kivy')) def on_pause(self): return True diff --git a/plyer_command_list.rst b/plyer_command_list.rst index 817774c..d9f4d83 100644 --- a/plyer_command_list.rst +++ b/plyer_command_list.rst @@ -160,7 +160,7 @@ Example:: from plyer import accelerometer accelerometer.enable() - accelerator.acceleration + accelerometer.acceleration Battery -------- diff --git a/remotekivy.kv b/remotekivy.kv index 5610b57..e301b66 100644 --- a/remotekivy.kv +++ b/remotekivy.kv @@ -69,8 +69,8 @@ NavigationDrawer id: manager MainScreen name: 'remote' - CommandScreen - name: 'plyer' + # CommandScreen + # name: 'plyer' ShellScreen name: 'console' diff --git a/requirements.txt b/requirements.txt index 355908d..0d5167f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,5 +4,5 @@ Kivy-Garden==0.1.4 pyasn1==0.1.9 pycrypto==2.6.1 requests==2.11.1 -Twisted==15.4.0 -zope.interface==4.3.2 +Twisted==20.3.0 +#zope.interface==4.3.2 diff --git a/test.py b/test.py new file mode 100644 index 0000000..6736043 --- /dev/null +++ b/test.py @@ -0,0 +1,139 @@ +__version__ = '0.2' + +import socket +import fcntl +import struct +from twisted.internet import reactor +from twisted.cred import portal, checkers +from twisted.conch import manhole, manhole_ssh +from twisted.conch.ssh import keys +from twisted.conch.insults import insults + +app = None + +#+---[RSA 3072]----+ +#|E .o o=.o.=.. | +#|o...=**=o= = | +#|o.o.+*++= + . . | +#|o+ . ....+ . o +.| +#| o ..S. . . +| +#| o .. | +#| . . | +#| . . | +#| .. | +#+----[SHA256]-----+ +private_rsa="""-----BEGIN OPENSSH PRIVATE KEY----- +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 +-----END OPENSSH PRIVATE KEY----- +""" + +public_rsa="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCu2YF61scgIo8RABiwTvhZLmb0k2cdWaSKMn6yGdVbpAYtwbqZ6i9ZxOISrROLz+vtiPmg2DuCF42MoRyTlAlAkQs0iJVBDyIwM5mvACDCkvEqdCJhb5fgSgiqSVE9C+eDdIv24nZHPlD1OLfncivUIEPkn0SmwhSRjQt+IBeMB0yAzM4waci96m364SdJBHQ6YxjF2lFVMtWfqYFu80JZeklM+bmSwGAZ+QvLIt0sg4hcGci4voWu1EIf2SFbbxzHyjFyyzPl4Q4QRjZZXoJG5P6GmKdME9DPOpANtyRN80ZWpQs+VzjR4X1LBref1isolEvfY/1srfVlQZZYm7PLpdJnQP9Z2tkQb4NPMd7hB7yWLY5/sMrR1Fi8+PnrhAlYtG9AjOlSCMD/CRsCMmrWTILNlJ3ZZjFDPXvlQ1nNPuwdFcYeriSHs6hrnuj15tl/TESt6nXvyU6Dgwhys6UrkBmncclXweR8VDk/RQtVQA1zXnSmMKQ1fQCcbNYqTQM=" + +class PythonManhole(manhole.ColoredManhole): + def __init__(self): + glob = { + '__name__':'__main__', + '__doc__':None, + '__package__':None, + '__spec__':None, + '__annotations__':{}, + '__builtins__':__builtins__ + } + + super().__init__(glob) + + def makeConnection(self, cxt): + self.transport = cxt.transport + return super().makeConnection(cxt) + + def lineReceived(self, *args): + # Catch exit() + try: + return super().lineReceived(*args) + except SystemExit: + self.transport.loseConnection() + + +def getManholeFactory(**users): + # Create a terminal + realm = manhole_ssh.TerminalRealm() + realm.chainedProtocolFactory.protocolFactory = PythonManhole + users = {key: value.encode() for key, value in users.items()} + p = portal.Portal(realm) + p.registerChecker( + checkers.InMemoryUsernamePasswordDatabaseDontUse(**users)) + + # Create a factory + f = manhole_ssh.ConchFactory(p) + f.publicKeys[b'ssh-rsa'] = keys.Key.fromString(public_rsa) + f.privateKeys[b'ssh-rsa'] = keys.Key.fromString(private_rsa) + return f + + +#class MainScreen: +# +# def __init__(self, **kwargs): +# ip = socket.gethostbyname(socket.gethostname()) +# if ip.startswith('127.'): +# interfaces = ['eth0', 'eth1', 'eth2', 'wlan0', 'wlan1', 'wifi0', +# 'tiwlan0', 'tiwlan1', 'ath0', 'ath1', 'ppp0'] +# for ifname in interfaces: +# try: +# ip = self.get_interface_ip(ifname) +# break +# except IOError: +# pass +# self.lan_ip = ip +# +# def get_interface_ip(self, ifname): +# s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +# return socket.inet_ntoa(fcntl.ioctl( +# s.fileno(), +# 0x8915, # SIOCGIFADDR +# struct.pack('256s', ifname[:15].encode('utf-8')) +# )[20:24]) +# +# +# +# def on_pause(self): +# return True + +if __name__ == '__main__': + # Define the local variables + connection = reactor.listenTCP(8001, getManholeFactory(admin='kivy')) + reactor.run()