Open Ismael-VC opened 8 years ago
OK, Thank you moving from twitter to here.
I think julia maybe work with SL4A, Because SL4A just launch the language binary in terminal app and you said "I can run Julia from Arch Linux Linux Deploy app in my rooted cellphone" in your tweet.
Next, you need to package the deploy app and register it to SL4A system as android application like py4a.
Please fork this project or SL4A language branch, or copy them for julia.
@Ismael-VC Are you familiar with the language? I can add the hook inside SL4A to recognize the language but I am pretty sure you will need a module on the Julia said to talk back to the rpc server as well as an interpreter for android. I can help you out with that but I don't know julia syntax...
p.s. good looking on running arch. i run arch too! :D
@ainsophical yes definitely! Thank you so much, that would be awesome, please let me know what I can do from the Julia side in order to help you make this possible. :+1:
The Julia syntax is easy! :smile:
It's a little outdated (v0.3) but for that introductory tutorial, syntax has remain the same AFAIKT.
@Ismael-VC
not really interested in julia but i think it would be cool to get more interpreted languages hooked into SL4A for a broader audience of this really cool app...
we need to hook julia into the known language list in SL4A. (Took care of this.... easy)
We need to compile Julia into an arm executable so it can run on android, and add any modules that are not c based. Then package this all up in an apk. I can start working on this as I want to get the process down so I can get other languages working in the future...
We also need an android module for julia. If you want you can convert this python android module to julia. Once I get the template going we can test it and see how it works...
import collections
import json
import os
import socket
import sys
PORT = os.environ.get('AP_PORT')
HOST = os.environ.get('AP_HOST')
HANDSHAKE = os.environ.get('AP_HANDSHAKE')
Result = collections.namedtuple('Result', 'id,result,error')
class Android(object):
def __init__(self, addr=None):
if addr is None:
addr = HOST, PORT
self.conn = socket.create_connection(addr)
self.client = self.conn.makefile()
self.id = 0
if HANDSHAKE is not None:
self._authenticate(HANDSHAKE)
def _rpc(self, method, *args):
data = {'id': self.id,
'method': method,
'params': args}
request = json.dumps(data)
self.client.write(request+'\n')
self.client.flush()
response = self.client.readline()
self.id += 1
result = json.loads(response)
if result['error'] is not None:
print result['error']
# namedtuple doesn't work with unicode keys.
return Result(id=result['id'], result=result['result'],
error=result['error'], )
def __getattr__(self, name):
def rpc_call(*args):
return self._rpc(name, *args)
return rpc_call
@ainsophical where does self._authenticate
comes from? I think the code snippet above could be incomplete. Where does this snippet come from?
Oh ok, I found it at:
If HANDSHAKE is not None
, calling Android()
would raise an: AttributeError: 'Android' object has no attribute '_authenticate'
Is this a :beetle:?
Julia ARM executable, the one I've tested on Android:
Three Julia packages would be needed, all are 100% julia code:
@Ismael-VC
i see what you mean and it didn't make much sense to me either. it's not a bug but i can't honestly say i know exactly how it works with a bit of prying into the code yet.
i ran the android.py module with that conditional commented out and got this error in logcat.
V/sl4a.JsonRpcServer:117: Sent: {"id":0,"result":null,"error":"java.lang.SecurityException: Authentication failed!"}
i found this in JsonRpcServer.java
private boolean checkHandshake(String method, JSONArray params) throws JSONException {
return method.equals("_authenticate") && this.mHandshake.equals(params.getString(0));
}
inherited through the socket connection somehow? not sure but yes that part is needed...
@ainsophical based on the Python and Ruby versions, I've come up with this:
__precompile__() # Allow for whole module precompilation.
module AndroidConnection
# It seems only this external package is needed (and it's dependeny `Compat.jl`).
using JSON: json
export Android
# Types don't own function methods.
type Android
client::TCPSocket
id::Int
end
# Type constructor
function Android()
handshake = ENV["AP_HANDSHAKE"]
_authenticate(handshake) # WTF!?
host = ENV["AP_HOST"]
port = parse(Int, ENV["AP_PORT"])
client = connect(host, port)
id = 0
new(client, id)
end
function _rpc(a::Android, method::Symbol, args...)
a.id += 1
request = json(Dict("id" => a.id, "method" => method, "params" => args))
println(a.client, request)
flush(a.client)
response = readline(a.client)
JSON.parse(response) # Last value is returned.
end
# In Julia `Base.getfield` (`foo.bar`) can't be extended "yet", instead
# `Base.getindex` (`foo[bar]`) is used.
Base.getindex(a::Android, method::Symbol) = (args...) -> _rpc(a, method, args...)
end # End module.
Still I can't understand how does self._authenticate
works, @kuri65536 could you give me a hint on this?
From all the versions found here, for several languages, none of them seems to be defininf their own _authenticate
method, I don't understand:
This is the Ruby version:
AP_PORT = ENV['AP_PORT']
AP_HOST = ENV['AP_HOST']
AP_HANDSHAKE = ENV['AP_HANDSHAKE']
require 'json/pure'
require 'socket'
def trap(*ignore)
# Trap does not work on Android.
end
class Android
def initialize()
@client = TCPSocket.new(AP_HOST, AP_PORT)
@id = 0
_authenticate(AP_HANDSHAKE) # WTF!?
end
def rpc(method, *args)
@id += 1
request = {'id' => @id, 'method' => method, 'params' => args}.to_json()
@client.puts request
response = @client.gets()
return JSON.parse(response)
end
def method_missing(method, *args)
rpc(method, *args)
end
end
I would expect this to fail like this:
julia> ENV["AP_HOST"] = "google.com"
"google.com"
julia> ENV["AP_PORT"] = 80
80
julia> ENV["AP_HANDSHAKE"] = :whatever
:whatever
julia> using AndroidConnection
julia> Android()
ERROR: UndefVarError: _authenticate not defined
in call at /home/ismaelvc/Android.jl:19
I'm also unsure as to when to increment id
since Python does it after the response
line, but Ruby does it before the request
line! :worried:
def _rpc(self, method, *args):
data = {'id': self.id,
'method': method,
'params': args}
request = json.dumps(data)
self.client.write(request+'\n')
self.client.flush()
response = self.client.readline()
self.id += 1 # WTF!?
result = json.loads(response)
if result['error'] is not None:
print result['error']
# namedtuple doesn't work with unicode keys.
return Result(id=result['id'], result=result['result'],
error=result['error'], )
def rpc(method, *args)
@id += 1 # WTF!?
request = {'id' => @id, 'method' => method, 'params' => args}.to_json()
@client.puts request
response = @client.gets()
return JSON.parse(response)
end
i dont have it figured out yet either but one thing you should know that there is a language class in SL4A that can be extended for each new language. Each one has to have something that looks for the import statement and the initialization of the droid object like so... it might be overriding the error as sl4a controls the interpreter as well and handles the statement itself. ive only been working on this project for a couple months now and its a bit to get a good handle on the internals so pardon not having a definite answer yet.
public class PythonLanguage extends Language {
public PythonLanguage() {
}
protected String getImportStatement() {
return "import android\n";
}
protected String getRpcReceiverDeclaration(String rpcReceiver) {
return rpcReceiver + " = android.Android()\n";
}
ill report back when i find out where this leads to get to that snippet i showed you before as it was the only line in the code that was _authenticate
as far as the increment, i dont think it matters much whether it starts at zero or one / before or after the request. it was probably just the preference of the person that coded the specific language module.. the rpc receiver loop doesnt look like it cares just so long as it has an id to pass as a param with each call to it..
This file has tests for the handshake:
This lines are also relevant, inside JsonRpcServer.java
:
Ok, I think I got this:
SL4A.jl
:__precompile__()
module SL4A
import JSON
export Android
const HOST = ENV["AP_HOST"]
const PORT = parse(Int, ENV["AP_PORT"])
const HANDSHAKE = ENV["AP_HANDSHAKE"]
typealias String AbstractString
type Android
client::TCPSocket
id::Int
function Android()
droid = new(connect(HOST, PORT), 0)
droid(:_authenticate, HANDSHAKE)
return droid
end
end
function Base.remotecall_fetch(droid::Android, method::String, params...)
request = JSON.json(Dict("id" => droid.id, "method" => method, "params" => params))
println(droid.client, request)
flush(droid.client)
response = readline(droid.client)
droid.id += 1
JSON.parse(response)
end
Base.call(droid::Android, method::String, args...) = remotecall_fetch(droid, method, params...)
Base.call(droid::Android, method::Symbol, args...) = call(droid, string(method), params...)
end
JuliaLanguage.java
:package com.googlecode.android_scripting.language;
public class JuliaLanguage extends Language {
@Override
protected String getImportStatement() {
return "using SL4A\n";
}
@Override
protected String getRpcReceiverDeclaration(String rpcReceiver) {
return rpcReceiver + " = Android()\n";
}
@Override
protected String getNull() {
return "nothing";
}
@Override
protected String getApplyOperatorText() {
return "(";
}
@Override
protected String getLeftParametersText() {
return ", ";
}
}
Continues discussion at Twitter:
There is a very active chat room for the Julia language, here!