I'd like to know how can I show simple and short data (eg: temperature data from a running script) in Android desktop using SL4A. My script gets temperature data from a web, and I tried showing it via notify, via toast and via dialogs, but I´d like it to remain accesible on the screen.
I undestand that webview is an option but I can´t write the xml code for a simple text box, as I undestand I should.
Is this right? Where could I get a (very) simple code example in xml to show text on screen (not full screen)?
Thanks!
I just re-read your question there are a few examples of full screen webviews however as far as I understand it webview is kind of an all or nothing deal. If you want to us Xml look into the Fullscreenwrapper2 as it allows you to use similar xml layouts to native java apps, from there you can look for examples of xml layouts and modify to fit.
Do you want a widget or just a dialog box? if you just want a dialog box you can just use a dialogCreateAlert
import android
droid = android.Android()
title=("Your Tittle")
content=("Your content")
droid.dialogCreateAlert(title, content)
droid.dialogShow()
However if you want it on a widget the simplest soloution I have found is to use a minimalistic text widget you can pass the data either through tasker or directly using a local intent.
Through Tasker:
import android, time
droid = android.Android()
class Task():
SET_VARIABLE = 547
def new_task(self):
self.action_cnt = 0
self.extras = {'version_number': '1.0', 'task_name': 'task' + str(time.time()), 'task_priority': 9 }
def set_var(self, varname, value):
self.action_cnt += 1
self.extras['action' + str(self.action_cnt)] = {'action': self.SET_VARIABLE, 'arg:1': varname, 'arg:2': value, 'arg:3': False, 'arg:4': False, 'arg:5': False}
def run_task(self):
taskIntent = droid.makeIntent('net.dinglisch.android.tasker.ACTION_TASK', None, None, self.extras).result
droid.sendBroadcastIntent(taskIntent)
def set_var_now(self, varname, value):
self.new_task()
self.set_var(varname, value)
self.run_task()
t = Task()
t.set_var_now("%Var", "Your variable value")
Directly via a Local intent (You select this in MT by using a Local variable field):
import android
droid = android.Android()
activity = 'com.twofortyfouram.locale.intent.action.FIRE_SETTING'
extras = {}
extras['de.devmil.minimaltext.locale.extras.VAR_NAME'] = 'Your Variable name'
extras['de.devmil.minimaltext.locale.extras.VAR_TEXT'] = 'Your Variable content'
packagename = 'de.devmil.minimaltext'
classname = 'de.devmil.minimaltext.locale.LocaleFireReceiver'
intent = droid.makeIntent(activity, None, None, extras, None, packagename, classname).result
droid.sendBroadcastIntent(intent)
Related
thank you for your time.
I have an APP I'm working on. I'm trying to automate something where every word I write in the MDTextField id: sent is turned into it's own MDChip id: chip to be selected later on by the user.
The closest I've gotten is using this:
class Main(Screen):
"""main application goes here"""
def my_callback(dt):
def __init__(self):
sent = self.ids.sentence.text.lower()
print(sent)
for word in sent.split():
c = MDChip(label=word, icon='check')
self.ids.chip.add_widget(c)
Clock.schedule_interval(my_callback, 0.5)
But it doesn't work. def init(self) never actually runs it seems... idk... I have verified that the my_callback function is getting called properly by the Clock but that's as far as it goes.
Any thoughts anyone..?
Maybe there is a better function inside Kivy that is called anytime something is typed in a textfield?? That would be perfect I think...
Ultimately I want to use one of the MDChip selected by the user and replace their sentence with a new word. I truly appreciate any help.
Your my_callback() method is just defining another method named __init__(), without actually calling that new method. That is an inner function and is not visible outside of my_callback().
Try changing my_callback() to:
def my_callback(self, dt):
sent = self.ids.sentence.text.lower()
print(sent)
for word in sent.split():
c = MDChip(label=word, icon='check')
self.ids.chip.add_widget(c)
Also, an __init__() method is the method used to initialize a Widget, and it's not good programming practice to use that method name for other purposes.
Here what I finally found with some help. Thanks all.
class Main(Screen):
def on_pre_enter(self):
Window.bind(on_key_down=self.call)
def call(self, *args):
self.ids.stack.clear_widgets()
sent = self.ids.sentence.text.lower()
for word in sent.split():
c = MDChip(label=word,
callback=self.do_something,
icon='checkbox-blank-outline',
color=[.1, .1, .1, .5],)
self.ids.stack.add_widget(c)
def do_something(self, inst, word, *args):
inst.icon = 'checkbox-marked-outline'
WORD = word
print(WORD)
return WORD
Using the Window.bind inside the on_pre_enter was the most important thing. Then it all worked. Here is my KV code:
MDStackLayout:
id: stack
orientation: 'lr-tb'
pos_hint: {'center_x': 0.5, 'center_y': 0.6}
padding: dp(8)
spacing: dp(8)
Context: I am developing a mobile Shiny app using the shinyMobile package, which is a wrapper for the famous framework7 HTML template.
In my app, the user has to make a selection of attributes on a first tab using multiple dropdown lists, and then, on the other tabs, some output is produced. Each tab requires the user to scroll up and down to access all the content and in this process, very often the 'pull to refresh' feature is triggered.
This is really annoying, because the entire attribute selection and output are lost, and the user has to start over from scratch.
What I tried: based on this SO thread which pointed me to this Google developer page, I tried setting the CSS overscroll-behavior property to contain with: body {overscroll-behavior-y: contain;}. PROBLEM: It does not work for me! (tested on Chrome Android)
Minimal reproducible example:
default app, deployed here
library(shiny);library(shinyMobile)
shiny::shinyApp(
ui = f7Page(
f7Card(
h5('try to pull to refresh. normally it should work.')
)
),
server = function(input, output) {}
)
Supposedly fixed app, deployed here
library(shiny);library(shinyMobile)
shiny::shinyApp(
ui = f7Page(
tags$style(type='text/css', '.body{overscroll-behavior-y: contain;}'),
f7Card(
h5('try to pull to refresh. Normally it should not work.')
)
),
server = function(input, output) {}
)
Hope you guys can reproduce my issue and identify what is amiss!!!
You might want to change your css part to: html,body{overscroll-behavior-y: contain;}, see https://stackoverflow.com/a/42509310/3502164.
Then it works for me on my mobile (android chrome).
Reproducible example:
library(shiny)
library(shinyMobile)
app <- shiny::shinyApp(
ui = f7Page(
tags$style(type='text/css', 'html,body{overscroll-behavior-y: contain;}'),
f7Card(
h5('try to pull to refresh. Normally it should not work.')
)
),
server = function(input, output) {}
)
# use host config to access app from mobiles in the same network
shiny::runApp(appDir = app, host = '0.0.0.0')
I can locate an iframe in an Android webview, but I need to interact with it. Various pages suggest
query("webView css:'iframe'", :stringByEvaluatingJavaScriptFromString => '...')
but that results in "No such method found: stringByEvaluatingJavaScriptFromString([String])" and it looks like an iOS function, so I'm pretty sure this is an iOS-only solution: I need Android.
I know I can pass javascript queries in by opening them in a URL, but I've only experienced getting stuff out of an Android webview with specially-code Chrome objects linked in to the application.
Any ideas for getting data out?
The iOS responses are correct, you should use javascript to get the contents of an iframe. However, you should use evaluate_javascript(query_string, javascript) to evaluate javascript in Android. e.g
evaluate_javascript("webview", "return document.getElementsByTagName('iframe')[0].contentWindow.document.getElementById('...').getBoundingClientRect();")
Use the coordinates to tap the view.
If the rect is not parsed correctly, you could do something like this:
var boundingBox = document.getElementsByTagName('iframe')[0].contentWindow.document.getElementById('main').getBoundingClientRect();
var rect = {};
rect.width = boundingBox.width;
rect.height = boundingBox.height;
rect.left = boundingBox.left;
rect.top = boundingBox.top;
return rect;
Secondly, query('webView css:#grabby') is not a valid query. query('webView css:"#grabby"') or query("webView css:'#grabby'") is. You are most likely running a (very) old version of Calabash-Android if the test-server is crashing instead of reporting it.
I wrote what amounted to an answer while I was exploring, but I'm posting it here to save someone else time (hopefully).
I can successfully run Javascript in the view:
query("webView index:0", :loadUrl => 'javascript:alert("Hello")')
so I can probably arrange to press buttons plausibly enough for testing:
query("webview index:0", :loadUrl => 'javascript: ifr = document.getElementsByTagName("iframe")[0]; idoc = ifr.contentDocument || ifr.contentWindow.document; idoc.getElementById('myCheck').click(); false')
To get data out again, I can create a DIV and set its title (for example) with some 'return' value:
query("webView index:0", :loadUrl => 'javascript:var idiv = document.createElement("div"); idiv.id="grabby"; document.getElementsByTagName("body")[0].appendChild(idiv); idiv.title="some return value"; false')
(The trailing 'false' in the javascript: command is there to prevent the result of running the javascript: command from replacing the page - that took a while to work out.)
I can look into the iframe too:
query("webView index:0", :loadUrl => 'javascript:var idiv = document.getElementById("grabby"); ifr = document.getElementsByTagName("iframe")[0]; idoc = ifr.contentDocument || ifr.contentWindow.document; idiv.title = idoc.getElementsByTagName("input")[3].id; false')
When I tried to retrieve it with
query('webView css:#grabby')
it hung for a while, then crashed with a timeout:
HTTPClient::ReceiveTimeoutError: execution expired
from /Users/tim./.rvm/gems/ruby-2.1.5/gems/httpclient-2.6.0.1/lib/httpclient/session.rb:876:in `gets'
from /Users/tim./.rvm/gems/ruby-2.1.5/gems/httpclient-2.6.0.1/lib/httpclient/session.rb:876:in `block in parse_header'
from ...
curiously, query('webView css:"*"') mostly seems to work without timeouts, and reports the 'grabby' DIV complete with the title string
query('webView css:"*"').find { |e| e['id'] == 'grabby' }
so that's useful enough. I'll have to write some horrible recursive descent thingy to fish out the iframe's contents - perhaps make the div invisible and just copy the htmlContent there.
I'm really not sure why the direct query crashes.
in Kivy, when I press the back button on my android device it throws me out of the application. is there a way to return back to the previous screen using the Kivy language and not python? this is what I have written in kivy:
<MyAppClass>:
AnchorLayout:
anchor_x : 'center'
anchor_y : 'top'
ScreenManager:
size_hint : 1, .9
id: _screen_manager
Screen:
name:'screen1'
StackLayout:
# irrelevant code
Screen:
name:'screen2'
StackLayout:
# irrelevant code
I need to manipulate the screen manager and its screens from python... if I can do so I will be ok with python.
Kivy on android binds the back button to the esc button so binding and listening to esc button in your app would help you handle how your app behaves when the back button is pressed.
In other words in your app when testing it on your desktop listen to the escape key from the system keyboard, this will be automatically be translated to being the back button on your android device. Something like::
def on_start():
from kivy.base import EventLoop
EventLoop.window.bind(on_keyboard=self.hook_keyboard)
def hook_keyboard(self, window, key, *largs):
if key == 27:
# do what you want, return True for stopping the propagation
return True
i guess that i have solved it but should thank both #inclement and #qua-non! your answers guys led me to the right way! so in kv i assume that i gave an id to my screen manager (please refer to my question where i have written the kv code) , in python i should do the following:
from kivy.core.window import Window
from kivy.properties import ObjectProperty
class MyAppClass(FloatLayout):#its a FloatLayout in my case
_screen_manager=ObjectProperty(None)
def __init__(self,**kwargs):
super(MyAppClass,self).__init__(**kwargs)
#code goes here and add:
Window.bind(on_keyboard=self.Android_back_click)
def Android_back_click(self,window,key,*largs):
if key == 27:
self._scree_manager.current='screen1'#you can create a method here to cache in a list the number of screens and then pop the last visited screen.
return True
class MyApp(App):
def build(self):
return MyAppClass()
if __name__=='__main__':
MyApp().run()
This is certainly possible. Here's a short example app with the method I use to do this:
from kivy.utils import platform
from kivy.core.window import Window
class ExampleApp(App):
manager = ObjectProperty()
def build(self):
sm = MyScreenManager()
self.manager = sm
self.bind(on_start=self.post_build_init)
return sm
def post_build_init(self, *args):
if platform() == 'android':
import android
android.map_key(android.KEYCODE_BACK, 1001)
win = Window
win.bind(on_keyboard=self.my_key_handler)
def my_key_handler(self, window, keycode1, keycode2, text, modifiers):
if keycode1 in [27, 1001]:
self.manager.go_back()
return True
return False
This should give the right basic idea, but a few notes:
ScreenManager doesn't keep track of the previous screens, it's up to you to implement this how you like. My example assumes you defined a class MyScreenManager with a go_back method.
It might not be necessary to bind to on_start and run post_build_init, this is just how the example I originally used did it (see below). It might be important sometimes though, possibly if the window is not initialised when build() is run, and the original mailing list post suggests the author needed it for some reason.
The example listens for keycodes 27 or 1001. As qua-non said while I was writing this, the former listens for esc, so you can get the same behaviour on desktop.
I didn't try without the android.map_key line, but it seems like it may not be necessary.
You mention you want to use kivy language and not python. You need to do some python to get this result, and I don't see a way around that (it's not really the domain of the kv language). I guess you could shift some stuff to kv by defining a 'go_back' event somewhere and triggering this when the key is pressed, along with binding your screenmanager to watch that event, but it seems like a long way around.
I based my code on the mailing list thread at https://groups.google.com/forum/#!topic/kivy-users/7rOZGMMIFXI . There might be a better way, but this is quite functional.
Now all the way in 2020 I'm using:
Clock.schedule_once(lambda x: Window.bind(on_keyboard=self.hook_keyboard))
in combination with a similar hook_keyboard method to the other answers, to delay the bind in my build method. Works fine, but none of these other ways ways seemed to work for me anymore.
I have started working with Kivy recently and have encountered a problem. I need to make a button/thumbnail with a URL image as background. I got some help on the Kivy user forum here but am still a little stuck. Here is my code just for that part:
for image_set, image_type in zip(categorized_images, image_types):
layout = GridLayout(cols=2, size_hint=(None, None))
layout.bind(minimum_height=layout.setter('height'))
scroll_view = ScrollView(size_hint=(None, None), size=(320, 200))
section = AccordionItem(title='%s' % image_type)
layout.add_widget(back_button1)
for image_path in image_set:
layout.add_widget(AsyncImage(source="http://www.example/"'%s' % image_path,\
size_hint=(None, None), size=(160, 160)))
scroll_view.add_widget(layout)
section.add_widget(scroll_view)
accordion.add_widget(section)
What I have now is just a bunch of images being created, but I need thumbnails to be created that will lead to the full sized images. Besides that I have the builder.load_string part and the ButtonBehavior class part mentioned in the Kivy link. I just don't know how to implement in that "for loop". Is it possible to treat the UrlBut instance as a widget?
Thumbnail to Full Image
Make UrlBut create and store a thumbnail when it is instantiated. You can do this with PIL, follow this tutorial http://andrius.miasnikovas.lt/2010/04/creating-thumbnails-from-photos-with-python-pil/
Then, have the on_press method (or equivalent) create a pop-up or overlay that contains the full size image.
Treating the UrlBut as a Widget
Read this: http://kivy.org/docs/api-kivy.lang.html#syntax-of-template
The Kivy docs say that a template defined in the kv file may be instantiated within your Python code like this (I am adapting the example given at that link):
Your kv language template:
[UrlBut#ButtonBehavior+AsyncImage]:
source: ctx.source
button_grab: True
on_press: eval(ctx.on_press)
Then in Python, you can instantiate the template with:
from kivy.lang import Builder
urlbut1 = Builder.template('UrlBut',source='http://lorempixel.com/400/200')
# create a second template with a different image
urlbut2 = Builder.template('UrlBut', source='http://lorempixel.com/400/200')
# and use urlbut1 and urlbut2 as other widgets.
Then, you could automate this with a for-loop:
for image_path in image_set:
layout.add_widget(Builder.template('UrlBut',source="http://www.example/"'%s' % image_path)