Compare commits
202 Commits
sriramsv/s
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ff2737535 | ||
|
|
fefa35c95d | ||
|
|
1fefd5791b | ||
|
|
bd02082021 | ||
|
|
fd2ef77480 | ||
|
|
0e81cb673a | ||
|
|
51f0eac6ee | ||
|
|
4d3ee33c01 | ||
|
|
74f0b60e97 | ||
|
|
c9caad2410 | ||
|
|
a897f1ed0d | ||
|
|
7fb84c38e7 | ||
|
|
424eaca556 | ||
|
|
c42280333b | ||
|
|
ff61bad9bf | ||
|
|
e744b0dc46 | ||
|
|
4b377577e5 | ||
|
|
7792fec8ac | ||
|
|
a4d7b80950 | ||
|
|
bec53749db | ||
|
|
bfd42cb2e7 | ||
|
|
eb22ab2829 | ||
|
|
934cd29dff | ||
|
|
2ab316efd6 | ||
|
|
aa215947c9 | ||
|
|
9a9e21a53d | ||
|
|
95d8ed99ee | ||
|
|
3b948cc4ca | ||
|
|
3aff6de786 | ||
|
|
26ebaa3020 | ||
|
|
d7cd1c7f26 | ||
|
|
6cdb5a9336 | ||
|
|
82ce7e6eff | ||
|
|
d68ab0934f | ||
|
|
b9d73171ed | ||
|
|
296fbee028 | ||
|
|
4904a0050a | ||
|
|
7074a17b83 | ||
|
|
cc4813730a | ||
|
|
d5a1103002 | ||
|
|
e94808c35a | ||
|
|
f2e28096c5 | ||
|
|
605d4636d8 | ||
|
|
adfa6fad13 | ||
|
|
b5b701ce1c | ||
|
|
d1e6efa1d4 | ||
|
|
fa1dc0ad31 | ||
|
|
79fe5e8e7b | ||
|
|
3b033d6086 | ||
|
|
da7adad749 | ||
|
|
dd8e3a8379 | ||
|
|
c416f29b97 | ||
|
|
0922b1f2b7 | ||
|
|
d2651f22cb | ||
|
|
6107bc47ce | ||
|
|
41ab2191be | ||
|
|
a4960a9b60 | ||
|
|
0069d1ed67 | ||
|
|
dbb0608176 | ||
|
|
d93c42b50a | ||
|
|
d1f1be14a1 | ||
|
|
f3100ec22c | ||
|
|
e828444426 | ||
|
|
b007f6b756 | ||
|
|
91f733c882 | ||
|
|
58d4d41ab3 | ||
|
|
98afb5708e | ||
|
|
2b3d6d9b90 | ||
|
|
c12341915f | ||
|
|
75bbaffda8 | ||
|
|
7f3fac9034 | ||
|
|
d6e4e53da5 | ||
|
|
1580867d31 | ||
|
|
71bb976fb6 | ||
|
|
9903c131ce | ||
|
|
f7e5426ebe | ||
|
|
97947882df | ||
|
|
11a8cf2ad6 | ||
|
|
d063132df2 | ||
|
|
b5840ee5bb | ||
|
|
c4b0969a9d | ||
|
|
c58dba3173 | ||
|
|
d63f93d39d | ||
|
|
64128b5f66 | ||
|
|
6ccc1593dc | ||
|
|
7fdfecc983 | ||
|
|
b2e60d725b | ||
|
|
d5101ca34b | ||
|
|
42d4d4a104 | ||
|
|
ad9af4c2b6 | ||
|
|
4e930db750 | ||
|
|
acb73cc4de | ||
|
|
430379546c | ||
|
|
42473099eb | ||
|
|
73b85a1a5c | ||
|
|
9ab0ab3934 | ||
|
|
7d6b925802 | ||
|
|
1c2ab3341a | ||
|
|
7fb9d43cea | ||
|
|
bb6a0a8104 | ||
|
|
b83fb1e1bb | ||
|
|
969c13fba1 | ||
|
|
bcf5484f49 | ||
|
|
36f4e4856b | ||
|
|
f90e811498 | ||
|
|
029781b7c2 | ||
|
|
4f4a82993c | ||
|
|
9e88620428 | ||
|
|
721413cd52 | ||
|
|
71793eb3d3 | ||
|
|
eb118bf32a | ||
|
|
28e0e9baf5 | ||
|
|
195d205a93 | ||
|
|
eea5047a2a | ||
|
|
5eea6d8b91 | ||
|
|
3242d9c24f | ||
|
|
3cbe02774f | ||
|
|
025032cbe9 | ||
|
|
a94a44c43d | ||
|
|
076af78f83 | ||
|
|
7b98d932b2 | ||
|
|
c1e934c7a1 | ||
|
|
df54b56f50 | ||
|
|
c5fe9fb575 | ||
|
|
4a8c56de95 | ||
|
|
6d713a3cc0 | ||
|
|
dec11bf971 | ||
|
|
df9354937b | ||
|
|
d2c81220dc | ||
|
|
3182eb0112 | ||
|
|
3917c09837 | ||
|
|
897fee33b7 | ||
|
|
db7149229a | ||
|
|
e563c5cbeb | ||
|
|
91993ae4a6 | ||
|
|
d8d9c12e07 | ||
|
|
83150d0654 | ||
|
|
adcdedea37 | ||
|
|
87c481a558 | ||
|
|
ce9bc66b71 | ||
|
|
8919997106 | ||
|
|
36ed0c7288 | ||
|
|
e3aaf8b6f1 | ||
|
|
b563f67867 | ||
|
|
fe13d7d708 | ||
|
|
b3b57e12fa | ||
|
|
ad79c9a7d6 | ||
|
|
015252323a | ||
|
|
b202daa621 | ||
|
|
8570afbefa | ||
|
|
469b2baf38 | ||
|
|
ac30702679 | ||
|
|
6fc6103576 | ||
|
|
6e9f34809c | ||
|
|
786f752143 | ||
|
|
52e27e22a0 | ||
|
|
bc003712ff | ||
|
|
da6e0435d5 | ||
|
|
7868273f74 | ||
|
|
de23ba41de | ||
|
|
5ad5be396f | ||
|
|
c8f0e8be79 | ||
|
|
23aa58c76d | ||
|
|
6a48001a9e | ||
|
|
7151dc73a0 | ||
|
|
029cc39c7f | ||
|
|
82526addad | ||
|
|
aa131b2e09 | ||
|
|
878c96ec88 | ||
|
|
d098a0f84d | ||
|
|
43328239c9 | ||
|
|
fab31ee2ee | ||
|
|
66ee4ef452 | ||
|
|
3806581020 | ||
|
|
cdb9b7aec6 | ||
|
|
081d8c41a1 | ||
|
|
b35fd3292c | ||
|
|
7aa368daca | ||
|
|
73dce8ba03 | ||
|
|
92f6e0de16 | ||
|
|
082df0cd09 | ||
|
|
55aad881e2 | ||
|
|
ae15d0e730 | ||
|
|
c02e945309 | ||
|
|
d66a798a99 | ||
|
|
a1644fe137 | ||
|
|
e872af804e | ||
|
|
43856c1fc1 | ||
|
|
c7a096d011 | ||
|
|
291d3ef446 | ||
|
|
b94901d741 | ||
|
|
f2cef5d080 | ||
|
|
706b3bec25 | ||
|
|
f47bc68a39 | ||
|
|
2c6d5ffb28 | ||
|
|
5f888e9c3e | ||
|
|
0f088a0708 | ||
|
|
d176696fc9 | ||
|
|
cadc5aace7 | ||
|
|
57e0c5c790 | ||
|
|
75057f8c42 | ||
|
|
4dc9f05907 |
100
CHANGELOG.md
Normal file
100
CHANGELOG.md
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
### 5.5.4
|
||||||
|
|
||||||
|
* Added `icon` meta tag
|
||||||
|
* Added `private_mode` meta tag
|
||||||
|
* Added `calendar:enabled_calendar_ids()` method
|
||||||
|
|
||||||
|
### 5.5.1
|
||||||
|
|
||||||
|
* Added `calendar:is_holiday()` method
|
||||||
|
|
||||||
|
### 5.5.0
|
||||||
|
|
||||||
|
* Added `aio:add_todo()` method
|
||||||
|
|
||||||
|
### 5.3.5
|
||||||
|
|
||||||
|
* Added `ai` module
|
||||||
|
|
||||||
|
### 5.3.1
|
||||||
|
|
||||||
|
* Added `string:trim()`, `string:starts_with()` and `string:ends_with()` methods
|
||||||
|
|
||||||
|
### 5.3.0
|
||||||
|
|
||||||
|
* Added `prefs:show_dialog` method
|
||||||
|
* Added `system:show_notify()` and `system:cancel_notify()` methods
|
||||||
|
* Added support for SVG icons to the Rich UI API
|
||||||
|
|
||||||
|
### 5.2.3
|
||||||
|
|
||||||
|
* Added `on_load()` callback
|
||||||
|
* Added `on_resume_when_folding` meta tag
|
||||||
|
|
||||||
|
### 5.2.1
|
||||||
|
|
||||||
|
* Added support for complex UIs
|
||||||
|
* Added `ui:set_progress()` function
|
||||||
|
|
||||||
|
### 5.2.0
|
||||||
|
|
||||||
|
* Added `widgets` module to app widgets interaction
|
||||||
|
|
||||||
|
### 5.1.0
|
||||||
|
|
||||||
|
* Added `add_purchase` action
|
||||||
|
* Added `on_contacts_loaded()` callback
|
||||||
|
|
||||||
|
### 4.9.4
|
||||||
|
|
||||||
|
* The `aio:actions()` function now also returns arguments format for each action
|
||||||
|
* Added function `system:format_date_localized()`
|
||||||
|
|
||||||
|
### 4.9.2
|
||||||
|
|
||||||
|
* The `apps` module now has an `app()` function that returns selected app table
|
||||||
|
* You can use `%%fa:ICON_NAME%%` tag in the any text to show FontAwesome icon inside the text
|
||||||
|
|
||||||
|
### 4.9.0
|
||||||
|
|
||||||
|
* The `apps` module now has an `apps()` function that returns a table with app details, including the app icon;
|
||||||
|
* The `phone:contacts()` function now returns an icon that can be used in the side menu;
|
||||||
|
* The `apps:request_icons()` and `phone:request_icons()` functions are deprecated.
|
||||||
|
|
||||||
|
### 4.8.0
|
||||||
|
|
||||||
|
* Side menu scripts support;
|
||||||
|
* New modules: `tasks` and `notes`;
|
||||||
|
* The `ui:colors()` can be called as `aio:colors()`;
|
||||||
|
* The `apps` and `phone` modules now returns icons;
|
||||||
|
* The `apps` modules now returns also Android for Work and cloned apps;
|
||||||
|
* The `apps` module now allows you to sort apps by category;
|
||||||
|
* The `phone` and `calendar` modules now have functions for requesting access rights;
|
||||||
|
* Added `phone:open_contact() function;
|
||||||
|
* Added `aio:actions()` function that returns a table of AIO Launcher actions;
|
||||||
|
* Added `calendar:open_new_event()` function that shows the system calendar with the new event.
|
||||||
|
* Added `aio:settings()` and `aio:open_settings()` functions
|
||||||
|
|
||||||
|
### Older versions
|
||||||
|
|
||||||
|
* 4.0.0 - first version with scripts support;
|
||||||
|
* 4.1.0 - added `weather` and `cloud` modules;
|
||||||
|
* 4.1.3 - added `notify`, `files` and `utils` modules;
|
||||||
|
* 4.1.5 - extended `notify` module, added `folded_string` arg to `ui:show_lines`;
|
||||||
|
* 4.3.0 - search scripts support;
|
||||||
|
* 4.4.0 - markdown support;
|
||||||
|
* 4.4.1 - rich text editor support;
|
||||||
|
* 4.4.2 - added `fmt` and `html` utility modules;
|
||||||
|
* 4.4.4 - added `tasker` module;
|
||||||
|
* 4.4.6 - added `csv` module;
|
||||||
|
* 4.4.7 - added `intent` module;
|
||||||
|
* 4.5.0 - the `aio` module has been significantly expanded, also added `system:currency()` and `ui:show_list_dialog()`;
|
||||||
|
* 4.5.2 - added `anim` and `morph` packages, added `calendar:open_event()` function;
|
||||||
|
* 4.5.3 - removed get_ prefixes where they are not needed while maintaining backward compatibility;
|
||||||
|
* 4.5.5 - added `on_action` callback and `calendar:add_event` function;
|
||||||
|
* 4.5.6 - `aio:active_widgets()` now returns also widget `label`, added `checks` module;
|
||||||
|
* 4.5.7 - added "fold" and "unfold" actions to the `on_action` callback;
|
||||||
|
* 4.6.0 - added `system:request_location()` and `tasker:send_command()` functions;
|
||||||
|
* 4.7.0 - added `ui:build()` and functions to display search results in different formats;
|
||||||
|
* 4.7.1 - fontawesome updated to version 6.3.0;
|
||||||
|
* 4.7.4 - added `prefs` module, `settings` module is deprecated.
|
||||||
602
README.md
602
README.md
@@ -2,63 +2,80 @@
|
|||||||
|
|
||||||
Starting from version 4.0, AIO Launcher supports scripts written in the [Lua scripting language](https://en.wikipedia.org/wiki/Lua_(programming_language)). Scripts should be placed in the directory `/sdcard/Android/data/ru.execbit.aiolauncher/files/`.
|
Starting from version 4.0, AIO Launcher supports scripts written in the [Lua scripting language](https://en.wikipedia.org/wiki/Lua_(programming_language)). Scripts should be placed in the directory `/sdcard/Android/data/ru.execbit.aiolauncher/files/`.
|
||||||
|
|
||||||
There are two types of scripts:
|
There are three types of scripts:
|
||||||
|
|
||||||
* _Widget scripts_, which can be added to the desktop using the side menu.
|
* _Widget scripts_, which can be added to the desktop using the side menu.
|
||||||
* _Search scripts_ that add results to the search box. These can be enabled in the settings.
|
* _Search scripts_ that add results to the search box. These can be enabled in the settings.
|
||||||
|
* _Side menu scripts_ that change the side menu.
|
||||||
|
|
||||||
The type of script is determined by the line (meta tag) at the beginning of the file:
|
The type of script is determined by the line (meta tag) at the beginning of the file:
|
||||||
|
|
||||||
* `-- type = "widget"`
|
* `-- type = "widget"`
|
||||||
* `-- type = "search"`
|
* `-- type = "search"`
|
||||||
|
* `-- type = "drawer"`
|
||||||
|
|
||||||
*Read more about meta tags at the end of the document.*
|
*Read more about meta tags at the end of the document.*
|
||||||
|
|
||||||
|
# Links
|
||||||
|
|
||||||
|
* [Step-by-step guide to writing scripts for AIO Launcher](README_INTRO.md)
|
||||||
|
* [AIO Scripting Telegram Group](https://t.me/aio_scripting)
|
||||||
|
* [AIO Script Store app](https://play.google.com/store/apps/details?id=ru.execbit.aiostore)
|
||||||
|
* [Lua Guide](https://www.lua.org/pil/contents.html)
|
||||||
|
* [Many Lua samples](http://lua-users.org/wiki/SampleCode)
|
||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
* 4.0.0 - first version with scripts support;
|
### 5.7.1
|
||||||
* 4.1.0 - added `weather` and `cloud` modules;
|
|
||||||
* 4.1.3 - added `notify`, `files` and `utils` modules;
|
* Added `tags` field to the app table
|
||||||
* 4.1.5 - extended `notify` module, added `folded_string` arg to `ui:show_lines`;
|
* Added `system:tz()` method
|
||||||
* 4.3.0 - search scripts support;
|
|
||||||
* 4.4.0 - markdown support;
|
### 5.7.0
|
||||||
* 4.4.1 - rich text editor support;
|
|
||||||
* 4.4.2 - added `fmt` and `html` utility modules;
|
* Added `ui:show_image(uri)` method
|
||||||
* 4.4.4 - added `tasker` module;
|
* Many changes in the `notify` module
|
||||||
* 4.4.6 - added `csv` module;
|
|
||||||
* 4.4.7 - added `intent` module;
|
### 5.6.1
|
||||||
* 4.5.0 - the `aio` module has been significantly expanded, also added `system:currency()` and `ui:show_list_dialog()`;
|
|
||||||
* 4.5.2 - added `anim` and `morph` packages, added `calendar:open_event()` function;
|
* Added `ui:set_expandable()` and `ui:is_expanded()` methods
|
||||||
* 4.5.3 - removed get_ prefixes where they are not needed while maintaining backward compatibility;
|
|
||||||
* 4.5.5 - added `on_action` callback and `calendar:add_event` function;
|
### 5.6.0
|
||||||
* 4.5.6 - `aio:active_widgets()` now returns also widget `label`, added `checks` module;
|
|
||||||
* 4.5.7 - added "fold" and "unfold" actions to the `on_action` callback;
|
* Added `ui:set_edit_mode_buttons()` method
|
||||||
* 4.6.0 - added `system:request_location()` and `tasker:send_command()` functions.
|
* Added size argument to `widgets:request_updates()` method
|
||||||
|
|
||||||
|
[Full changelog](CHANGELOG.md)
|
||||||
|
|
||||||
# Widget scripts
|
# Widget scripts
|
||||||
|
|
||||||
The work of the widget script begins with one of the three described functions. Main work should be done in one of them.
|
The work of the widget script begins with one of the three described functions. Main work should be done in one of them.
|
||||||
|
|
||||||
|
* `on_load()` - called on first script load (_starting with AIO Launcher 5.2.3_).
|
||||||
* `on_resume()` - called every time you return to the desktop.
|
* `on_resume()` - called every time you return to the desktop.
|
||||||
* `on_alarm()` - called every time you return to the desktop, but no more than once every 30 minutes.
|
* `on_alarm()` - called every time you return to the desktop, but no more than once every 30 minutes.
|
||||||
* `on_tick(ticks)` - called every second while the launcher is on the screen. The `ticks` parameter is number of seconds after last return to the launcher.
|
* `on_tick(ticks)` - called every second while the launcher is on the screen. The `ticks` parameter is number of seconds after last return to the launcher.
|
||||||
|
|
||||||
The `on_resume()` and `on_alarm()` callbacks are also triggered when a widget is added to the screen and the screen is forced to refresh.
|
The `on_resume()` and `on_alarm()` callbacks are also triggered when a widget is added to the screen (if `on_load()` is not defined) and the screen is forced to refresh.
|
||||||
|
|
||||||
For most network scripts `on_alarm()` should be used.
|
For most network scripts `on_alarm()` should be used.
|
||||||
|
|
||||||
# Search scripts
|
# Search scripts
|
||||||
|
|
||||||
Unlike widget scripts, search scripts are launched only when you open the search window. Then the following function is triggered each time a character is entered:
|
Unlike widget scripts, search scripts are launched only when you open the search window:
|
||||||
|
|
||||||
|
* `on_load()` - called every time when user opens the search window (_starting with AIO Launcher 5.2.3_).
|
||||||
|
|
||||||
|
Then the following function is triggered each time a character is entered:
|
||||||
|
|
||||||
* `on_search(string)` is run when each character is entered, `string` - entered string.
|
* `on_search(string)` is run when each character is entered, `string` - entered string.
|
||||||
|
|
||||||
The search script can use two functions to display search results:
|
The search script can use two functions to display search results:
|
||||||
|
|
||||||
* `search:show(strings, [colors])` - shows results BELOW all other results;
|
* `search:show_buttons(names, [colors], [top])` - show buttons in the search results, the first parameter is table of button names, second - table of button colors in format `#XXXXXX`, `top` - whether the results need to be shown at the top (false by default);
|
||||||
* `search:show_top(strings, [colors])` - shows results BEFORE all other results.
|
* `search:show_lines(lines, [colors], [top])` - show text lines in the search results;
|
||||||
|
* `search:show_progress(names, progresses, [colors], [top])` - show progress bars in the search results, the first parameter is a table of names, the second is a table of progress bars (from 1 to 100);
|
||||||
Both functions take a table with search results strings as the first argument. Second optional parameter: table with colors of results in format `#XXXXXX`.
|
* `search:show_chart(points, format, [title], [show_grid], [top])` - show chart in the search results, parameters are analogous to `ui:show_chart()`.
|
||||||
|
|
||||||
When user click on a result, one of the following functions will be executed:
|
When user click on a result, one of the following functions will be executed:
|
||||||
|
|
||||||
@@ -75,33 +92,78 @@ If you want the script to respond only to search queries that have a word in the
|
|||||||
|
|
||||||
If you put such a tag at the beginning of your script, its `on_search()` function will only be called if a user types something like "youtube funny video" or "yt funny video". The prefix itself will be removed before being passed to the function.
|
If you put such a tag at the beginning of your script, its `on_search()` function will only be called if a user types something like "youtube funny video" or "yt funny video". The prefix itself will be removed before being passed to the function.
|
||||||
|
|
||||||
|
# Side menu scripts
|
||||||
|
|
||||||
|
With side menu scripts, you can display your own list in the menu. The script can be selected by pulling the menu down.
|
||||||
|
|
||||||
|
The script starts with the following function:
|
||||||
|
|
||||||
|
* `on_drawer_open()`
|
||||||
|
|
||||||
|
In this function, you must prepare a list and display it using one of the following functions:
|
||||||
|
|
||||||
|
* `drawer:show_list(lines, [icons], [badges], [show_alphabet])` - shows lines, optionally you can specify a table of icons (in `fa:icon_name` format), a table of lines to be displayed in badges and pass a boolean value: whether to show the alphabet;
|
||||||
|
* `drawer:show_ext_list(lines, [max_lines)` - shows multiline lines, optionally you can specify the maximum number of lines of each element (default is 5).
|
||||||
|
|
||||||
|
The following functions are also available:
|
||||||
|
|
||||||
|
* `drawer:add_buttons(icons, default_index)` - shows icons at the bottom of the menu (in `fa:icon_name` format), `default_index` is the index of the selected icon;
|
||||||
|
* `drawer:clear()` - clears the list;
|
||||||
|
* `drawer:close()` - closes the menu;
|
||||||
|
* `drawer:change_view(name)` - switches the menu to another script or display style (argument: either script file name, `sortable` or `categories`).
|
||||||
|
|
||||||
|
Clicking on list items will call `on_click(index)`, long-clicking will call `on_long_click(index)`, clicking a bottom icon will call `on_button_click(index)`.
|
||||||
|
|
||||||
|
The list output functions support HTML and Markdown (see User Interface section for details).
|
||||||
|
|
||||||
# API Reference
|
# API Reference
|
||||||
|
|
||||||
## User Interface
|
## User Interface
|
||||||
|
|
||||||
_Available only in widget scripts._
|
_Available only in widget scripts._
|
||||||
|
|
||||||
|
_AIO Launcher also offers a way to create more complex UIs: [instructions](README_RICH_UI.md)_
|
||||||
|
|
||||||
* `ui:show_text(string)` - displays plain text in widget, repeated call will erase previous text;
|
* `ui:show_text(string)` - displays plain text in widget, repeated call will erase previous text;
|
||||||
* `ui:show_lines(table, [table], [folded_string])` - displays a list of lines with the sender (in the manner of a mail widget), the second argument (optional) - the corresponding senders (formatting in the style of a mail widget), folded\_string (optional) - string to be shown in folded mode;
|
* `ui:show_lines(table, [table], [folded_string])` - displays a list of lines with the sender (in the manner of a mail widget), the second argument (optional) - the corresponding senders (formatting in the style of a mail widget), folded\_string (optional) - string to be shown in folded mode;
|
||||||
* `ui:show_table(table, [main_column], [centering], [folded_value])` - displays table, first argument: table of tables, second argument: main column, it will be stretched, occupying main table space (if argument is zero or not specified all table elements will be stretched evenly), third argument: boolean value indicating whether table cells should be centered, fourth argument: string or table to be shown in folded mode;
|
* `ui:show_table(table, [main_column], [centering], [folded_value])` - displays table, first argument: table of tables, second argument: main column, it will be stretched, occupying main table space (if argument is zero or not specified all table elements will be stretched evenly), third argument: boolean value indicating whether table cells should be centered, fourth argument: string or table to be shown in folded mode;
|
||||||
* `ui:show_buttons(names, [colors])` - displays a list of buttons, the first argument is a table of strings, the second is an optional argument, a table of colors in the format #XXXXXX;
|
* `ui:show_buttons(names, [colors])` - displays a list of buttons, the first argument is a table of strings, the second is an optional argument, a table of colors in the format #XXXXXX;
|
||||||
* `ui:show_progress_bar(text, current_value, max_value, [color])` - shows the progress bar;
|
* `ui:show_progress_bar(text, current_value, max_value, [color])` - shows the progress bar;
|
||||||
* `ui:show_chart(points, [format], [title], [show_grid], [folded_string], [copyright])` - shows the chart, points - table of coordinate tables, format - data format (see below), title - chart name, show\_grid - grid display flag, folded\_string - string for the folded state (otherwise the name will be shown), copyright - string displayed in the lower right corner;
|
* `ui:show_chart(points, [format], [title], [show_grid], [folded_string], [copyright])` - shows the chart, points - table of coordinate tables, format - data format (see below), title - chart name, show\_grid - grid display flag, folded\_string - string for the folded state (otherwise the name will be shown), copyright - string displayed in the lower right corner;
|
||||||
|
* `ui:show_image(uri)` - show image by URL;
|
||||||
* `ui:show_toast(string)` - shows informational message in Android style;
|
* `ui:show_toast(string)` - shows informational message in Android style;
|
||||||
* `ui:default_title()` - returns the standard widget title (set in the `name` metadata);
|
* `ui:default_title()` - returns the standard widget title (set in the `name` metadata);
|
||||||
* `ui:set_title()` - changes the title of the widget, should be called before the data display function (empty line - reset to the standard title);
|
* `ui:set_title()` - changes the title of the widget, should be called before the data display function (empty line - reset to the standard title);
|
||||||
* `ui:set_folding_flag(boolean)` - sets the flag of the folded mode of the widget, the function should be called before the data display functions;
|
* `ui:set_folding_flag(boolean)` - sets the flag of the folded mode of the widget, the function should be called before the data display functions;
|
||||||
* `ui:folding_flag()` - returns folding flag;
|
* `ui:folding_flag()` - returns folding flag;
|
||||||
* `ui:colors()` - returns table with current theme colors;
|
* `ui:set_expandable()` - shows expand button on widget update;
|
||||||
|
* `ui:is_expanded()` - checks if expanded mode is enabled;
|
||||||
When you click on any element of the interface, the `on_click(number)` callback will be executed, where number is the ordinal number of the element. A long click calls `on_long_click(number)`. For example, if you use `ui:show_buttons` to show three buttons, then clicking the first button will call `on_click` with argument 1, the second with arguments 2, and so on. If there is only one element on the screen, the argument will always be equal to one and can be omitted.
|
* `ui:set_progress(float)` - sets current widget progress (like in Player and Health widgets);
|
||||||
|
* `ui:set_edit_mode_buttons(table)` - adds icons listed in the table (formatted as `"fa:name"`) to the edit mode. When an icon is clicked, the function `on_edit_mode_button_click(index)` will be called.
|
||||||
|
|
||||||
The `ui:show_chart()` function takes a string as its third argument to format the x and y values on the screen. For example, the string `x: date y: number` means that the X-axis values should be formatted as dates, and the Y-values should be formatted as a regular number. There are four formats in total:
|
The `ui:show_chart()` function takes a string as its third argument to format the x and y values on the screen. For example, the string `x: date y: number` means that the X-axis values should be formatted as dates, and the Y-values should be formatted as a regular number. There are four formats in total:
|
||||||
|
|
||||||
* `number` - an ordinary number with group separation;
|
* `number` - an ordinary number with group separation;
|
||||||
* `float` - the same, but with two decimal places;
|
* `float` - the same, but with two decimal places;
|
||||||
* `date` - date in day.month format;
|
* `date` - date in day.month format;
|
||||||
* `time` - time in hours:minutes format.
|
* `time` - time in hours:minutes format;
|
||||||
|
* `none` - disable.
|
||||||
|
|
||||||
|
### Clicks
|
||||||
|
|
||||||
|
When you click on any element of the interface, the `on_click(number)` callback will be executed, where number is the ordinal number of the element. A long click calls `on_long_click(number)`. For example, if you use `ui:show_buttons` to show three buttons, then clicking the first button will call `on_click` with argument 1, the second with arguments 2, and so on. If there is only one element on the screen, the argument will always be equal to one and can be omitted.
|
||||||
|
|
||||||
|
### Folding
|
||||||
|
|
||||||
|
By default, the script shows either the first line of content or a line specified in the function argument in collapsed mode. However, you can change this behavior using a special meta-tag:
|
||||||
|
|
||||||
|
```
|
||||||
|
-- on_resume_when_folding = "true"
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, the `on_resume()` callback will be triggered each time the widget is collapsed. Then you can check the widget's collapsed status using the `ui:folding_flag()` function and display different UI depending on the state of this flag.
|
||||||
|
|
||||||
|
### HTML and Markdown formatting
|
||||||
|
|
||||||
The functions `ui:show_text()`, `ui:show_lines()` and `ui:show_table()` support many HTML tags. For example:
|
The functions `ui:show_text()`, `ui:show_lines()` and `ui:show_table()` support many HTML tags. For example:
|
||||||
|
|
||||||
@@ -114,17 +176,29 @@ First line<br/> Second line
|
|||||||
|
|
||||||
You can also use Markdown markup. To do this, add the prefix `%%mkd%%` to the beginning of the line. Or you can disable the formatting completely with the prefix `%%txt%%`.
|
You can also use Markdown markup. To do this, add the prefix `%%mkd%%` to the beginning of the line. Or you can disable the formatting completely with the prefix `%%txt%%`.
|
||||||
|
|
||||||
|
_Keep in mind: HTML formatting and icons will not work if you use the second parameter in `ui:show_lines()`._
|
||||||
|
|
||||||
|
### Icons
|
||||||
|
|
||||||
|
You can insert FontAwesome icons inside the text, to do this use this syntax: `%%fa:ICON_NAME%%. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
ui:show_text("<b>This</b> is the text with icons %%fa:face-smile%% %%fa:poo%% <i>and styles</i>")
|
||||||
|
```
|
||||||
|
|
||||||
The `ui:show_buttons()` function supports Fontawesome icons. Simply specify `fa:icon_name` as the button name, for example: `fa:play`.
|
The `ui:show_buttons()` function supports Fontawesome icons. Simply specify `fa:icon_name` as the button name, for example: `fa:play`.
|
||||||
|
|
||||||
|
_Note: AIO only supports icons up to Fontawesome 6.3.0._
|
||||||
|
|
||||||
## Dialogs
|
## Dialogs
|
||||||
|
|
||||||
_Available only in widget scripts._
|
_Available only in widget scripts._
|
||||||
|
|
||||||
* `ui:show_dialog(title, text, [button1_text], [button2_text])` - show dialog, the first argument is the title, the second is the text, button1\_text is the name of the first button, button2\_text is the name of the second button;
|
* `dialogs:show_dialog(title, text, [button1_text], [button2_text])` - show dialog, the first argument is the title, the second is the text, button1\_text is the name of the first button, button2\_text is the name of the second button;
|
||||||
* `ui:show_edit_dialog(title, [text], [default_value])` - show the dialog with the input field: title - title, text - signature, default\_value - standard value of the input field;
|
* `dialogs:show_edit_dialog(title, [text], [default_value])` - show the dialog with the input field: title - title, text - signature, default\_value - standard value of the input field;
|
||||||
* `ui:show_radio_dialog (title, lines, [index])` - show a dialog with a choice: title - title, lines - table of lines, index - index of the default value;
|
* `dialogs:show_radio_dialog (title, lines, [index])` - show a dialog with a choice: title - title, lines - table of lines, index - index of the default value;
|
||||||
* `ui:show_checkbox_dialog(title, lines, [table])` - show dialog with selection of several elements: title - title, lines - table of lines, table - table default values;
|
* `dialogs:show_checkbox_dialog(title, lines, [table])` - show dialog with selection of several elements: title - title, lines - table of lines, table - table default values;
|
||||||
* `ui:show_list_dialog(prefs)` - shows dialog with list of data.
|
* `dialogs:show_list_dialog(prefs)` - shows dialog with list of data.
|
||||||
|
|
||||||
Dialog button clicks should be handled in the `on_dialog_action(number)` callback, where 1 is the first button, 2 is the second button, and -1 is nothing (dialog just closed). `ui:show_radio_dialog()` returns the index of the selected item or -1 in case the cancel button was pressed. `ui:show_checkbox_dialog()` returns the table of indexes or -1. `ui:show_edit_dialog()` returns text or -1.
|
Dialog button clicks should be handled in the `on_dialog_action(number)` callback, where 1 is the first button, 2 is the second button, and -1 is nothing (dialog just closed). `ui:show_radio_dialog()` returns the index of the selected item or -1 in case the cancel button was pressed. `ui:show_checkbox_dialog()` returns the table of indexes or -1. `ui:show_edit_dialog()` returns text or -1.
|
||||||
|
|
||||||
@@ -144,7 +218,7 @@ List dialog accepts table as argument:
|
|||||||
|
|
||||||
_Avaialble from: 4.4.1_
|
_Avaialble from: 4.4.1_
|
||||||
|
|
||||||
* `ui:show_rich_editor(prefs)` - show complex editor dialog with undo support, lists, time, colors support. It can be used to create notes and tasks style widgets.
|
* `dialogs:show_rich_editor(prefs)` - show complex editor dialog with undo support, lists, time, colors support. It can be used to create notes and tasks style widgets.
|
||||||
|
|
||||||
Dialog accepts table as argument:
|
Dialog accepts table as argument:
|
||||||
|
|
||||||
@@ -189,11 +263,63 @@ Here `share`, `copy` and `trash` are the names of the icons, which can be found
|
|||||||
|
|
||||||
When you click on any menu item, the collback `on_context_menu_click(idx)` will be called, where `idx` is the index of the menu item.
|
When you click on any menu item, the collback `on_context_menu_click(idx)` will be called, where `idx` is the index of the menu item.
|
||||||
|
|
||||||
|
## Meta widgets
|
||||||
|
|
||||||
|
_Avaialble from: 4.7.0_
|
||||||
|
|
||||||
|
* `ui:build(table)` - constructs a widget from pieces of other AIO widgets.
|
||||||
|
|
||||||
|
The function takes a command table of this format as a parameter:
|
||||||
|
|
||||||
|
```
|
||||||
|
`battery` - battery progress bar;
|
||||||
|
`ram` - ram progress bar;
|
||||||
|
`nand` - nand progress bar;
|
||||||
|
`traffic` - traffic progress bar;
|
||||||
|
`screen` - screen time progress bar;
|
||||||
|
`alarm` - next alarm info;
|
||||||
|
`clock` - current time;
|
||||||
|
`notes [NUM]` - last NUM notes;
|
||||||
|
`tasks [NUM]` - last NUM tasks;
|
||||||
|
`calendar [NUM]` - last NUM calendar events;
|
||||||
|
`calendarw` - weekly calendar;
|
||||||
|
`exchage [NUM] [FROM] [TO]` - exchange rate FROM currency TO currency;
|
||||||
|
`player` - player controls;
|
||||||
|
`weather [NUM]` - weather forecast for NUM days;
|
||||||
|
`worldclock [TIME_ZONE]` - time in the given TIME_ZONE;
|
||||||
|
`notify [NUM]` - last NUM notifications;
|
||||||
|
`dialogs [NUM]` - last NUM dialogs;
|
||||||
|
`calculator` - calculator;
|
||||||
|
`feed [NUM]` - news feed;
|
||||||
|
`control` - control panel;
|
||||||
|
`stopwatch` - stopwatch;
|
||||||
|
`finance [NUM]` - finance tickers;
|
||||||
|
`financechart` - finance chart;
|
||||||
|
`contacts [NUM]` - contacts (number of lines);
|
||||||
|
`apps [NUM]` - frequent apps (number of lines);
|
||||||
|
`appbox [NUM]` - my apps (number of lines);
|
||||||
|
`applist [NUM]` - apps list (number of lines);
|
||||||
|
`appfolders [NUM]` - app folders;
|
||||||
|
`appcategories [NUM]` - app categries;
|
||||||
|
`timer` - timers;
|
||||||
|
`mailbox [NUM]` - mail widget;
|
||||||
|
`dialer` - dialer;
|
||||||
|
`recorder` - recorder;
|
||||||
|
`telegram` - telegram messages;
|
||||||
|
`smartspacer` - SmartSpacer;
|
||||||
|
`widgetscontainer` - widgets container;
|
||||||
|
`tips` - tips;
|
||||||
|
`text [TEXT]` - just shows TEXT;
|
||||||
|
`space [NUM]` - NUM of spaces.
|
||||||
|
```
|
||||||
|
|
||||||
|
[Sample](samples/build_ui_sample.lua).
|
||||||
|
|
||||||
## System
|
## System
|
||||||
|
|
||||||
* `system:open_browser(url)` - opens the specified URL in a browser or application that can handle this type of URL;
|
* `system:open_browser(url)` - opens the specified URL in a browser or application that can handle this type of URL;
|
||||||
* `system:exec(string)` - executes a shell command;
|
* `system:exec(string, [id])` - executes a shell command;
|
||||||
* `system:su(string)` - executes a shell command as root;
|
* `system:su(string, [id])` - executes a shell command as root;
|
||||||
* `system:location()` - returns the location in the table with two values (location request is NOT executed, the value previously saved by the system is used);
|
* `system:location()` - returns the location in the table with two values (location request is NOT executed, the value previously saved by the system is used);
|
||||||
* `system:request_location()` - queries the current location and returns it to the `on_location_result` callback;
|
* `system:request_location()` - queries the current location and returns it to the `on_location_result` callback;
|
||||||
* `system:to_clipboard(string)` - copies the string to the clipboard;
|
* `system:to_clipboard(string)` - copies the string to the clipboard;
|
||||||
@@ -202,14 +328,33 @@ When you click on any menu item, the collback `on_context_menu_click(idx)` will
|
|||||||
* `system:alarm_sound(seconds)` - make alarm sound;
|
* `system:alarm_sound(seconds)` - make alarm sound;
|
||||||
* `system:share_text(string)` - opens the "Share" system dialog;
|
* `system:share_text(string)` - opens the "Share" system dialog;
|
||||||
* `system:lang()` - returns the language selected in the system;
|
* `system:lang()` - returns the language selected in the system;
|
||||||
|
* `system:tz()` - returns TimeZone string (example: Africa/Cairo);
|
||||||
* `system:tz_offset()` - returns TimeZone offset in seconds;
|
* `system:tz_offset()` - returns TimeZone offset in seconds;
|
||||||
* `system:currency()` - returns default currency code based on locale;
|
* `system:currency()` - returns default currency code based on locale;
|
||||||
|
* `system:format_date_localized(format, date)` - returns localized date string (using java formatting);
|
||||||
* `system:battery_info()` - returns table with battery info;
|
* `system:battery_info()` - returns table with battery info;
|
||||||
* `system:system_info()` - returns table with system info.
|
* `system:system_info()` - returns table with system info.
|
||||||
|
|
||||||
The result of executing a shell command is sent to the `on_shell_result(string)` callback.
|
The result of executing a shell command is sent to the `on_shell_result(string)` or `on_shell_result_$id(string)` (_starting from AIO 5.7.5_) callback.
|
||||||
|
|
||||||
## Intens
|
* `system:show_notify(table)` - show system notifycation;
|
||||||
|
* `system:cancel_notify()` - cancel notification.
|
||||||
|
|
||||||
|
These two functions can be used to display, update, and delete system notifications. The possible fields for the `table` (each of them is optional) are:
|
||||||
|
|
||||||
|
```
|
||||||
|
`message` - the message displayed in the notification;
|
||||||
|
`silent` - true if the notification should be silent;
|
||||||
|
`action1` - the name of the first notification action;
|
||||||
|
`action2` - the name of the second notification action;
|
||||||
|
`action3` - the name of the third notification action.
|
||||||
|
```
|
||||||
|
|
||||||
|
When the notification is clicked, the main launcher window will open. When one of the three actions is clicked, the callback `on_notify_action(idx, name)` will be executed with the action's index and name as parameters.
|
||||||
|
|
||||||
|
_Keep in mind that the callback will only be executed for scripts of type `widget`._
|
||||||
|
|
||||||
|
## Intents
|
||||||
|
|
||||||
* `intent:start_activity(table)` - starts activity with intent described in the table;
|
* `intent:start_activity(table)` - starts activity with intent described in the table;
|
||||||
* `intent:send_broadcast(table)` - sends broadcast intent described in the table.
|
* `intent:send_broadcast(table)` - sends broadcast intent described in the table.
|
||||||
@@ -234,23 +379,58 @@ Intent table format (all fields are optional):
|
|||||||
* `aio:fold_widget(string, [boolean])` - fold/unfold widget (if you do not specify the second argument the state will be switched) (_available from: 4.5.0_);
|
* `aio:fold_widget(string, [boolean])` - fold/unfold widget (if you do not specify the second argument the state will be switched) (_available from: 4.5.0_);
|
||||||
* `aio:is_widget_added(string)` - checks if the widget is added to the screen;
|
* `aio:is_widget_added(string)` - checks if the widget is added to the screen;
|
||||||
* `aio:self_name()` - returns current script file name (_available from: 4.5.0_);
|
* `aio:self_name()` - returns current script file name (_available from: 4.5.0_);
|
||||||
|
* `aio:send_message(value, [script_name])` - sends lua value to other script or scripts (_avaialble from: 4.5.0_);
|
||||||
|
* `aio:colors()` - returns table with current theme colors;
|
||||||
* `aio:do_action(string)` - performs an AIO action ([more](https://aiolauncher.app/api.html));
|
* `aio:do_action(string)` - performs an AIO action ([more](https://aiolauncher.app/api.html));
|
||||||
* `aio:send_message(value, [script_name])` - sends lua value to other script or scripts (_avaialble from: 4.5.0_).
|
* `aio:actions()` - returns a list of available actions;
|
||||||
|
* `aio:settings()` - returns a list of available AIO Settings sections;
|
||||||
|
* `aio:open_settings([section])` - open AIO Settings or AIO Settings section;
|
||||||
|
* `aio:add_todo(icon, text)` - add a TODO item with the specified Fontawesome icon and text.
|
||||||
|
|
||||||
Format of table elements returned by `aio:available_widgets()`:
|
Format of table elements returned by `aio:available_widgets()`:
|
||||||
|
|
||||||
* `name` - internal name of the widget;
|
```
|
||||||
* `type` - widget type: `builtin`, `script` or `plugin`;
|
`name` - internal name of the widget;
|
||||||
* `description` - widget description (usually empty for non-script widgets);
|
`label` - title of the widget;
|
||||||
* `clonable` - true if the widget can have clones (examples: "My apps", "Contacts", "Mailbox" widgets);
|
`type` - widget type: `builtin`, `script` or `plugin`;
|
||||||
* `enabled` - true if the widget is placed on the screen.
|
`description` - widget description (usually empty for non-script widgets);
|
||||||
|
`clonable` - true if the widget can have clones (examples: "My apps", "Contacts", "Mailbox" widgets);
|
||||||
|
`enabled` - true if the widget is placed on the screen.
|
||||||
|
```
|
||||||
|
|
||||||
Format of table elements returned by `aio:active_widgets()`:
|
Format of table elements returned by `aio:active_widgets()`:
|
||||||
|
|
||||||
* `name` - internal name of the widget;
|
```
|
||||||
* `label` - widget visible name;
|
`name` - internal name of the widget;
|
||||||
* `position` - position on the screen;
|
`label` - widget visible name;
|
||||||
* `folded` - true if widget is folded.
|
`position` - position on the screen;
|
||||||
|
`folded` - true if widget is folded.
|
||||||
|
```
|
||||||
|
|
||||||
|
Format of table elements returned by `aio:actions()`:
|
||||||
|
|
||||||
|
```
|
||||||
|
`name` - action name;
|
||||||
|
`short_name` - action short name;
|
||||||
|
`label` - action name visible to the user;
|
||||||
|
`args` - action arguments if any.
|
||||||
|
```
|
||||||
|
|
||||||
|
Format of table elements returned by `aio:colors()`:
|
||||||
|
|
||||||
|
```
|
||||||
|
`primary_text` – base text color;
|
||||||
|
`secondary_text` – color for secondary text (e.g., sender name, time, etc.);
|
||||||
|
`button` – button background color;
|
||||||
|
`button_text` – text color inside buttons;
|
||||||
|
`progress` – general progress bar color;
|
||||||
|
`progress_good` – color for positive progress states (e.g., full battery or charging);
|
||||||
|
`progress_bad` – color for negative progress states (e.g., battery level below 15%);
|
||||||
|
`enabled_icon` – color for enabled icons (see the Control Panel widget);
|
||||||
|
`disabled_icon` – color for disabled icons (see the Control Panel widget);
|
||||||
|
`accent` – accent color;
|
||||||
|
`badge` – badge color.
|
||||||
|
```
|
||||||
|
|
||||||
To accept a value sent by the `send_message` function, the receiving script must implement a callback `on_message(value)`.
|
To accept a value sent by the `send_message` function, the receiving script must implement a callback `on_message(value)`.
|
||||||
|
|
||||||
@@ -272,13 +452,45 @@ function on_action()
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To change the action of the settings icon in the widget's edit menu, you can add the on_settings() function to the script. It will be called every time the user presses the icon.
|
||||||
|
|
||||||
|
```
|
||||||
|
function on_settings()
|
||||||
|
ui:show_toast("Settings icon clicked!")
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
## Application management
|
## Application management
|
||||||
|
|
||||||
* `apps:list([sort_by], [no_hidden])` - returns the package table of all installed applications, `sort_by` - sort option (see below), `no_hidden` - true if no hidden applications are needed;
|
* `apps:apps([sort_by])` - returns the table of tables of all installed applications;
|
||||||
* `apps:name(package)` - returns application name;
|
* `apps:app(package_name)` - return the table of the given application;
|
||||||
* `apps:color(package)` - returns the color of the application in #XXXXXXXX format;
|
|
||||||
* `apps:launch(package)` - launches the application;
|
* `apps:launch(package)` - launches the application;
|
||||||
* `apps:show_edit_dialog(package)` - shows edit dialog of the application.
|
* `apps:show_edit_dialog(package)` - shows edit dialog of the application;
|
||||||
|
* `apps:categories()` - returns a table of category tables.
|
||||||
|
|
||||||
|
The format of the app table:
|
||||||
|
|
||||||
|
```
|
||||||
|
`pkg` - name of the app package (if it is cloned app or Android for Work app package name will also contain user id, like that: `com.example.app:123`);
|
||||||
|
`name` - name of the application;
|
||||||
|
`color` - application color;
|
||||||
|
`hidden` - true if the application is hidden;
|
||||||
|
`suspended` - true if the application is suspended;
|
||||||
|
`category_id` - category ID;
|
||||||
|
`tags` - array of tags;
|
||||||
|
`badge` - number on the badge;
|
||||||
|
`icon` - icon of the application in the form of a link (can be used in the side menu scripts).
|
||||||
|
```
|
||||||
|
|
||||||
|
The format of the category table:
|
||||||
|
|
||||||
|
```
|
||||||
|
`id` - category id (id above 1000 are custom categories);
|
||||||
|
`name` - category name;
|
||||||
|
`icon` - category icon;
|
||||||
|
`color` - category color;
|
||||||
|
`hidden` - the category is hidden by the user.
|
||||||
|
```
|
||||||
|
|
||||||
Sorting options:
|
Sorting options:
|
||||||
|
|
||||||
@@ -305,42 +517,110 @@ If there is a problem with the network, the `on_network_error_$id` callback will
|
|||||||
|
|
||||||
## Calendar
|
## Calendar
|
||||||
|
|
||||||
* `calendar:events([start_date], [end_date], [cal_table])` - returns table of event tables of all calendars, start\_date - event start date, end\_date - event end date, cal\_table - calendar ID table;
|
* `calendar:events([start_date], [end_date], [cal_table])` - returns table of event tables of all calendars, start\_date - event start date, end\_date - event end date, cal\_table - calendar ID table (function will return the string `permission_error` if the launcher does not have permissions to read the calendar);
|
||||||
* `calendar:calendars()` - returns table of calendars tables;
|
* `calendar:calendars()` - returns table of calendars tables;
|
||||||
|
* `calendar:request_permission()` - requests access rights to the calendar;
|
||||||
* `calendar:show_event_dialog(id)` - shows the event dialog;
|
* `calendar:show_event_dialog(id)` - shows the event dialog;
|
||||||
* `calendar:open_ovent(id)` - opens an event in the system calendar;
|
* `calendar:open_event(id|event_table)` - opens an event in the system calendar;
|
||||||
* `calendar:add_event(event_table)` - adds event to the system calendar.
|
* `calendar:open_new_event([start], [end])` - opens a new event in the calendar, `start` - start date of the event in seconds, `end` - end date of the event;
|
||||||
|
* `calendar:add_event(event_table)` - adds event to the system calendar;
|
||||||
|
* `calendar:is_holiday(date)` - returns true if the given date is a holiday or a weekend;
|
||||||
|
* `calendar:enabled_calendar_ids()` - returns list of calendar IDs enabled in the builtin Calendar widget settings.
|
||||||
|
|
||||||
Event table format:
|
Event table format:
|
||||||
|
|
||||||
* `id` - event ID;
|
```
|
||||||
* `calendar_id` - calendar ID;
|
`id` - event ID;
|
||||||
* `title` - title of the event;
|
`calendar_id` - calendar ID;
|
||||||
* `description` - description of the event;
|
`title` - title of the event;
|
||||||
* `location` - address of the event by string;
|
`description` - description of the event;
|
||||||
* `begin` - start time of the event (in seconds);
|
`color` - color of the event;
|
||||||
* `end` - time of the event end (in seconds);
|
`status` - status string of the event or empty;
|
||||||
* `all_day` - boolean value, which means that the event lasts all day.
|
`location` - address of the event by string;
|
||||||
|
`begin` - start time of the event (in seconds);
|
||||||
|
`end` - time of the event end (in seconds);
|
||||||
|
`all_day` - boolean value, which means that the event lasts all day.
|
||||||
|
```
|
||||||
|
|
||||||
Calendar table format:
|
Calendar table format:
|
||||||
|
|
||||||
* `id` - calendar identifier;
|
```
|
||||||
* `name` - name of the calendar;
|
`id` - calendar identifier;
|
||||||
* `color` - color of the calendar in the format #XXXXXXXX.
|
`name` - name of the calendar;
|
||||||
|
`color` - color of the calendar in the format #XXXXXXXX.
|
||||||
|
```
|
||||||
|
|
||||||
|
The function `calendar:request_permission()` calls `on_permission_granted()` callback if the user agrees to grant permission.
|
||||||
|
|
||||||
## Phone
|
## Phone
|
||||||
|
|
||||||
* `phone:contacts()` - returns table of phone contacts;
|
* `phone:contacts()` - returns table of phone contacts (function will return the string `permission_error` if the launcher does not have permissions to read the calendar);
|
||||||
|
* `phone:request_permission()` - requests access rights to the contacts;
|
||||||
* `phone:make_call(number)` - dial the number in the dialer;
|
* `phone:make_call(number)` - dial the number in the dialer;
|
||||||
* `phone:send_sms(number, [text])` - open SMS application and enter the number, optionally enter text;
|
* `phone:send_sms(number, [text])` - open SMS application and enter the number, optionally enter text;
|
||||||
* `phone:show_contact_dialog(id)` - open contact dialog;
|
* `phone:show_contact_dialog(id|lookup_key)` - open contact dialog;
|
||||||
|
* `phone:open_contact(id)` - open contact in the contacts app.
|
||||||
|
|
||||||
Contacts table format:
|
Contacts table format:
|
||||||
|
|
||||||
* `id` - contact id;
|
```
|
||||||
* `lookup_key` - unique contact identifier;
|
`id` - contact id;
|
||||||
* `name` - contact name;
|
`lookup_key` - unique contact identifier;
|
||||||
* `number` - contact number.
|
`name` - contact name;
|
||||||
|
`number` - contact number;
|
||||||
|
`icon` - contact icon in the form of a link (can be used in the side menu scripts).
|
||||||
|
```
|
||||||
|
|
||||||
|
The function `phone:request_permission()` calls `on_permission_granted()` callback if the user agrees to grant permission.
|
||||||
|
|
||||||
|
Upon the first launch of the application, contacts may not yet be loaded, so in the scripts, you can use the `on_contacts_loaded()` callback, which will be called after the contacts are fully loaded.
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
_Avaialble from: 4.8.0_
|
||||||
|
|
||||||
|
* `tasks:load()` - loads tasks;
|
||||||
|
* `tasks:add(task_table)` - adds the task described in the table;
|
||||||
|
* `tasks:remove(task_id)` - removes the task with the specified ID;
|
||||||
|
* `tasks:save(task_table)` - saves the task;
|
||||||
|
* `tasks:show_editor(task_id)` - shows the task editing dialog.
|
||||||
|
|
||||||
|
Once the tasks are loaded, the `on_tasks_loaded()` function will be executed.
|
||||||
|
|
||||||
|
The format of the task table:
|
||||||
|
|
||||||
|
```
|
||||||
|
`id` - task ID;
|
||||||
|
`text` - text;
|
||||||
|
`date` - date of creation;
|
||||||
|
`due_date` - deadline;
|
||||||
|
`completed_date` - task completion date;
|
||||||
|
`high_priority` - flag of high priority;
|
||||||
|
`notification` - flag of notification display;
|
||||||
|
`is_today` - is it today's task?
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
_Avaialble from: 4.8.0_
|
||||||
|
|
||||||
|
* `notes:load()` - loads notes;
|
||||||
|
* `notes:add(note_table)` - adds the note described in the table;
|
||||||
|
* `notes:remove(note_id)` - removes the note with the specified ID;
|
||||||
|
* `notes:save(note_table)` - saves the note;
|
||||||
|
* `notes:colors()` - returns a list of colors (index is the color ID);
|
||||||
|
* `notes:show_editor(note_id)` - shows the note editing dialog.
|
||||||
|
|
||||||
|
Once the notes are loaded, the `on_notes_loaded()` function will be executed.
|
||||||
|
|
||||||
|
The format of the note table:
|
||||||
|
|
||||||
|
```
|
||||||
|
`id` - note ID;
|
||||||
|
`text` - text;
|
||||||
|
`color` - note color ID;
|
||||||
|
`position` - note position on the screen.
|
||||||
|
```
|
||||||
|
|
||||||
## Weather
|
## Weather
|
||||||
|
|
||||||
@@ -350,12 +630,14 @@ _Avaialble from: 4.1.0_
|
|||||||
|
|
||||||
Function returns the weather data in the `on_weather_result(result)` callback, where `result` is a table of tables with the following fields:
|
Function returns the weather data in the `on_weather_result(result)` callback, where `result` is a table of tables with the following fields:
|
||||||
|
|
||||||
* `time` - time in seconds;
|
```
|
||||||
* `temp` - temperature;
|
`time` - time in seconds;
|
||||||
* `icon_code` - code of weather icon;
|
`temp` - temperature;
|
||||||
* `humidity` - humidity;
|
`icon_code` - code of weather icon;
|
||||||
* `wind_speed` - wind speed;
|
`humidity` - humidity;
|
||||||
* `wind_direction` - wind direction.
|
`wind_speed` - wind speed;
|
||||||
|
`wind_direction` - wind direction.
|
||||||
|
```
|
||||||
|
|
||||||
## Cloud
|
## Cloud
|
||||||
|
|
||||||
@@ -370,37 +652,76 @@ _Avaialble from: 4.1.0_
|
|||||||
|
|
||||||
All data are returned in `on_cloud_result(meta, content)`. The first argument is the metadata, either a metadata table (in the case of `list_dir()`) or an error message string. The second argument is the contents of the file (in the case of `get_file()`).
|
All data are returned in `on_cloud_result(meta, content)`. The first argument is the metadata, either a metadata table (in the case of `list_dir()`) or an error message string. The second argument is the contents of the file (in the case of `get_file()`).
|
||||||
|
|
||||||
## Notifications
|
## Profiles
|
||||||
|
|
||||||
|
_Avaialble from: 5.3.6._
|
||||||
|
|
||||||
|
* `profiles:list()` - returns a list of saved profiles;
|
||||||
|
* `profiles:dump(name)` - saves a new profile with the specified name;
|
||||||
|
* `profiles:restore(name)` - restores the saved profile;
|
||||||
|
* `profiles:dump_json()` - creates a new profile but instead of saving it, returns it as a JSON string;
|
||||||
|
* `profiles:restore_json(json)` - restores a profile previously saved using `dump_json()`.
|
||||||
|
|
||||||
|
## AI
|
||||||
|
|
||||||
|
_Avaialble from: 5.3.5._
|
||||||
|
|
||||||
|
_Requires subscription._
|
||||||
|
|
||||||
|
* `ai:complete(text)` - send message to the AI;
|
||||||
|
* `ai:translate(text, lang)` - translate text to the language with code `lang` (`en`, `de`, `es` etc).
|
||||||
|
|
||||||
|
All functions return an answer in the callback `on_ai_answer`. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
function on_alarm()
|
||||||
|
ai:complete("Who are you?")
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_ai_answer(answer)
|
||||||
|
ui:show_text(answer)
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
_Keep in mind that the launcher imposes certain limitations on the use of this module. If you use it too frequently, you will receive an `error: rate limit` instead of a response. Additionally, an error will be returned if the request includes too much text._
|
||||||
|
|
||||||
|
## Reading notifications
|
||||||
|
|
||||||
_Available only in widget scripts._
|
_Available only in widget scripts._
|
||||||
_Avaialble from: 4.1.3_
|
|
||||||
|
|
||||||
* `notify:request_current()` - requests current notifications from the launcher;
|
_Avaialble from: 4.1.3._
|
||||||
|
|
||||||
|
_This module is intended for reading notifications from other applications. To send notifications, use the `system` module._
|
||||||
|
|
||||||
|
* `notify:list()` - returns list of current notifications as table of tables;
|
||||||
* `notify:open(key)` - opens notification with specified key;
|
* `notify:open(key)` - opens notification with specified key;
|
||||||
* `notify:close(key)` - removes the notification with the specified key;
|
* `notify:close(key)` - removes the notification with the specified key;
|
||||||
* `notify:do_action(key, action_id)` - sends notification action (_available from: 4.1.5_);
|
* `notify:do_action(key, action_id)` - sends notification action (_available from: 4.1.5_).
|
||||||
* `notify:consumed(key)` - mark notification as consumed so built-in Notifications widget will not show it;
|
|
||||||
|
|
||||||
The `notify:request_current()` function asks for all current notifications. The Launcher returns them one by one to the `on_notify_posted(table)` callback, where table is the table representing the notification. The same callback will be called when a new notification appears. When the notification is closed, the `on_notify_removed(table)` colbeck will be called.
|
The launcher triggers callback when a notification is received or removed:
|
||||||
|
|
||||||
|
* `on_notifications_updated()` – notification list changed;
|
||||||
|
|
||||||
Notification table format:
|
Notification table format:
|
||||||
|
|
||||||
* `key` - a key uniquely identifying the notification;
|
```
|
||||||
* `time` - time of notification publication in seconds;
|
`key` - a key uniquely identifying the notification;
|
||||||
* `package` - name of the application package that sent the notification;
|
`time` - time of notification publication in seconds;
|
||||||
* `number` - the number on the notification badge, if any;
|
`package` - name of the application package that sent the notification;
|
||||||
* `importance` - notification importance level: from 1 (low) to 4 (high), 3 - standard;
|
`number` - the number on the notification badge, if any;
|
||||||
* `category` - notification category, for example `email`;
|
`importance` - notification importance level: from 1 (low) to 4 (high), 3 - standard;
|
||||||
* `title` - notification title;
|
`category` - notification category, for example `email`;
|
||||||
* `text` - notification text;
|
`title` - notification title;
|
||||||
* `sub_text` - additional notification text;
|
`text` - notification text;
|
||||||
* `big_text` - extended notification text;
|
`sub_text` - additional notification text;
|
||||||
* `is_clearable` - true, if the notification is clearable;
|
`big_text` - extended notification text;
|
||||||
* `group_id` - notification group ID;
|
`is_clearable` - true, if the notification is clearable;
|
||||||
* `messages` - table of tables with fields: `sender`, `text`, `time` (_available from: 4.1.5_);
|
`group_id` - notification group ID;
|
||||||
* `actions` - table notifications actions with fields: `id`, `title`, `have_input` (_available from: 4.1.5_);
|
`messages` - table of tables with fields: `sender`, `text`, `time` (_available from: 4.1.5_);
|
||||||
|
`actions` - table notifications actions with fields: `id`, `title`, `have_input` (_available from: 4.1.5_);
|
||||||
|
```
|
||||||
|
|
||||||
Keep in mind that the AIO Launcher also calls `request_current()` every time you return to the launcher, which means that all scripts will also get notification information in the `on_notify_posted()` callback every time you return to the desktop.
|
Keep in mind that the AIO Launcher also request current notifications every time you return to the launcher, which means that all scripts will also get the `on_notifications_updated()` callback called.
|
||||||
|
|
||||||
## Files
|
## Files
|
||||||
|
|
||||||
@@ -412,21 +733,63 @@ _Avaialble from: 4.1.3_
|
|||||||
|
|
||||||
All files are created in the subdirectory `/sdcard/Android/data/ru.execbit.aiolauncher/files/scripts` without ability to create subdirectories.
|
All files are created in the subdirectory `/sdcard/Android/data/ru.execbit.aiolauncher/files/scripts` without ability to create subdirectories.
|
||||||
|
|
||||||
|
## Rich UI
|
||||||
|
|
||||||
|
Starting with version 5.2.1, AIO Launcher includes an API that allows for displaying a more complex interface than what the high-level functions of the `ui` module allowed. For example, you can display text of any size, center it, move it up and down, display buttons on the left and right sides of the screen, draw icons of different sizes, and much more. Essentially, you can replicate the appearance of any built-in AIO widget.
|
||||||
|
|
||||||
|
[Detailed instructions](README_RICH_UI.md)
|
||||||
|
|
||||||
|
## App widgets
|
||||||
|
|
||||||
|
Starting from version 5.2.0, AIO Launcher supports interaction with app widgets through scripts. This means that you can create a wrapper for any app's widget that will fully match the appearance and style of AIO. You can also use this API if you need to retrieve information from other applications, such as the balance on your mobile phone account or your car's parking spot. If the application has a widget providing such information, you will be able to access it.
|
||||||
|
|
||||||
|
[Detailed instructions](README_APP_WIDGETS.md)
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
|
|
||||||
* `settings:get()` - returns the settings table in an array of words format;
|
_Deprecated in 4.7.4. Use Preferences module._
|
||||||
* `settings:set(table)` - saves the settings table in an array of words format;
|
|
||||||
* `settings:get_kv()` - returns the settings table in `key=value` format;
|
|
||||||
* `settings:set_kv(table)` - saves settings table in the format `key=value`;
|
|
||||||
* `settings:show_dialog()` - show settings change dialog.
|
|
||||||
|
|
||||||
User can change settings through the dialog, which is available by clicking on the "gear" in the edit menu of the widget. If in the widget metadata there is a field `arguments_help`, its value will be shown in the edit dialog. If there is a field `arguments_default` - it will be used to get default arguments.
|
* ~~`settings:get()` - returns the settings table in an array of words format;~~
|
||||||
|
* ~~`settings:set(table)` - saves the settings table in an array of words format;~~
|
||||||
|
* ~~`settings:get_kv()` - returns the settings table in `key=value` format;~~
|
||||||
|
* ~~`settings:set_kv(table)` - saves settings table in the format `key=value`;~~
|
||||||
|
* ~~`settings:show_dialog()` - show settings change dialog.~~
|
||||||
|
|
||||||
The standard edit dialog can be replaced by your own if you implement the `on_settings()` function.
|
~~User can change settings through the dialog, which is available by clicking on the "gear" in the edit menu of the widget. If in the widget metadata there is a field `arguments_help`, its value will be shown in the edit dialog. If there is a field `arguments_default` - it will be used to get default arguments.~~
|
||||||
|
|
||||||
|
~~The standard edit dialog can be replaced by your own if you implement the `on_settings()` function.~~
|
||||||
|
|
||||||
|
## Preferences
|
||||||
|
|
||||||
|
The `prefs` module is designed to permanently store the script settings. It is a simple Lua table which saves to disk all the data written to it.
|
||||||
|
|
||||||
|
You can use it just like any other table with the exception that it cannot be used as a raw array.
|
||||||
|
|
||||||
|
Sample:
|
||||||
|
|
||||||
|
```
|
||||||
|
prefs = require "prefs"
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
prefs.new_key = "Hello"
|
||||||
|
ui:show_lines{prefs.new_key}
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
The `new_key` will be present in the table even after the AIO Launcher has been restarted.
|
||||||
|
|
||||||
|
The `show_dialog()` method automatically creates a window of current settings from fields defined in prefs. The window will display all fields with a text key and a value of one of three types: string, number, or boolean. All other fields of different types will be omitted. Fields whose names start with an underscore will also be omitted. Script will be reloaded on settings save.
|
||||||
|
|
||||||
|
Starting from version 5.5.2, you can change the order of fields in the dialog by simply specifying the order in `prefs._dialog_order`. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
prefs._dialog_order = "message,start_time,end_time"
|
||||||
|
```
|
||||||
|
|
||||||
## Animation and real time updates
|
## Animation and real time updates
|
||||||
|
|
||||||
_Available only in widget scripts._
|
_Available only in widget scripts._
|
||||||
|
|
||||||
_Avaialble from: 4.5.2_
|
_Avaialble from: 4.5.2_
|
||||||
|
|
||||||
The scripts API is designed in a way that every function that changes a widget's UI updates the entire interface. This approach makes the API as simple and convenient as possible for quick scripting, but it also prevents the creation of more complex scripts that change the UI state very often.
|
The scripts API is designed in a way that every function that changes a widget's UI updates the entire interface. This approach makes the API as simple and convenient as possible for quick scripting, but it also prevents the creation of more complex scripts that change the UI state very often.
|
||||||
@@ -439,7 +802,7 @@ There are two modules to solve this problem: `morph` and `anim`. The first is us
|
|||||||
* `morph:run_with_delay(delay, function)` - populates specified Lua function with delay `delay`;
|
* `morph:run_with_delay(delay, function)` - populates specified Lua function with delay `delay`;
|
||||||
* `morph:cancel(idx)` - cancels a previously run change if it is not yet complete (e.g., delay or animation is not over).
|
* `morph:cancel(idx)` - cancels a previously run change if it is not yet complete (e.g., delay or animation is not over).
|
||||||
* `anim:blink(idx)` - blinks UI element with index `idx`;
|
* `anim:blink(idx)` - blinks UI element with index `idx`;
|
||||||
* `anim:move(x, y, [delay])` - moves element sideways by specified number of DP and returns back after delay;
|
* `anim:move(idx, x, y, [delay])` - moves element sideways by specified number of DP and returns back after delay;
|
||||||
* `anim:heartbeat(idx)` - animation of heartbeat;
|
* `anim:heartbeat(idx)` - animation of heartbeat;
|
||||||
* `anim:shake(idx)` - shake animation;
|
* `anim:shake(idx)` - shake animation;
|
||||||
* `anim:cancel(idx)` - cancel the running animation.
|
* `anim:cancel(idx)` - cancel the running animation.
|
||||||
@@ -459,7 +822,7 @@ _Avaialble from: 4.4.4_
|
|||||||
|
|
||||||
* `tasker:tasks([project])` - returns a list of all the tasks in the Tasker, the second optional argument is the project for which you want to get the tasks (returns nil if Tasker is not installed or enabled);
|
* `tasker:tasks([project])` - returns a list of all the tasks in the Tasker, the second optional argument is the project for which you want to get the tasks (returns nil if Tasker is not installed or enabled);
|
||||||
* `tasker:projects()` - returns all Tasker projects (returns nil if Tasker is not installed or enabled);
|
* `tasker:projects()` - returns all Tasker projects (returns nil if Tasker is not installed or enabled);
|
||||||
* `tasker:run_task(name, [args])` - executes the task in the Tasker, the second optional argument is a table of variables passed to the task in the format `{ "name" = "value" }`;
|
* `tasker:run_task(name, [args])` - executes the task in the Tasker, the second optional argument is a table of variables passed to the task in the format `{ name = "value" }`;
|
||||||
* `tasker:run_own_task(commands)` - constructs and performs the task on the fly;
|
* `tasker:run_own_task(commands)` - constructs and performs the task on the fly;
|
||||||
* `tasker:send_command(command)` - sends [tasker command](https://tasker.joaoapps.com/commandsystem.html).
|
* `tasker:send_command(command)` - sends [tasker command](https://tasker.joaoapps.com/commandsystem.html).
|
||||||
|
|
||||||
@@ -497,11 +860,17 @@ The standard Lua API is extended with the following features:
|
|||||||
|
|
||||||
* `string:split(delimeter)` - splits the string using the specified delimiter and returns a table;
|
* `string:split(delimeter)` - splits the string using the specified delimiter and returns a table;
|
||||||
* `string:replace(regexp, string)` - replaces the text found by the regular expression with another text;
|
* `string:replace(regexp, string)` - replaces the text found by the regular expression with another text;
|
||||||
|
* `string:trim()` - removes leading and trailing spaces from the string;
|
||||||
|
* `string:starts_with(substring)` - returns true if the string starts with the specified substring;
|
||||||
|
* `string:ends_with(substring)` - returns true if the string ends with the specified substring;
|
||||||
* `slice(table, start, end)` - returns the part of the table starting with the `start` index and ending with `end` index;
|
* `slice(table, start, end)` - returns the part of the table starting with the `start` index and ending with `end` index;
|
||||||
* `get_index(table, value)` - returns the index of the table element;
|
* `index(table, value)` - returns the index of the table element;
|
||||||
* `get_key(table, value)` - returns the key of the table element;
|
* `key(table, value)` - returns the key of the table element;
|
||||||
* `concat_tables(table1, table2)` - adds elements from array table `table2` to `table1`;
|
* `concat(table1, table2)` - adds elements from array table `table2` to `table1`;
|
||||||
|
* `reverse(table)` - returns a table in which the elements follow in reverse order;
|
||||||
|
* `serialize(table)` - serializes the table into executable Lua code;
|
||||||
* `round(x, n)` - rounds the number;
|
* `round(x, n)` - rounds the number;
|
||||||
|
* `map(function, table)`, `filter(function, table)`, `head(table)`, `tail(table)`, `reduce(function, table)` - several convenience functional utilities form Haskell, Python etc.
|
||||||
|
|
||||||
AIO Launcher also includes:
|
AIO Launcher also includes:
|
||||||
|
|
||||||
@@ -523,12 +892,12 @@ In order for AIO Launcher to correctly display information about the script in t
|
|||||||
|
|
||||||
```
|
```
|
||||||
-- name = "Covid info"
|
-- name = "Covid info"
|
||||||
|
-- icon = "fontawesome_icon_name"
|
||||||
-- description = "Cases of illness and death from covid"
|
-- description = "Cases of illness and death from covid"
|
||||||
-- data_source = "https://covid19api.com"
|
-- data_source = "https://covid19api.com"
|
||||||
-- arguments_help = "Specify the country code"
|
|
||||||
-- arguments_default = "RU"
|
|
||||||
-- type = "widget"
|
-- type = "widget"
|
||||||
-- foldable = "false"
|
-- foldable = "true"
|
||||||
|
-- private_mode = "false"
|
||||||
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
||||||
-- version = "1.0"
|
-- version = "1.0"
|
||||||
```
|
```
|
||||||
@@ -561,7 +930,8 @@ Some tips on writing and debugging scripts:
|
|||||||
* The most convenient way to upload scripts to your smartphone is to use the `install-scripts.sh` script from this repository. This is a sh script for UNIX systems which loads all the scripts from the repository onto the (virtual) memory card of the smartphone using ADB. You can edit it to your liking.
|
* The most convenient way to upload scripts to your smartphone is to use the `install-scripts.sh` script from this repository. This is a sh script for UNIX systems which loads all the scripts from the repository onto the (virtual) memory card of the smartphone using ADB. You can edit it to your liking.
|
||||||
* The easiest way to reload an updated widget script is to swipe the widget to the right and then press the "reload" button. The search scripts will be reloaded automatically next time you open the search window.
|
* The easiest way to reload an updated widget script is to swipe the widget to the right and then press the "reload" button. The search scripts will be reloaded automatically next time you open the search window.
|
||||||
* Since version 4.3.0 AIO Launcher supports widget scripts hot reloading. To enable it, go to AIO Settings -> About and click on the version number 7 times. Then open AIO Settings -> Testing and enable the option "Hot reload scripts on resume". Now when you change the script, it will be automatically reloaded when you return to the desktop.
|
* Since version 4.3.0 AIO Launcher supports widget scripts hot reloading. To enable it, go to AIO Settings -> About and click on the version number 7 times. Then open AIO Settings -> Testing and enable the option "Hot reload scripts on resume". Now when you change the script, it will be automatically reloaded when you return to the desktop.
|
||||||
* Since version 4.4.2 AIO Launcher includes `debug` module with methods: `debug:log(text)`, `debug:toast(text)` and `debug:dialog(text)`.
|
* Since version 4.4.2 AIO Launcher includes `debug` module with methods: `debug:log(text)`, `debug:toast(text)` and `debug:dialog(text)`;
|
||||||
|
* Since version 4.8.0 you can use `--testing = "true"` meta tag. In this case, launcher will gray out the script and place it at the end of the list in the side menu.
|
||||||
|
|
||||||
# Contribution
|
# Contribution
|
||||||
|
|
||||||
|
|||||||
132
README_APP_WIDGETS.md
Normal file
132
README_APP_WIDGETS.md
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
Starting from version 5.2.0, AIO Launcher supports interaction with app widgets through scripts. This means that you can create a wrapper for any app's widget that will fully match the appearance and style of AIO. You can also use this API if you need to retrieve information from other applications, such as the balance on your mobile phone account or your car's parking spot. If the application has a widget providing such information, you will be able to access it.
|
||||||
|
|
||||||
|
### Introduction
|
||||||
|
|
||||||
|
Before you start working with app widgets, you need to find out the app widget provider's name and the widget interface structure. Both can be done using the [android-widget-dumper.lua](dev/android-widget-dumper.lua) script. Enter the package name of the desired application in its global variable `app_pkg` and run the script. It will display all the widgets available for that application. Click on the widget name, and you will see its provider name (first line) and a tree-like structure of its UI. Click on the text to copy it.
|
||||||
|
|
||||||
|
_Note: Some widgets can dynamically change their UI depending on their size. If you encounter such a widget, update the value of the `widget_size` variable by specifying a string from `1x1` to `4x4`. In other cases, leave the value as `nil`._
|
||||||
|
|
||||||
|
Take, for example, The Weather Channel app (package name: `com.weather.Weather`). Install this app and add its package name to the dumper script. After running the dumper, it will show you a list of widgets. Click on "Widget 2x2". A widget configuration screen will open. Click Done at the top of the configurator screen, and you will see the data of this widget. The first line will be the provider's name:
|
||||||
|
|
||||||
|
```
|
||||||
|
com.weather.Weather/com.weather.Weather.widgets.WeatherWidgetProvider2x2
|
||||||
|
```
|
||||||
|
|
||||||
|
The second block of information is the widget interface structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
relative_layout_1:
|
||||||
|
image_1: #13356F
|
||||||
|
relative_layout_2:
|
||||||
|
image_2: 270x270
|
||||||
|
text_1: 40°
|
||||||
|
text_2: Paris, France
|
||||||
|
text_3: Weather data not available
|
||||||
|
```
|
||||||
|
|
||||||
|
You can see that the widget contains two images and three text fields: `text_1`, `text_2`, `text_3`. The first two contain the information we need: temperature and location.
|
||||||
|
|
||||||
|
Now we have everything to write our own widget that will use information from The Weather Channel widget and display it in a format convenient for us!
|
||||||
|
|
||||||
|
### Writing a Widget Wrapper
|
||||||
|
|
||||||
|
Let's start by writing a widget initialization function:
|
||||||
|
|
||||||
|
```
|
||||||
|
function setup_app_widget()
|
||||||
|
local id = widgets:setup("com.weather.Weather/com.weather.Weather.widgets.WeatherWidgetProvider2x2")
|
||||||
|
if (id ~= nil) then
|
||||||
|
prefs.wid = id
|
||||||
|
else
|
||||||
|
ui:show_text("Can't add widget")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Using the `widgets:setup()` method, we ask the launcher to prepare a widget for us. The method returns an identifier needed to work with the widget, or `nil` if an error occurred. Note that this function may trigger the widget's configuration window if it has one.
|
||||||
|
|
||||||
|
Having obtained the identifier, we can use it to communicate with the widget. For this purpose, we call the `widgets:request_updates()` method (you can specify the widget size as a string from 1x1 to 4x4 as the second argument):
|
||||||
|
|
||||||
|
```
|
||||||
|
widgets:request_updates(prefs.wid)
|
||||||
|
```
|
||||||
|
|
||||||
|
This method prepares the widget and then calls the `on_widget_updated(bridge)` callback every time the widget updates. The `bridge` object here is the main "window" or "bridge" for interacting with the widget. It's with its help that we will extract information from the widget.
|
||||||
|
|
||||||
|
The most straightforward way to do this is to use the `bridge:dump_table()` method, which returns the same UI element hierarchy as the dumper script but in a Lua table format. Here's how we can use it to extract the strings we need and display them on the screen:
|
||||||
|
|
||||||
|
```
|
||||||
|
function on_app_widget_updated(bridge)
|
||||||
|
local tab = bridge:dump_table()
|
||||||
|
temp = tab.relative_layout_1.relative_layout_2.text_1
|
||||||
|
location = tab.relative_layout_1.relative_layout_2.text_2
|
||||||
|
|
||||||
|
if location ~= nil and temp ~= nil then
|
||||||
|
ui:show_text(location..": "..temp)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the example [android-widget-sample2.lua](samples/android-widget-sample2.lua) to see how it looks on the screen.
|
||||||
|
|
||||||
|
Another way to extract the same information is to use the `bridge:dump_strings()` method:
|
||||||
|
|
||||||
|
```
|
||||||
|
function on_app_widget_updated(bridge)
|
||||||
|
strings = bridge:dump_strings().values
|
||||||
|
temp = strings[1]
|
||||||
|
location = strings[2]
|
||||||
|
|
||||||
|
if location ~= nil and temp ~= nil then
|
||||||
|
ui:show_text(location..": "..temp)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
It returns a table of tables, where the `keys` table is an array of UI text elements, and `values` contains the text strings within them. You can use whichever method suits you better.
|
||||||
|
|
||||||
|
In both cases, the strings can include HTML tags (`<b>`, `<i>`, `<s>`, `<u>`) if the corresponding styles were applied to the text in the widget.
|
||||||
|
|
||||||
|
### Handling Clicks
|
||||||
|
|
||||||
|
Extracting information from an app widget is only half the job. Most widgets are interactive, meaning you can click on them to get some response, like adding a new note or opening a currency exchange rate window.
|
||||||
|
|
||||||
|
Implementing clicks on widget elements from the script is very simple: just use the `bridge:click()` method, passing it the name of the UI element or the string it contains as an argument:
|
||||||
|
|
||||||
|
```
|
||||||
|
bridge:click(temp)
|
||||||
|
```
|
||||||
|
|
||||||
|
Open the example [android-widget-sample2.lua](samples/android-widget-sample2.lua) to see how this can be used in the script.
|
||||||
|
|
||||||
|
### Updating and releasing the Widget
|
||||||
|
|
||||||
|
The AIO Launcher widget API is designed to keep the widget in memory all the time while the script is on the screen. This means that after calling `widgets:request_updates()`, the launcher will call the `on_widget_updated()` callback after _every_ widget interface update as long as the script is on the launcher's screen, and the launcher is on the phone's screen.
|
||||||
|
|
||||||
|
In most cases, such a mechanism is preferable. However, there are situations when `on_widget_updated()` should only be called when the script asks for it, and the widget itself should not constantly hang in memory. For this, there's a special `bridge:free()` method.
|
||||||
|
|
||||||
|
For example, you have a widget whose information you want to receive every morning, not with every update. In this case, you do everything the same as in the example above, but at the end of the `on_app_widget_updated()` function, you call `bridge:free()`. After that, the launcher will release the widget and will no longer call the `on_widget_updated()` method (while the widget itself will not respond to clicks). Then, it's enough to call `widgets:request_updates()` every morning to update the script's content.
|
||||||
|
|
||||||
|
### Other Functions
|
||||||
|
|
||||||
|
The information provided above is sufficient to handle almost any application widget. However, to write a truly efficient script, you'll need a few more auxiliary functions:
|
||||||
|
|
||||||
|
* `widgets:bound(id)` - returns true if the specified widget identifier was previously initialized using `widgets:setup()`. You should _always_ call this function at the beginning of the script and call `widgets:setup()` only if it returns `false`.
|
||||||
|
|
||||||
|
* `bridge:color(element_name)` - returns the color of an element in the format `#000000`. With this method, you can find out the color of the text, the background color of the layout (if set), or even extract the primary color from an image.
|
||||||
|
|
||||||
|
Other functions:
|
||||||
|
|
||||||
|
```
|
||||||
|
* bridge:label() - returns the name of the widget;
|
||||||
|
* bridge:provider() - returns the name of the widget provider;
|
||||||
|
* bridge:dump_tree() - returns the UI tree in text format;
|
||||||
|
* bridge:dump_json() - returns the UI tree in Json format;
|
||||||
|
* bridge:dump_colors() - returns a table of color tables, where `keys` are element names, `values` are colors;
|
||||||
|
* bridge:dump_elements(element_name) - returns a table of tables of specified elements, where `keys` are element names, `values` are their values.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
In the [samples](samples/) directory, you will find several widget examples. Also, pay attention to the Todoist and Chrome widgets in the [community](community/) directory.
|
||||||
|
|
||||||
201
README_INTRO.md
Normal file
201
README_INTRO.md
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
# Creating Your Own Scripts for AIO Launcher: A Step-by-Step Guide for Beginners
|
||||||
|
|
||||||
|
**AIO Launcher** isn’t just an alternative to the standard Android home screen — it transforms your device into a powerful tool for personalization through Lua scripting. In this article, we’ll show you how to write your first scripts, presenting three examples ranging from simple text output to an interactive widget and integration with an open API. We’ll also explain how to add your scripts to the launcher via AIO Store.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How to Create and Load Scripts
|
||||||
|
|
||||||
|
You can write scripts for AIO Launcher in any text editor—whether it’s Sublime Text, Visual Studio Code, or even Notepad. The key is to save your file with the **.lua** extension. Then, using the **AIO Store** app, you can easily add your file to the launcher. In AIO Store, at the end of the list of installed scripts there’s a special "Add Script" button that lets you add your own script.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 1. Hello, World!
|
||||||
|
|
||||||
|
The simplest script is the classic "Hello, World!" In this example, when the widget is launched, a welcome message appears on the screen.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- name = "Hello, World!"
|
||||||
|
-- type = "widget"
|
||||||
|
|
||||||
|
function on_load()
|
||||||
|
-- On the first load of the widget, display the text "Hello, World!".
|
||||||
|
ui:show_text("Hello, World!")
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
*Explanation:*
|
||||||
|
|
||||||
|
- The metadata at the beginning of the file sets the script’s name and type.
|
||||||
|
- The `on_load()` function is called when the script starts.
|
||||||
|
- The `ui:show_text()` method displays the text on the widget.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 2. Interactive Counter
|
||||||
|
|
||||||
|
In the second example, we create an interactive counter using two buttons. Instead of displaying the count separately, the current counter value is embedded directly in the label of the "Increase" button. When you press "Increase", the counter increments and the button label updates; pressing "Reset" sets the counter back to zero.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- name = "Interactive Counter"
|
||||||
|
-- type = "widget"
|
||||||
|
|
||||||
|
-- Global variable to store the current count.
|
||||||
|
local counter = 0
|
||||||
|
|
||||||
|
-- Function to update the display: shows two buttons with the counter embedded in the first button.
|
||||||
|
function update_display()
|
||||||
|
ui:show_buttons({ "Increase (" .. counter .. ")", "Reset" })
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_load()
|
||||||
|
update_display()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Function to handle button clicks.
|
||||||
|
function on_click(index)
|
||||||
|
if index == 1 then
|
||||||
|
counter = counter + 1
|
||||||
|
elseif index == 2 then
|
||||||
|
counter = 0
|
||||||
|
end
|
||||||
|
update_display()
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
*Explanation:*
|
||||||
|
|
||||||
|
- When the script launches, the `on_load()` function calls `update_display()`, which displays two buttons.
|
||||||
|
- The first button’s label includes the current counter value (e.g., "Increase (0)").
|
||||||
|
- The `on_click(index)` function handles button presses: if the first button is pressed, the counter increases; if the second button is pressed, it resets to zero.
|
||||||
|
- After each action, `update_display()` updates the button labels accordingly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 3. Cat Facts
|
||||||
|
|
||||||
|
In the third example, we use the Cat Facts API to retrieve a random cat fact. This version uses the `on_alarm` callback instead of `on_load` so that the widget automatically updates every 30 minutes.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- name = "Cat Facts"
|
||||||
|
-- type = "widget"
|
||||||
|
|
||||||
|
-- on_alarm is called automatically, up to once every 30 minutes, to refresh the widget.
|
||||||
|
function on_alarm()
|
||||||
|
ui:show_text("Loading a random cat fact...")
|
||||||
|
http:get("https://catfact.ninja/fact")
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_network_result(body, code)
|
||||||
|
if code == 200 then
|
||||||
|
local json = require "json"
|
||||||
|
local data = json.decode(body) -- The json module converts the JSON string into a Lua table.
|
||||||
|
if data and data.fact then
|
||||||
|
ui:show_text("Random Cat Fact:\n" .. data.fact)
|
||||||
|
else
|
||||||
|
ui:show_text("Failed to retrieve a cat fact.")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
ui:show_text("Error loading data. Code: " .. code)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
*Explanation:*
|
||||||
|
|
||||||
|
- In this example, the `on_alarm()` function is used to trigger an automatic update every 30 minutes, ensuring that your widget stays fresh with a new cat fact.
|
||||||
|
- The `http:get()` call fetches data from the Cat Fact API, and the `json` module decodes the returned JSON string into a Lua table for easy access.
|
||||||
|
|
||||||
|
Remember: there’s also an `on_resume` callback that is invoked every time you return to the home screen, which you can use for additional actions if desired.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 4. Search Engine Selector for the Search Window
|
||||||
|
|
||||||
|
In this example, we demonstrate how to create a search script for the AIO Launcher search window. Unlike widget scripts, search scripts are activated when the search window is opened and as the user types their query. This script presents two search suggestions — one for searching on Google and another for Bing. When a suggestion is clicked, the corresponding search engine opens in the browser.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- name = "Search Engine Selector"
|
||||||
|
-- type = "search"
|
||||||
|
|
||||||
|
local lastQuery = ""
|
||||||
|
|
||||||
|
function on_search(query)
|
||||||
|
lastQuery = query
|
||||||
|
local results = {}
|
||||||
|
if #query > 0 then
|
||||||
|
table.insert(results, "Search Google for: " .. query)
|
||||||
|
table.insert(results, "Search Bing for: " .. query)
|
||||||
|
end
|
||||||
|
search:show_lines(results)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(index)
|
||||||
|
if index == 1 then
|
||||||
|
system:open_browser("https://www.google.com/search?q=" .. lastQuery)
|
||||||
|
return true
|
||||||
|
elseif index == 2 then
|
||||||
|
system:open_browser("https://www.bing.com/search?q=" .. lastQuery)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
*Explanation:*
|
||||||
|
|
||||||
|
- This search script is identified as a search script by the meta tag `-- type = "search"`.
|
||||||
|
- The global variable `lastQuery` stores the most recent search query.
|
||||||
|
- The `on_load()` function is called each time the search window is opened, displaying a default message.
|
||||||
|
- The `on_search(query)` function is invoked whenever the user types something; it saves the query and builds a list of suggestions, which are displayed via `search:show_lines()`.
|
||||||
|
- The `on_click(index)` function handles the user's selection: if the first result is clicked, the system opens Google with the query; if the second is clicked, Bing is used.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example 5. Side Menu Script
|
||||||
|
|
||||||
|
This example demonstrates how to create a side menu (drawer) script for AIO Launcher. Side menu scripts allow you to add custom items to the launcher's side menu. The script type is defined by the meta tag `-- type = "drawer"`, and the primary callback function is `on_drawer_open()`, which is invoked when the side menu is opened. When the user selects an item, the `on_click(index)` function is called to execute the corresponding action.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- name = "Custom Drawer Menu"
|
||||||
|
-- type = "drawer"
|
||||||
|
|
||||||
|
function on_drawer_open()
|
||||||
|
local menuItems = {"Open Website", "Update list", "Launch App"}
|
||||||
|
drawer:show_list(menuItems)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(index)
|
||||||
|
if index == 1 then
|
||||||
|
system:open_browser("https://www.example.com")
|
||||||
|
elseif index == 2 then
|
||||||
|
local menuItems = {"Open Website", "List updated", "Launch App"}
|
||||||
|
drawer:show_list(menuItems)
|
||||||
|
elseif index == 3 then
|
||||||
|
apps:launch("com.android.contacts")
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
*Explanation:*
|
||||||
|
|
||||||
|
- The meta tags at the top specify the script’s name and indicate that it is a drawer (side menu) script.
|
||||||
|
- The `on_drawer_open()` function is automatically called when the side menu is opened; it builds a list of menu items (in this case, three items) and displays them using `drawer:show_list()`.
|
||||||
|
- The `on_click(index)` function handles user selection: clicking the first item opens a website, the second updates items list, and the third launches an application.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
Creating scripts for AIO Launcher opens up endless possibilities for customizing and extending the functionality of your Android device. From simple text output to interactive widgets and integration with external services, all of this is accessible thanks to the powerful API of AIO Launcher and the flexibility of Lua. Use any text editor you prefer to write your scripts, then easily add them to the launcher via AIO Store using the dedicated "Add Script" button at the end of your installed scripts list. Experiment, integrate new data sources, and create unique solutions that make your home screen truly your own!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Sources for Further Study
|
||||||
|
|
||||||
|
- citeturn0search0 [Official AIO Launcher Scripts Repository](https://github.com/zobnin/aiolauncher_scripts)
|
||||||
|
- [AIO Launcher on Google Play](https://play.google.com/store/apps/details?id=ru.execbit.aiolauncher&hl=en)
|
||||||
|
- [Lua Documentation](https://www.lua.org/pil/)
|
||||||
|
- [Cat Fact API](https://catfact.ninja/fact)
|
||||||
|
- [AIO Store](https://play.google.com/store/apps/details?id=ru.execbit.aiolauncher.store)
|
||||||
|
|
||||||
|
These resources will help you dive deeper into creating scripts for AIO Launcher and explore new ways to extend your Android device’s functionality.
|
||||||
94
README_RICH_UI.md
Normal file
94
README_RICH_UI.md
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
Starting with version 5.2.1, AIO Launcher includes an API that allows for displaying a more complex interface than what the high-level functions of the `ui` module allowed. For example, you can display text of any size, center it, move it up and down, display buttons on the left and right sides of the screen, draw icons of different sizes, and much more. Essentially, you can replicate the appearance of any built-in AIO widget.
|
||||||
|
|
||||||
|
Open the example [rich-gui-basic-sample.lua](samples/rich-gui-basic-sample.lua) and study it. As you can see, the new API consists of just one function `gui`, which takes a table describing the UI as input and returns an object that has a `render()` method for drawing this UI.
|
||||||
|
|
||||||
|
The UI is built line by line, using commands that add elements from left to right, with the possibility of moving to a new line. The provided example displays two lines, under which are two buttons:
|
||||||
|
|
||||||
|
```
|
||||||
|
{"text", "First line"},
|
||||||
|
{"new_line", 1},
|
||||||
|
{"text", "Second line"},
|
||||||
|
{"new_line", 2},
|
||||||
|
{"button", "Button #1"},
|
||||||
|
{"spacer", 2},
|
||||||
|
{"button", "Button #2"},
|
||||||
|
```
|
||||||
|
|
||||||
|
The first command displays the line "First line", the "new_line" command moves to a new line, adding a space equal to one unit (each unit is 4 pixels). Then, the "text" command adds a new line, followed by a move to a new line and the display of two buttons. Since there is no "new_line" command between the button display commands, they will be displayed in one line, one after the other, from left to right with a gap of 2 units. The "spacer" command is responsible for the horizontal gap.
|
||||||
|
|
||||||
|
This example is already useful, but it's too plain. Let's add some colors and vary the text lines a bit: make the first line larger, and write the second line in italic font. Also, let's work on the color of the buttons:
|
||||||
|
|
||||||
|
```
|
||||||
|
{"text", "First line", {size = 21}},
|
||||||
|
{"new_line", 1},
|
||||||
|
{"text", "<i>Second line</i>"},
|
||||||
|
{"new_line", 2},
|
||||||
|
{"button", "Button #1", {color = "#ff0000"}},
|
||||||
|
{"spacer", 2},
|
||||||
|
{"button", "Button #2", {color = "#0000ff"}},
|
||||||
|
```
|
||||||
|
|
||||||
|
To change the size of the text, we used the table `{size = 21}`. This is the standard approach if a command has more than one parameter, then all other arguments are passed in the table. To make the text italic, we used the HTML tag `<i>`. The "text" command supports many tags for text transformation. The color of the buttons is also changed using a table.
|
||||||
|
|
||||||
|
It's more interesting now, but again, an interface where all elements are grouped on the left side may not always be suitable. We need a way to align elements on the right side of the screen or in the center. Let's make the first line be in the middle of the line (kind of like a title), and the second button - on the right side:
|
||||||
|
|
||||||
|
```
|
||||||
|
{"text", "First line", {size = 21, gravity = "center_h"}},
|
||||||
|
{"new_line", 1},
|
||||||
|
{"text", "<i>Second line</i>"},
|
||||||
|
{"new_line", 2},
|
||||||
|
{"button", "Button #1", {color = "#ff0000"}},
|
||||||
|
{"spacer", 2},
|
||||||
|
{"button", "Button #2", {color = "#0000ff", gravity = "right"}},
|
||||||
|
```
|
||||||
|
|
||||||
|
Here we used the `gravity` parameter to change the location of the element in the line. Three things are important to know about `gravity`:
|
||||||
|
|
||||||
|
1. It changes the position of the element only within the current line;
|
||||||
|
2. Possible `gravity` values: `left`, `top`, `right`, `bottom`, `center_h` (horizontal centering), `center_v` (vertical centering) and `anchor_prev`;
|
||||||
|
3. `Gravity` values can be combined, for example, to display a text element in the top right corner of the current line, you can specify `gravity = "top|right"`;
|
||||||
|
|
||||||
|
Also, there are two limitations to know about:
|
||||||
|
|
||||||
|
1. The `center_h` value is applied to each element separately, meaning if you add two lines with the gravity value `center_h` in one line, they will not both be grouped and displayed in the center, but instead, they will split the screen in half and be displayed each in the center of its half. This situation can be rectified by using the `anchor_prev` as the value for gravity. This flag anchors the current element to the previous element of the current line, so that the `gravity` value of the previous element starts affecting both elements.
|
||||||
|
2. The `right` value affects not only the element to which it is applied but also all subsequent elements in the current line. That means if you add another button after "Button #2", it will also be on the right side after "Button #2".
|
||||||
|
|
||||||
|
Surely you will want to add icons to your UI. There are several ways to do this. The first way is to embed the icon directly into the text, in this case, you can use any icon from the FontAwesome set:
|
||||||
|
|
||||||
|
```
|
||||||
|
{"text", "This is icon: %%fa:microphone%%"}
|
||||||
|
```
|
||||||
|
|
||||||
|
The second way: use the icon command (in this case, you can also specify the size and color of the icon):
|
||||||
|
|
||||||
|
```
|
||||||
|
{"icon", "fa:microphone", {size = 32, color = "#ff0000"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Third way: display icon in the button:
|
||||||
|
|
||||||
|
```
|
||||||
|
{"button", "fa:microphone"}
|
||||||
|
{"button", "Text with icon: %%fa:microphone%%"}
|
||||||
|
```
|
||||||
|
|
||||||
|
The second method also allows displaying icons of applications and contacts. How to do this is shown in the example [rich-gui-sample.lua](samples/rich-gui-sample.lua). You can also use the `icon` element to display your own icons in SVG format. Example: [svg-sample.lua](samples/svg-sample.lua).
|
||||||
|
|
||||||
|
By the way, if you want the button to stretch across the entire width of the screen, you can use the expand argument:
|
||||||
|
|
||||||
|
```
|
||||||
|
{"button", "Full with button", {expand = true}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Handling clicks on elements works the same as when using the `ui` module API. Just define `on_click()` or `on_long_click()` functions. The first parameter of the function will be the index of the element. How to use this mechanism can be learned in the example [samples/rich-ui-sample.lua].
|
||||||
|
|
||||||
|
This is all you need to know about the new API. Below is an example demonstrating all supported elements and all their default parameters:
|
||||||
|
|
||||||
|
```
|
||||||
|
{"text", "", {size = 17, color = "", gravity = "left"}},
|
||||||
|
{"button", "", {color = "", gravity = "left", expand = "false"}},
|
||||||
|
{"icon", "", {size = 17, color = "", gravity = "left"}},
|
||||||
|
{"progress", "", {progress = 0, color = ""}},
|
||||||
|
{"new_line", 0},
|
||||||
|
{"spacer", 0},
|
||||||
|
```
|
||||||
26
community/actions-menu.lua
Normal file
26
community/actions-menu.lua
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
-- name = "Actions menu"
|
||||||
|
-- name_id = "actions"
|
||||||
|
-- description = "Shows aio launcher actions"
|
||||||
|
-- type = "drawer"
|
||||||
|
-- aio_version = "4.7.99"
|
||||||
|
-- author = "Evgeny Zobnin"
|
||||||
|
-- version = "1.0"
|
||||||
|
|
||||||
|
function on_drawer_open()
|
||||||
|
actions = aio:actions()
|
||||||
|
labels = map(actions, function(it) return it.label end)
|
||||||
|
drawer:show_list(labels)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
aio:do_action(actions[idx].name)
|
||||||
|
drawer:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
function map(tbl, f)
|
||||||
|
local ret = {}
|
||||||
|
for k,v in pairs(tbl) do
|
||||||
|
ret[k] = f(v)
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
64
community/alquran-widget.lua
Normal file
64
community/alquran-widget.lua
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
-- name = "AlQuran"
|
||||||
|
-- description = "AlQuran"
|
||||||
|
-- data_source = "https://quran.com/"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Nuhu Sule (ncalyx@gmail.com)"
|
||||||
|
-- version = "2.3"
|
||||||
|
-- foldable = "false"
|
||||||
|
|
||||||
|
local json = require "json"
|
||||||
|
local verse_data = nil -- Store the verse translation data
|
||||||
|
|
||||||
|
function on_alarm()
|
||||||
|
-- Fetch random verse with English translation
|
||||||
|
http:get("https://api.alquran.cloud/v1/ayah/random/en.sahih")
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_network_result(result, code)
|
||||||
|
if code >= 200 and code < 300 then
|
||||||
|
local response = json.decode(result)
|
||||||
|
|
||||||
|
if response and response.data then
|
||||||
|
-- Store verse data including Surah name, verse number, and English translation
|
||||||
|
verse_data = {
|
||||||
|
surah = response.data.surah.englishName,
|
||||||
|
verse_number = response.data.number,
|
||||||
|
translation = response.data.text -- English translation only
|
||||||
|
}
|
||||||
|
|
||||||
|
display_verse()
|
||||||
|
else
|
||||||
|
ui:show_message("Error loading verse data.")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
-- Show error if the HTTP request fails
|
||||||
|
ui:show_message("Error fetching verse. Please try again later.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function display_verse()
|
||||||
|
if verse_data then
|
||||||
|
-- Prepare display lines with English translation
|
||||||
|
local display_lines = {
|
||||||
|
"Verse " .. verse_data.verse_number .. ": " .. verse_data.translation
|
||||||
|
}
|
||||||
|
local display_titles = {
|
||||||
|
"Surah: " .. verse_data.surah
|
||||||
|
}
|
||||||
|
|
||||||
|
ui:show_lines(display_lines, display_titles)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click()
|
||||||
|
if verse_data then
|
||||||
|
-- Prepare text to copy to clipboard with English translation only
|
||||||
|
local clipboard_text = "Verse " .. verse_data.verse_number .. ": " .. verse_data.translation ..
|
||||||
|
" - Surah: " .. verse_data.surah
|
||||||
|
|
||||||
|
system:to_clipboard(clipboard_text)
|
||||||
|
ui:show_message("Verse copied to clipboard!")
|
||||||
|
else
|
||||||
|
ui:show_message("No verse available to copy.")
|
||||||
|
end
|
||||||
|
end
|
||||||
88
community/amdroid-buttons-app-widget.lua
Normal file
88
community/amdroid-buttons-app-widget.lua
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
-- name = "Amdroid Buttons"
|
||||||
|
-- description = "Foldable AIO wrapper for the Amdroid Buttons app widget"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Theodor Galanis (t.me/TheodorGalanis)"
|
||||||
|
-- version = "1.10"
|
||||||
|
-- foldable = "true"
|
||||||
|
-- on_resume_when_folding = "true"
|
||||||
|
-- uses_app = "com.amdroidalarmclock.amdroid"
|
||||||
|
|
||||||
|
local prefs = require "prefs"
|
||||||
|
local indices = {1, 3, 4, 6, 8}
|
||||||
|
local next_alarm = ""
|
||||||
|
local accent="#888888"
|
||||||
|
local temp = {}
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
if not widgets:bound(prefs.wid) then
|
||||||
|
setup_app_widget()
|
||||||
|
end
|
||||||
|
accent = aio:colors().accent
|
||||||
|
widgets:request_updates(prefs.wid)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_app_widget_updated(bridge)
|
||||||
|
local tab = bridge:dump_table()
|
||||||
|
next_alarm = tab.frame_layout_1.v_layout_1.text_1
|
||||||
|
w_bridge = bridge
|
||||||
|
local state = ui:folding_flag()
|
||||||
|
if state == false then
|
||||||
|
temp = {
|
||||||
|
{"icon", "fa:alarm-clock", {gravity="center_h|center_v",color = accent}},
|
||||||
|
{"spacer", 2 },
|
||||||
|
{"text", next_alarm, {gravity="anchor_prev"}},
|
||||||
|
{"button", "fa:backward", {gravity = "right"}},
|
||||||
|
{"spacer", 2},
|
||||||
|
{"button", "fa:right-long-to-line"},
|
||||||
|
{"spacer", 2},
|
||||||
|
{"button", "fa:forward"}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
temp = {
|
||||||
|
{"icon", "fa:alarm-clock", {gravity="center_h|center_v",color = accent}},
|
||||||
|
{"spacer", 2 },
|
||||||
|
{"text", next_alarm, {gravity="anchor_prev"}}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
my_gui = gui(temp)
|
||||||
|
my_gui.render()
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
if idx == indices[1] or idx == indices[2] then
|
||||||
|
w_bridge:click(next_alarm)
|
||||||
|
elseif idx == indices[3] then
|
||||||
|
w_bridge:click("image_3")
|
||||||
|
elseif idx == indices[4] then
|
||||||
|
w_bridge:click("image_1")
|
||||||
|
elseif idx == indices[5] then
|
||||||
|
w_bridge:click("image_2")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_long_click(idx)
|
||||||
|
if idx == indices[1] or idx == indices[2] then
|
||||||
|
ui:show_toast("Open Amdroid app")
|
||||||
|
elseif idx == indices[3] then
|
||||||
|
ui:show_toast("Adjust alarm's next occurance time 10 minutes earlier")
|
||||||
|
elseif idx == indices[4] then
|
||||||
|
ui:show_toast("Skip alarm's next occurance")
|
||||||
|
elseif idx == indices[5] then
|
||||||
|
ui:show_toast("Adjust alarm's next occurance time 10 minutes later")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_settings()
|
||||||
|
ui:show_dialog("Amdroid app Buttons widget", "This script wrapper uses Amdroid app's Buttons widget. Long click each button for function description. No settings are required.")
|
||||||
|
end
|
||||||
|
|
||||||
|
function setup_app_widget()
|
||||||
|
local id = widgets:setup("com.amdroidalarmclock.amdroid/com.amdroidalarmclock.amdroid.AmdroidAppWidgetProvider")
|
||||||
|
|
||||||
|
if (id ~= nil) then
|
||||||
|
prefs.wid = id
|
||||||
|
else
|
||||||
|
ui:show_text("Can't add widget")
|
||||||
|
end
|
||||||
|
end
|
||||||
60
community/amdroid-nextalarm-app-widget.lua
Normal file
60
community/amdroid-nextalarm-app-widget.lua
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
-- name = "Amdroid Next Alarm"
|
||||||
|
-- description = "AIO wrapper for the Amdroid Next alarm app widget"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Theodor Galanis (t.me/TheodorGalanis)"
|
||||||
|
-- version = "1.11"
|
||||||
|
-- foldable = "false"
|
||||||
|
-- uses_app = "com.amdroidalarmclock.amdroid"
|
||||||
|
|
||||||
|
local prefs = require "prefs"
|
||||||
|
|
||||||
|
local next_alarm = ""
|
||||||
|
local accent="#888888"
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
if not widgets:bound(prefs.wid) then
|
||||||
|
setup_app_widget()
|
||||||
|
end
|
||||||
|
accent = aio:colors().accent
|
||||||
|
primary = aio:colors().primary_text
|
||||||
|
widgets:request_updates(prefs.wid)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_app_widget_updated(bridge)
|
||||||
|
local tab = bridge:dump_table()
|
||||||
|
|
||||||
|
next_alarm = tab.v_layout_1.text_1
|
||||||
|
w_bridge = bridge
|
||||||
|
|
||||||
|
if next_alarm ~= nil
|
||||||
|
then
|
||||||
|
my_gui=gui{
|
||||||
|
{"icon", "fa:alarm-clock", {gravity="center_h|center_v",color = accent}},
|
||||||
|
{"spacer", 2},
|
||||||
|
{"text", next_alarm, {gravity="anchor_prev"}}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
my_gui=gui{
|
||||||
|
{"text", "Empty", {gravity="center_h" }}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
my_gui.render()
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
w_bridge:click(next_alarm)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_settings()
|
||||||
|
dialogs:show_dialog("Amdroid app Next Alarm widget", "This script wrapper uses Amdroid app's Next alarm widget to display the next scheduled alarm. No settings are required.")
|
||||||
|
end
|
||||||
|
|
||||||
|
function setup_app_widget()
|
||||||
|
local id = widgets:setup("com.amdroidalarmclock.amdroid/com.amdroidalarmclock.amdroid.widgets.NextAlarmWidgetProvider")
|
||||||
|
|
||||||
|
if (id ~= nil) then
|
||||||
|
prefs.wid = id
|
||||||
|
else
|
||||||
|
ui:show_text("Can't add widget")
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,360 +0,0 @@
|
|||||||
-- name = "My apps"
|
|
||||||
-- description = "Simple apps widget"
|
|
||||||
-- type = "widget"
|
|
||||||
-- version = "1.0"
|
|
||||||
-- author = "Andrey Gavrilov"
|
|
||||||
|
|
||||||
local utf8 = require "utf8"
|
|
||||||
local dialog_id = ""
|
|
||||||
local app_idx = 0
|
|
||||||
|
|
||||||
function on_resume()
|
|
||||||
update_args()
|
|
||||||
local folding = false
|
|
||||||
if settings:get_kv()["folding"] == "true" then
|
|
||||||
folding = true
|
|
||||||
end
|
|
||||||
ui:set_folding_flag(folding)
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_alarm()
|
|
||||||
local args = settings:get_kv()
|
|
||||||
if next(args) == nil then
|
|
||||||
args["no_hidden"] = true
|
|
||||||
args["columns"] = 4
|
|
||||||
args["trim"] = 10
|
|
||||||
args["folding"] = false
|
|
||||||
args["1"] = "com.android.settings"
|
|
||||||
settings:set_kv(args)
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_settings()
|
|
||||||
dialog_id = "settings"
|
|
||||||
local tab = {"Applications list","No hidden setting","Columns number","Trim app name","Autofolding setting","Widget title"}
|
|
||||||
ui:show_radio_dialog("Settings",tab)
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_dialog_action(data)
|
|
||||||
if data == -1 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if dialog_id == "settings" then
|
|
||||||
if data == 1 then
|
|
||||||
dialog_id = "apps"
|
|
||||||
local tab = get_all_apps("abc",settings:get_kv()["no_hidden"])
|
|
||||||
ui:show_checkbox_dialog("Select apps",tab[2],args_to_idx(tab[1]))
|
|
||||||
return
|
|
||||||
elseif data == 2 then
|
|
||||||
dialog_id = "no_hidden"
|
|
||||||
local tab = {"Not show hidden applications"}
|
|
||||||
local tt = {}
|
|
||||||
if tostring(settings:get_kv()["no_hidden"]) == "true" then
|
|
||||||
tt = {1}
|
|
||||||
end
|
|
||||||
ui:show_checkbox_dialog("No hidden settings",tab,tt)
|
|
||||||
return
|
|
||||||
elseif data == 3 then
|
|
||||||
dialog_id = "columns"
|
|
||||||
ui:show_edit_dialog("Columns number","",settings:get_kv()["columns"])
|
|
||||||
return
|
|
||||||
elseif data == 4 then
|
|
||||||
dialog_id = "trim"
|
|
||||||
ui:show_edit_dialog("Trim app name","0 - not trim",settings:get_kv()["trim"])
|
|
||||||
return
|
|
||||||
elseif data == 5 then
|
|
||||||
dialog_id = "folding"
|
|
||||||
local tab = {"Autofolding"}
|
|
||||||
local tt = {}
|
|
||||||
if tostring(settings:get_kv()["folding"]) == "true" then
|
|
||||||
tt = {1}
|
|
||||||
end
|
|
||||||
ui:show_checkbox_dialog("Autofolding settings",tab,tt)
|
|
||||||
return
|
|
||||||
elseif data == 6 then
|
|
||||||
dialog_id = "name"
|
|
||||||
ui:show_edit_dialog("Set widget title","Empty - default title",ui:get_default_title())
|
|
||||||
return
|
|
||||||
end
|
|
||||||
elseif dialog_id == "no_hidden" then
|
|
||||||
local args = settings:get_kv()
|
|
||||||
if next(data) == nil then
|
|
||||||
args["no_hidden"] = false
|
|
||||||
else
|
|
||||||
args["no_hidden"] = true
|
|
||||||
end
|
|
||||||
settings:set_kv(args)
|
|
||||||
update_args()
|
|
||||||
redraw()
|
|
||||||
return
|
|
||||||
elseif dialog_id == "apps" then
|
|
||||||
settings:set_kv(idx_to_args(data))
|
|
||||||
redraw()
|
|
||||||
return
|
|
||||||
elseif dialog_id == "columns" then
|
|
||||||
if data == tostring(tonumber(data)) then
|
|
||||||
if tonumber(data) == 0 then
|
|
||||||
data = "1"
|
|
||||||
end
|
|
||||||
local args = settings:get_kv()
|
|
||||||
args["columns"] = math.floor(tonumber(data))
|
|
||||||
settings:set_kv(args)
|
|
||||||
update_args()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
return
|
|
||||||
elseif dialog_id == "trim" then
|
|
||||||
if data == tostring(tonumber(data)) then
|
|
||||||
local args = settings:get_kv()
|
|
||||||
args["trim"] = math.floor(tonumber(data))
|
|
||||||
settings:set_kv(args)
|
|
||||||
update_args()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
return
|
|
||||||
elseif dialog_id == "folding" then
|
|
||||||
local args = settings:get_kv()
|
|
||||||
if next(data) == nil then
|
|
||||||
args["folding"] = false
|
|
||||||
else
|
|
||||||
args["folding"] = true
|
|
||||||
end
|
|
||||||
settings:set_kv(args)
|
|
||||||
update_args()
|
|
||||||
redraw()
|
|
||||||
return
|
|
||||||
elseif dialog_id == "name" then
|
|
||||||
local title = ui:get_default_title()
|
|
||||||
if data ~= "" then
|
|
||||||
title = data
|
|
||||||
end
|
|
||||||
ui:set_title(title)
|
|
||||||
return
|
|
||||||
elseif dialog_id == "move" then
|
|
||||||
if data == tostring(tonumber(data)) then
|
|
||||||
local idx = math.floor(tonumber(data))
|
|
||||||
local args = settings:get_kv()
|
|
||||||
if idx < 1 then
|
|
||||||
idx = 1
|
|
||||||
elseif idx > max_key(args) then
|
|
||||||
idx = max_key(args)
|
|
||||||
end
|
|
||||||
local from = args[tostring(app_idx)]
|
|
||||||
local to = args[tostring(idx)]
|
|
||||||
args[tostring(app_idx)] = to
|
|
||||||
args[tostring(idx)] = from
|
|
||||||
settings:set_kv(args)
|
|
||||||
update_args()
|
|
||||||
redraw()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_context_menu_click(idx)
|
|
||||||
if idx == 4 then
|
|
||||||
apps:show_edit_dialog(settings:get_kv()[tostring(app_idx)])
|
|
||||||
elseif idx == 1 then
|
|
||||||
local args = settings:get_kv()
|
|
||||||
if app_idx == max_key(args) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local from = args[tostring(app_idx)]
|
|
||||||
local to = args[tostring(app_idx+1)]
|
|
||||||
args[tostring(app_idx)] = to
|
|
||||||
args[tostring(app_idx+1)] = from
|
|
||||||
settings:set_kv(args)
|
|
||||||
update_args()
|
|
||||||
redraw()
|
|
||||||
return
|
|
||||||
elseif idx == 3 then
|
|
||||||
local args = settings:get_kv()
|
|
||||||
if app_idx == 1 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local from = args[tostring(app_idx)]
|
|
||||||
local to = args[tostring(app_idx-1)]
|
|
||||||
args[tostring(app_idx)] = to
|
|
||||||
args[tostring(app_idx-1)] = from
|
|
||||||
settings:set_kv(args)
|
|
||||||
update_args()
|
|
||||||
redraw()
|
|
||||||
return
|
|
||||||
elseif idx == 2 then
|
|
||||||
local args = settings:get_kv()
|
|
||||||
local new_args = {}
|
|
||||||
for k,v in pairs(args) do
|
|
||||||
if k ~= tostring(app_idx) then
|
|
||||||
new_args[k] = v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
settings:set_kv(new_args)
|
|
||||||
update_args()
|
|
||||||
redraw()
|
|
||||||
return
|
|
||||||
elseif idx == 5 then
|
|
||||||
dialog_id = "move"
|
|
||||||
local text = "Number from 1 to "..tostring(max_key(settings:get_kv()))
|
|
||||||
ui:show_edit_dialog("Set position",text,app_idx)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function redraw()
|
|
||||||
local cols = tonumber(settings:get_kv()["columns"])
|
|
||||||
if cols == 0 or cols == nil then
|
|
||||||
cols = 1
|
|
||||||
end
|
|
||||||
ui:show_table(table_to_tables(tab_from_args(), cols))
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_click(idx)
|
|
||||||
apps:launch(settings:get_kv()[tostring(idx)])
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_long_click(idx)
|
|
||||||
app_idx = idx
|
|
||||||
ui:show_context_menu({
|
|
||||||
{ "chevron-right", "Forward" },
|
|
||||||
{ "times", "Remove" },
|
|
||||||
{ "chevron-left", "Back" },
|
|
||||||
{ "edit", "Edit" },
|
|
||||||
{ "exchange", "Move" }
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
function tab_from_args()
|
|
||||||
local args = settings:get_kv()
|
|
||||||
local len = tonumber(args["trim"])
|
|
||||||
if len == nil then
|
|
||||||
len = 0
|
|
||||||
end
|
|
||||||
local tab = {}
|
|
||||||
for k,v in pairs(args) do
|
|
||||||
if k == tostring(tonumber(k)) then
|
|
||||||
tab[tonumber(k)] = get_formatted_name(v,len)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return tab
|
|
||||||
end
|
|
||||||
|
|
||||||
function get_formatted_name(pkg,len)
|
|
||||||
local str = apps:get_name(pkg)
|
|
||||||
if utf8.len(str) > len and len > 0 then
|
|
||||||
str = utf8.sub(str,1,len-1):gsub("[%. ]*$","").."."
|
|
||||||
end
|
|
||||||
return "<font color=\""..apps:get_color(pkg).."\">"..str.."</font>"
|
|
||||||
end
|
|
||||||
|
|
||||||
function table_to_tables(tab, num)
|
|
||||||
local out_tab = {}
|
|
||||||
local row = {}
|
|
||||||
|
|
||||||
for k,v in ipairs(tab) do
|
|
||||||
table.insert(row, v)
|
|
||||||
if k % num == 0 then
|
|
||||||
table.insert(out_tab, row)
|
|
||||||
row = {}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if row ~= {} then
|
|
||||||
table.insert(out_tab, row)
|
|
||||||
end
|
|
||||||
return out_tab
|
|
||||||
end
|
|
||||||
|
|
||||||
function get_all_apps(sort_by,no_hidden)
|
|
||||||
if tostring(no_hidden) == "true" then
|
|
||||||
no_hidden = true
|
|
||||||
else
|
|
||||||
no_hidden = false
|
|
||||||
end
|
|
||||||
local t = settings:get_kv()
|
|
||||||
local all_apps = apps:get_list(sort_by,no_hidden)
|
|
||||||
local apps_names = {}
|
|
||||||
for k,v in ipairs(all_apps) do
|
|
||||||
apps_names[k] = apps:get_name(v)
|
|
||||||
end
|
|
||||||
return {all_apps,apps_names}
|
|
||||||
end
|
|
||||||
|
|
||||||
function args_to_idx(tab)
|
|
||||||
local args = settings:get_kv()
|
|
||||||
local t = {}
|
|
||||||
for k,v in pairs(args) do
|
|
||||||
local idx = get_index(tab,v)
|
|
||||||
if idx > 0 then
|
|
||||||
table.insert(t,idx)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
|
|
||||||
function idx_to_args(tab)
|
|
||||||
local args = settings:get_kv()
|
|
||||||
local all_apps = get_all_apps("abc",args["no_hidden"])[1]
|
|
||||||
local new_args = {}
|
|
||||||
for i,v in ipairs(tab) do
|
|
||||||
if get_key(args,all_apps[tonumber(v)]) == 0 then
|
|
||||||
new_args[tostring(max_key(args)+i)] = all_apps[tonumber(v)]
|
|
||||||
else
|
|
||||||
new_args[get_key(args,all_apps[tonumber(v)])] = all_apps[tonumber(v)]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
new_args = sort_by_key(new_args)
|
|
||||||
new_args["no_hidden"] = args["no_hidden"]
|
|
||||||
new_args["columns"] = args["columns"]
|
|
||||||
new_args["trim"] = args["trim"]
|
|
||||||
new_args["folding"] = args["folding"]
|
|
||||||
return new_args
|
|
||||||
end
|
|
||||||
|
|
||||||
function update_args()
|
|
||||||
local args = settings:get_kv()
|
|
||||||
local all_apps = get_all_apps("abc",args["no_hidden"])[1]
|
|
||||||
local new_args = {}
|
|
||||||
for k,v in pairs(args) do
|
|
||||||
if k == tostring(tonumber(k)) then
|
|
||||||
if get_index(all_apps,v) ~= 0 then
|
|
||||||
new_args[k] = v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
new_args = sort_by_key(new_args)
|
|
||||||
new_args["no_hidden"] = args["no_hidden"]
|
|
||||||
new_args["columns"] = args["columns"]
|
|
||||||
new_args["trim"] = args["trim"]
|
|
||||||
new_args["folding"] = args["folding"]
|
|
||||||
settings:set_kv(new_args)
|
|
||||||
end
|
|
||||||
|
|
||||||
function sort_by_key(tab)
|
|
||||||
local t = {}
|
|
||||||
local tt = {}
|
|
||||||
for k,v in pairs(tab) do
|
|
||||||
table.insert(t,tonumber(k))
|
|
||||||
end
|
|
||||||
table.sort(t)
|
|
||||||
for i,v in ipairs(t) do
|
|
||||||
for kk,vv in pairs(tab) do
|
|
||||||
if kk == tostring(v) then
|
|
||||||
tt[tostring(i)] = vv
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return tt
|
|
||||||
end
|
|
||||||
|
|
||||||
function max_key(tab)
|
|
||||||
local t = {}
|
|
||||||
for k,v in pairs(tab) do
|
|
||||||
if k == tostring(tonumber(k)) then
|
|
||||||
table.insert(t,k)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
table.sort(t)
|
|
||||||
return #t
|
|
||||||
end
|
|
||||||
66
community/arm-ru-abc-widget.lua
Normal file
66
community/arm-ru-abc-widget.lua
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
-- name = "Армянский алфавит"
|
||||||
|
-- description = "Армянский алфавит с транскрипцией"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
||||||
|
-- version = "1.0"
|
||||||
|
-- lang = "ru"
|
||||||
|
|
||||||
|
local tab = {
|
||||||
|
{
|
||||||
|
"<b>Աա</b>", "А",
|
||||||
|
"<b>Բբ</b>", "Б",
|
||||||
|
"<b>Գգ</b>", "Г",
|
||||||
|
"<b>Դդ</b>", "Д",
|
||||||
|
"<b>Եե</b>", "Е",
|
||||||
|
"<b>Զզ</b>", "З",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<b>Էէ</b>", "Э",
|
||||||
|
"<b>Ըը</b>", "Ы",
|
||||||
|
"<b>Թթ</b>", "Тh",
|
||||||
|
"<b>Ժժ</b>", "Ж",
|
||||||
|
"<b>Իի</b>", "И",
|
||||||
|
"<b>Լլ</b>", "Ль",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<b>Խխ</b>", "Х",
|
||||||
|
"<b>Ծծ</b>", "Ц",
|
||||||
|
"<b>Կկ</b>", "K",
|
||||||
|
"<b>Հհ</b>", "h",
|
||||||
|
"<b>Ձձ</b>", "Дз",
|
||||||
|
"<b>Ղղ</b>", "Гг",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<b>Ճճ</b>", "Ч",
|
||||||
|
"<b>Մմ</b>", "М",
|
||||||
|
"<b>Յյ</b>", "Й",
|
||||||
|
"<b>Նն</b>", "Н",
|
||||||
|
"<b>Շշ</b>", "Ш",
|
||||||
|
"<b>Ոո</b>", "О",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<b>Չչ</b>", "Ч",
|
||||||
|
"<b>Պպ</b>", "П",
|
||||||
|
"<b>Ջջ</b>", "Дж",
|
||||||
|
"<b>Ռռ</b>", "Рр",
|
||||||
|
"<b>Սս</b>", "С",
|
||||||
|
"<b>Վվ</b>", "В",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<b>Տտ</b>", "Т",
|
||||||
|
"<b>Րր</b>", "Р",
|
||||||
|
"<b>Ցց</b>", "Цh",
|
||||||
|
"<b>Ու</b>", "У",
|
||||||
|
"<b>Փփ</b>", "Пh",
|
||||||
|
"<b>Քք</b>", "Kh",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"<b>և</b>", "Ев",
|
||||||
|
"<b>Oo</b>", "О",
|
||||||
|
"<b>Ֆֆ</b>", "Ф",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
ui:show_table(tab)
|
||||||
|
end
|
||||||
@@ -4,38 +4,40 @@
|
|||||||
-- type = "search"
|
-- type = "search"
|
||||||
-- lang = "ru"
|
-- lang = "ru"
|
||||||
-- author = "Andrey Gavrilov"
|
-- author = "Andrey Gavrilov"
|
||||||
-- version = "1.0"
|
-- version = "1.1"
|
||||||
|
|
||||||
local num = ""
|
|
||||||
|
|
||||||
function on_search(input)
|
function on_search(input)
|
||||||
num = input:match("^n (.+)")
|
local num = input:match("^(+?7?%d%d%d%d%d%d%d%d%d%d+)$")
|
||||||
if not num then
|
if not num then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
search:show({"Оператор "..num})
|
show_operator(num)
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_click()
|
function show_operator(num)
|
||||||
|
local json = require "json"
|
||||||
local uri = "http://rosreestr.subnets.ru/?get=num&format=json&num=" .. num:gsub("%D", "")
|
local uri = "http://rosreestr.subnets.ru/?get=num&format=json&num=" .. num:gsub("%D", "")
|
||||||
http:get(uri)
|
local tab = {}
|
||||||
return false
|
local result = shttp:get(uri)
|
||||||
end
|
if not result.error then
|
||||||
|
local t = json.decode(result.body)
|
||||||
function on_network_result(result)
|
if not t.error then
|
||||||
local json = require "json"
|
if not t["0"].country then
|
||||||
local t = json.decode(result)
|
if not t["0"].moved2operator then
|
||||||
if not t.error then
|
table.insert(tab, t["0"].operator)
|
||||||
if not t["0"].country then
|
else
|
||||||
if not t["0"].moved2operator then
|
table.insert(tab, t["0"].moved2operator)
|
||||||
search:show({t["0"].operator, t["0"].region})
|
end
|
||||||
|
table.insert(tab, t["0"].region)
|
||||||
else
|
else
|
||||||
search:show({t["0"].moved2operator, t["0"].region})
|
table.insert(tab, t["0"].country)
|
||||||
|
table.insert(tab, t["0"].description)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
search:show({t["0"].country, t["0"].description})
|
table.insert(tab, t.error)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
search:show({t.error})
|
table.insert(tab, result.error)
|
||||||
end
|
end
|
||||||
|
search:show_lines({table.concat(tab, ", ")}, {aio:colors().button})
|
||||||
end
|
end
|
||||||
|
|||||||
313
community/bible-search.lua
Normal file
313
community/bible-search.lua
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
-- name = "Bible Search"
|
||||||
|
-- type = "search"
|
||||||
|
-- author = "Evon Smith"
|
||||||
|
-- description = "Type in Bible book and chapter to open the YouVersion Bible App to that location right from the search window"
|
||||||
|
-- version = "2.0"
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
-- Complete Bible books list with aliases and chapters
|
||||||
|
------------------------------------------------------------
|
||||||
|
local BOOKS = {
|
||||||
|
{ name = "Genesis", aliases = {"genesis","gen","ge"}, chapters = 50, osis = "GEN" },
|
||||||
|
{ name = "Exodus", aliases = {"exodus","exo","ex"}, chapters = 40, osis = "EXO" },
|
||||||
|
{ name = "Leviticus", aliases = {"leviticus","lev","le"}, chapters = 27, osis = "LEV" },
|
||||||
|
{ name = "Numbers", aliases = {"numbers","num","nu","nm"}, chapters = 36, osis = "NUM" },
|
||||||
|
{ name = "Deuteronomy", aliases = {"deuteronomy","deut","dt"}, chapters = 34, osis = "DEU" },
|
||||||
|
{ name = "Joshua", aliases = {"joshua","josh","jos"}, chapters = 24, osis = "JOS" },
|
||||||
|
{ name = "Judges", aliases = {"judges","judg","jdg"}, chapters = 21, osis = "JDG" },
|
||||||
|
{ name = "Ruth", aliases = {"ruth","ru"}, chapters = 4, osis = "RUT" },
|
||||||
|
{ name = "1 Samuel", aliases = {"1samuel","1sam","1sa","i samuel","1 sam"}, chapters = 31, osis = "1SA" },
|
||||||
|
{ name = "2 Samuel", aliases = {"2samuel","2sam","2sa","ii samuel","2 sam"}, chapters = 24, osis = "2SA" },
|
||||||
|
{ name = "1 Kings", aliases = {"1kings","1ki","i kings","1 ki"}, chapters = 22, osis = "1KI" },
|
||||||
|
{ name = "2 Kings", aliases = {"2kings","2ki","ii kings","2 ki"}, chapters = 25, osis = "2KI" },
|
||||||
|
{ name = "1 Chronicles", aliases = {"1chronicles","1chr","i chronicles","1 chr"}, chapters = 29, osis = "1CH" },
|
||||||
|
{ name = "2 Chronicles", aliases = {"2chronicles","2chr","ii chronicles","2 chr"}, chapters = 36, osis = "2CH" },
|
||||||
|
{ name = "Ezra", aliases = {"ezra","ezr"}, chapters = 10, osis = "EZR" },
|
||||||
|
{ name = "Nehemiah", aliases = {"nehemiah","neh","ne"}, chapters = 13, osis = "NEH" },
|
||||||
|
{ name = "Esther", aliases = {"esther","est","es"}, chapters = 10, osis = "EST" },
|
||||||
|
{ name = "Job", aliases = {"job","jb"}, chapters = 42, osis = "JOB" },
|
||||||
|
{ name = "Psalms", aliases = {"psalms","psalm","ps","psa"}, chapters = 150, osis = "PSA" },
|
||||||
|
{ name = "Proverbs", aliases = {"proverbs","prov","prv","pr"}, chapters = 31, osis = "PRO" },
|
||||||
|
{ name = "Ecclesiastes", aliases = {"ecclesiastes","eccl","ecc"}, chapters = 12, osis = "ECC" },
|
||||||
|
{ name = "Song of Solomon", aliases = {"songofsolomon","song","so","ss","song of songs","songs"}, chapters = 8, osis = "SNG" },
|
||||||
|
{ name = "Isaiah", aliases = {"isaiah","isa","is"}, chapters = 66, osis = "ISA" },
|
||||||
|
{ name = "Jeremiah", aliases = {"jeremiah","jer","je"}, chapters = 52, osis = "JER" },
|
||||||
|
{ name = "Lamentations", aliases = {"lamentations","lam","la"}, chapters = 5, osis = "LAM" },
|
||||||
|
{ name = "Ezekiel", aliases = {"ezekiel","ezek","eze"}, chapters = 48, osis = "EZK" },
|
||||||
|
{ name = "Daniel", aliases = {"daniel","dan","da"}, chapters = 12, osis = "DAN" },
|
||||||
|
{ name = "Hosea", aliases = {"hosea","hos","ho"}, chapters = 14, osis = "HOS" },
|
||||||
|
{ name = "Joel", aliases = {"joel","jl"}, chapters = 3, osis = "JOL" },
|
||||||
|
{ name = "Amos", aliases = {"amos","am"}, chapters = 9, osis = "AMO" },
|
||||||
|
{ name = "Obadiah", aliases = {"obadiah","obad","ob"}, chapters = 1, osis = "OBA" },
|
||||||
|
{ name = "Jonah", aliases = {"jonah","jon","jh"}, chapters = 4, osis = "JON" },
|
||||||
|
{ name = "Micah", aliases = {"micah","mic","mc"}, chapters = 7, osis = "MIC" },
|
||||||
|
{ name = "Nahum", aliases = {"nahum","nah","na"}, chapters = 3, osis = "NAM" },
|
||||||
|
{ name = "Habakkuk", aliases = {"habakkuk","hab","hb"}, chapters = 3, osis = "HAB" },
|
||||||
|
{ name = "Zephaniah", aliases = {"zephaniah","zeph","zep"}, chapters = 3, osis = "ZEP" },
|
||||||
|
{ name = "Haggai", aliases = {"haggai","hag","hg"}, chapters = 2, osis = "HAG" },
|
||||||
|
{ name = "Zechariah", aliases = {"zechariah","zech","zec"}, chapters = 14, osis = "ZEC" },
|
||||||
|
{ name = "Malachi", aliases = {"malachi","mal","ml"}, chapters = 4, osis = "MAL" },
|
||||||
|
{ name = "Matthew", aliases = {"matthew","matt","mt"}, chapters = 28, osis = "MAT" },
|
||||||
|
{ name = "Mark", aliases = {"mark","mk","mrk"}, chapters = 16, osis = "MRK" },
|
||||||
|
{ name = "Luke", aliases = {"luke","lk","luk"}, chapters = 24, osis = "LUK" },
|
||||||
|
{ name = "John", aliases = {"john","jn","jhn"}, chapters = 21, osis = "JHN" },
|
||||||
|
{ name = "Acts", aliases = {"acts","ac","acts of the apostles"}, chapters = 28, osis = "ACT" },
|
||||||
|
{ name = "Romans", aliases = {"romans","rom","ro"}, chapters = 16, osis = "ROM" },
|
||||||
|
{ name = "1 Corinthians", aliases = {"1corinthians","1cor","1co","i corinthians","1 cor"}, chapters = 16, osis = "1CO" },
|
||||||
|
{ name = "2 Corinthians", aliases = {"2corinthians","2cor","2co","ii corinthians","2 cor"}, chapters = 13, osis = "2CO" },
|
||||||
|
{ name = "Galatians", aliases = {"galatians","gal","ga"}, chapters = 6, osis = "GAL" },
|
||||||
|
{ name = "Ephesians", aliases = {"ephesians","eph","ep"}, chapters = 6, osis = "EPH" },
|
||||||
|
{ name = "Philippians", aliases = {"philippians","phil","php"}, chapters = 4, osis = "PHP" },
|
||||||
|
{ name = "Colossians", aliases = {"colossians","col","co"}, chapters = 4, osis = "COL" },
|
||||||
|
{ name = "1 Thessalonians", aliases = {"1thessalonians","1thess","1th","i thessalonians","1 thess"}, chapters = 5, osis = "1TH" },
|
||||||
|
{ name = "2 Thessalonians", aliases = {"2thessalonians","2thess","2th","ii thessalonians","2 thess"}, chapters = 3, osis = "2TH" },
|
||||||
|
{ name = "1 Timothy", aliases = {"1timothy","1tim","1ti","i timothy","1 tim"}, chapters = 6, osis = "1TI" },
|
||||||
|
{ name = "2 Timothy", aliases = {"2timothy","2tim","2ti","ii timothy","2 tim"}, chapters = 4, osis = "2TI" },
|
||||||
|
{ name = "Titus", aliases = {"titus","tit","ti"}, chapters = 3, osis = "TIT" },
|
||||||
|
{ name = "Philemon", aliases = {"philemon","philem","phm"}, chapters = 1, osis = "PHM" },
|
||||||
|
{ name = "Hebrews", aliases = {"hebrews","heb","he"}, chapters = 13, osis = "HEB" },
|
||||||
|
{ name = "James", aliases = {"james","jas","jm"}, chapters = 5, osis = "JAS" },
|
||||||
|
{ name = "1 Peter", aliases = {"1peter","1pet","1pe","i peter","1 pet"}, chapters = 5, osis = "1PE" },
|
||||||
|
{ name = "2 Peter", aliases = {"2peter","2pet","2pe","ii peter","2 pet"}, chapters = 3, osis = "2PE" },
|
||||||
|
{ name = "1 John", aliases = {"1john","1jn","i john","1 jn"}, chapters = 5, osis = "1JN" },
|
||||||
|
{ name = "2 John", aliases = {"2john","2jn","ii john","2 jn"}, chapters = 1, osis = "2JN" },
|
||||||
|
{ name = "3 John", aliases = {"3john","3jn","iii john","3 jn"}, chapters = 1, osis = "3JN" },
|
||||||
|
{ name = "Jude", aliases = {"jude","jud"}, chapters = 1, osis = "JUD" },
|
||||||
|
{ name = "Revelation", aliases = {"revelation","rev","re","revelations"}, chapters = 22, osis = "REV" },
|
||||||
|
}
|
||||||
|
|
||||||
|
-- Build alias lookup table
|
||||||
|
local ALIAS = {}
|
||||||
|
local function _norm(s) return (s or ""):lower():gsub("[%s%p]+","") end
|
||||||
|
for _, b in ipairs(BOOKS) do
|
||||||
|
ALIAS[_norm(b.name)] = b
|
||||||
|
for _, a in ipairs(b.aliases) do ALIAS[_norm(a)] = b end
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
-- State management
|
||||||
|
------------------------------------------------------------
|
||||||
|
local suggestions = {} -- Store all clickable suggestions with their URLs
|
||||||
|
local currentQuery = ""
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
-- Helper functions
|
||||||
|
------------------------------------------------------------
|
||||||
|
local function trim(s) return (s or ""):match("^%s*(.-)%s*$") end
|
||||||
|
|
||||||
|
-- Enhanced parser that handles multi-word book names better
|
||||||
|
local function parse_book_chapter(q)
|
||||||
|
q = trim(q or "")
|
||||||
|
if q == "" then return nil, nil end
|
||||||
|
|
||||||
|
-- Try to match book name with optional chapter
|
||||||
|
-- This pattern allows for multi-word book names
|
||||||
|
local book_part, chapter_part = q:match("^(.-)%s+(%d+)$")
|
||||||
|
|
||||||
|
if book_part and chapter_part then
|
||||||
|
-- Found both book and chapter
|
||||||
|
return trim(book_part), tonumber(chapter_part)
|
||||||
|
else
|
||||||
|
-- No chapter number found, treat entire query as book name
|
||||||
|
return q, nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Enhanced book resolver for better multi-word matching
|
||||||
|
local function resolve_book(letters)
|
||||||
|
if not letters then return nil end
|
||||||
|
|
||||||
|
local key = _norm(letters)
|
||||||
|
|
||||||
|
-- Exact match first
|
||||||
|
if ALIAS[key] then return ALIAS[key] end
|
||||||
|
|
||||||
|
-- Try partial matching for better multi-word support
|
||||||
|
local matches = {}
|
||||||
|
for ak, b in pairs(ALIAS) do
|
||||||
|
if ak:find("^" .. key) then
|
||||||
|
table.insert(matches, {alias = ak, book = b, priority = 1})
|
||||||
|
elseif ak:find(key, 1, true) then
|
||||||
|
table.insert(matches, {alias = ak, book = b, priority = 2})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Return best match if found
|
||||||
|
if #matches > 0 then
|
||||||
|
table.sort(matches, function(a, b)
|
||||||
|
if a.priority == b.priority then
|
||||||
|
return #a.alias < #b.alias -- Prefer shorter matches
|
||||||
|
end
|
||||||
|
return a.priority < b.priority
|
||||||
|
end)
|
||||||
|
return matches[1].book
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function build_youversion_url(osis, chapter)
|
||||||
|
return string.format("youversion://bible?reference=%s.%d", osis, chapter)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get matching books based on query
|
||||||
|
local function get_matching_books(query)
|
||||||
|
if not query or query == "" then
|
||||||
|
return BOOKS -- Return all books if no query
|
||||||
|
end
|
||||||
|
|
||||||
|
local matches = {}
|
||||||
|
local key = _norm(query)
|
||||||
|
|
||||||
|
for _, book in ipairs(BOOKS) do
|
||||||
|
local book_key = _norm(book.name)
|
||||||
|
local is_match = false
|
||||||
|
|
||||||
|
-- Check if book name starts with query
|
||||||
|
if book_key:find("^" .. key) then
|
||||||
|
is_match = true
|
||||||
|
else
|
||||||
|
-- Check aliases
|
||||||
|
for _, alias in ipairs(book.aliases) do
|
||||||
|
if _norm(alias):find("^" .. key) then
|
||||||
|
is_match = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if is_match then
|
||||||
|
table.insert(matches, book)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If no matches found with prefix matching, try substring matching
|
||||||
|
if #matches == 0 then
|
||||||
|
for _, book in ipairs(BOOKS) do
|
||||||
|
local book_key = _norm(book.name)
|
||||||
|
if book_key:find(key, 1, true) then
|
||||||
|
table.insert(matches, book)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return #matches > 0 and matches or BOOKS
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
-- Main search function
|
||||||
|
------------------------------------------------------------
|
||||||
|
function on_search(query)
|
||||||
|
currentQuery = query
|
||||||
|
suggestions = {}
|
||||||
|
|
||||||
|
local book_part, chapter = parse_book_chapter(query)
|
||||||
|
|
||||||
|
if book_part then
|
||||||
|
local specific_book = resolve_book(book_part)
|
||||||
|
|
||||||
|
if specific_book and chapter then
|
||||||
|
-- Specific book and chapter requested
|
||||||
|
if chapter >= 1 and chapter <= specific_book.chapters then
|
||||||
|
local url = build_youversion_url(specific_book.osis, chapter)
|
||||||
|
suggestions[1] = {
|
||||||
|
text = string.format("Open %s %d in YouVersion", specific_book.name, chapter),
|
||||||
|
url = url
|
||||||
|
}
|
||||||
|
search:show_buttons({suggestions[1].text})
|
||||||
|
return
|
||||||
|
else
|
||||||
|
search:show_lines({
|
||||||
|
string.format("%s only has %d chapter%s",
|
||||||
|
specific_book.name,
|
||||||
|
specific_book.chapters,
|
||||||
|
specific_book.chapters == 1 and "" or "s")
|
||||||
|
})
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Show book/chapter suggestions
|
||||||
|
local matching_books = get_matching_books(book_part)
|
||||||
|
local button_texts = {}
|
||||||
|
local max_suggestions = 10 -- Limit number of suggestions shown
|
||||||
|
local suggestion_count = 0
|
||||||
|
|
||||||
|
for _, book in ipairs(matching_books) do
|
||||||
|
if suggestion_count >= max_suggestions then break end
|
||||||
|
|
||||||
|
-- If book has few chapters, show all in one line
|
||||||
|
if book.chapters <= 5 then
|
||||||
|
local chapters_text = ""
|
||||||
|
for ch = 1, book.chapters do
|
||||||
|
if ch > 1 then chapters_text = chapters_text .. ", " end
|
||||||
|
chapters_text = chapters_text .. ch
|
||||||
|
end
|
||||||
|
suggestion_count = suggestion_count + 1
|
||||||
|
local idx = #suggestions + 1
|
||||||
|
suggestions[idx] = {
|
||||||
|
text = string.format("%s (Ch: %s)", book.name, chapters_text),
|
||||||
|
book = book,
|
||||||
|
all_chapters = true
|
||||||
|
}
|
||||||
|
table.insert(button_texts, suggestions[idx].text)
|
||||||
|
else
|
||||||
|
-- For books with many chapters, show range
|
||||||
|
suggestion_count = suggestion_count + 1
|
||||||
|
local idx = #suggestions + 1
|
||||||
|
suggestions[idx] = {
|
||||||
|
text = string.format("%s (1-%d chapters)", book.name, book.chapters),
|
||||||
|
book = book,
|
||||||
|
all_chapters = true
|
||||||
|
}
|
||||||
|
table.insert(button_texts, suggestions[idx].text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #button_texts == 0 then
|
||||||
|
search:show_lines({"No matching books found. Try: Genesis, John, Psalms..."})
|
||||||
|
else
|
||||||
|
search:show_buttons(button_texts)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
-- Handle button clicks
|
||||||
|
------------------------------------------------------------
|
||||||
|
function on_click(index)
|
||||||
|
if not index then index = 1 end
|
||||||
|
|
||||||
|
local suggestion = suggestions[index]
|
||||||
|
if not suggestion then return false end
|
||||||
|
|
||||||
|
-- If it's a direct chapter link, open it
|
||||||
|
if suggestion.url then
|
||||||
|
system:open_browser(suggestion.url)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If it's a book suggestion, show its chapters
|
||||||
|
if suggestion.book and suggestion.all_chapters then
|
||||||
|
local book = suggestion.book
|
||||||
|
suggestions = {}
|
||||||
|
local button_texts = {}
|
||||||
|
|
||||||
|
-- Create buttons for each chapter
|
||||||
|
for ch = 1, book.chapters do
|
||||||
|
local idx = #suggestions + 1
|
||||||
|
local url = build_youversion_url(book.osis, ch)
|
||||||
|
suggestions[idx] = {
|
||||||
|
text = string.format("%s %d", book.name, ch),
|
||||||
|
url = url
|
||||||
|
}
|
||||||
|
table.insert(button_texts, suggestions[idx].text)
|
||||||
|
end
|
||||||
|
|
||||||
|
search:show_buttons(button_texts)
|
||||||
|
return false -- Don't close search, allow chapter selection
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
116
community/birthdays-widget.lua
Normal file
116
community/birthdays-widget.lua
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
-- name = "Birthdays"
|
||||||
|
-- name_id = "birthday"
|
||||||
|
-- description = "Shows upcoming birthdays from the contacts"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Andrey Gavrilov"
|
||||||
|
-- version = "1.3"
|
||||||
|
|
||||||
|
local prefs = require "prefs"
|
||||||
|
local fmt = require "fmt"
|
||||||
|
|
||||||
|
local months = {
|
||||||
|
"January",
|
||||||
|
"February",
|
||||||
|
"March",
|
||||||
|
"April",
|
||||||
|
"May",
|
||||||
|
"June",
|
||||||
|
"July",
|
||||||
|
"August",
|
||||||
|
"September",
|
||||||
|
"October",
|
||||||
|
"November",
|
||||||
|
"December"
|
||||||
|
}
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
if not prefs.count then
|
||||||
|
prefs.count = 10
|
||||||
|
end
|
||||||
|
phone:request_permission()
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_permission_granted()
|
||||||
|
contacts = calendar:contacts_events()
|
||||||
|
redraw()
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_contacts_loaded()
|
||||||
|
redraw()
|
||||||
|
end
|
||||||
|
|
||||||
|
function redraw()
|
||||||
|
table.sort(contacts,function(a,b) return a.begin < b.begin end)
|
||||||
|
events = {}
|
||||||
|
local lines = {}
|
||||||
|
local lines_exp = {}
|
||||||
|
prev_begin = contacts[1].begin
|
||||||
|
for i,v in ipairs(contacts) do
|
||||||
|
local fmt_out = fmt_line(v)
|
||||||
|
local insert = 0
|
||||||
|
if #lines == 0 then
|
||||||
|
insert = 1
|
||||||
|
elseif not (fmt_out == lines[#lines]) then
|
||||||
|
insert = 1
|
||||||
|
end
|
||||||
|
if insert == 1 then
|
||||||
|
if #lines_exp >= prefs.count and prev_begin ~= v.begin then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
table.insert(events, v)
|
||||||
|
if #lines < prefs.count then
|
||||||
|
table.insert(lines, fmt_out)
|
||||||
|
end
|
||||||
|
table.insert(lines_exp, fmt_out)
|
||||||
|
prev_begin = v.begin
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if ui.set_expandable then
|
||||||
|
if #lines_exp == #lines then
|
||||||
|
ui:set_expandable(false)
|
||||||
|
else
|
||||||
|
ui:set_expandable(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if ui.is_expanded and ui:is_expanded() then
|
||||||
|
ui:show_lines(lines_exp)
|
||||||
|
else
|
||||||
|
ui:show_lines(lines)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function fmt_line(event)
|
||||||
|
local line = event.title
|
||||||
|
if os.date("%y%m%d",event.begin) == os.date("%y%m%d") then
|
||||||
|
line = fmt.bold(fmt.colored(line, aio:colors().accent))
|
||||||
|
end
|
||||||
|
return line .. fmt.secondary(" - ") .. fmt_date(event.begin)
|
||||||
|
end
|
||||||
|
|
||||||
|
function fmt_date(date)
|
||||||
|
local d = ""
|
||||||
|
if os.date("%y%m%d",date) == os.date("%y%m%d") then
|
||||||
|
d = "Today"
|
||||||
|
elseif os.date("%y%m%d",date-86400) == os.date("%y%m%d") then
|
||||||
|
d = "Tomorrow"
|
||||||
|
else
|
||||||
|
d = months[tonumber(os.date("%m", date))] .. ", " .. tostring(tonumber(os.date("%d", date)))
|
||||||
|
end
|
||||||
|
return fmt.secondary(d)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
calendar:show_event_dialog(events[idx])
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_settings()
|
||||||
|
dialogs:show_radio_dialog("Number of events", {1,2,3,4,5,6,7,8,9,10}, prefs.count)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_dialog_action(idx)
|
||||||
|
if idx == -1 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
prefs.count = idx
|
||||||
|
redraw()
|
||||||
|
end
|
||||||
89
community/calendar-progress-widget.lua
Normal file
89
community/calendar-progress-widget.lua
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
-- name = "Calendar Progress"
|
||||||
|
-- description = "Shows day, week, month, and year progress bars"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "meluskyc"
|
||||||
|
-- version = "1.0"
|
||||||
|
-- foldable = "false"
|
||||||
|
|
||||||
|
local prefs = require "prefs"
|
||||||
|
|
||||||
|
function on_load()
|
||||||
|
prefs.show_day = true
|
||||||
|
prefs.show_week = true
|
||||||
|
prefs.show_month = true
|
||||||
|
prefs.show_year = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
local now = os.date("*t", os.time())
|
||||||
|
local gui_elems = {}
|
||||||
|
|
||||||
|
if prefs.show_day then
|
||||||
|
local seconds_since_day_start =
|
||||||
|
now.sec + -- current minute
|
||||||
|
now.min * 60 + -- previous minutes
|
||||||
|
now.hour * 3600 -- previous hours
|
||||||
|
local progress_percentage = math.floor((seconds_since_day_start / (24 * 60 * 60)) * 100)
|
||||||
|
local label = "Day: " .. progress_percentage .. "%"
|
||||||
|
table.insert(gui_elems, {"progress", label, {progress = progress_percentage}})
|
||||||
|
|
||||||
|
if prefs.show_week or prefs.show_month or prefs.show_year then
|
||||||
|
table.insert(gui_elems, {"new_line", 1})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if prefs.show_week then
|
||||||
|
local seconds_since_week_start =
|
||||||
|
now.sec + -- current minute
|
||||||
|
now.min * 60 + -- previous minutes
|
||||||
|
now.hour * 3600 + -- previous hours
|
||||||
|
(now.wday - 1) * 86400 -- previous days
|
||||||
|
local progress_percentage = math.floor((seconds_since_week_start / (7 * 24 * 60 * 60)) * 100)
|
||||||
|
local label = "Week: " .. progress_percentage .. "%"
|
||||||
|
table.insert(gui_elems, {"progress", label, {progress = progress_percentage}})
|
||||||
|
|
||||||
|
if prefs.show_month or prefs.show_year then
|
||||||
|
table.insert(gui_elems, {"new_line", 1})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if prefs.show_month then
|
||||||
|
local days_in_month = os.date("*t", os.time{year=now.year, month=now.month+1, day=0}).day
|
||||||
|
local seconds_since_month_start =
|
||||||
|
now.sec + -- current minute
|
||||||
|
now.min * 60 + -- previous minutes
|
||||||
|
now.hour * 3600 + -- previous hours
|
||||||
|
(now.day - 1) * 86400 -- previous days
|
||||||
|
local progress_percentage = math.floor((seconds_since_month_start / (days_in_month * 24 * 60 * 60)) * 100)
|
||||||
|
local label = "Month: " .. progress_percentage .. "%"
|
||||||
|
table.insert(gui_elems, {"progress", label, {progress = progress_percentage}})
|
||||||
|
|
||||||
|
if prefs.show_year then
|
||||||
|
table.insert(gui_elems, {"new_line", 1})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if prefs.show_year then
|
||||||
|
local seconds_since_year_start =
|
||||||
|
now.sec + -- current minute
|
||||||
|
now.min * 60 + -- previous minutes
|
||||||
|
now.hour * 3600 + -- previous hours
|
||||||
|
(now.yday - 1) * 86400 -- previous days
|
||||||
|
|
||||||
|
-- check for leap year
|
||||||
|
local days_in_year = 365
|
||||||
|
if (now.year % 4 == 0 and now.year % 100 ~= 0) or (now.year % 400 == 0) then
|
||||||
|
days_in_year = 366
|
||||||
|
end
|
||||||
|
|
||||||
|
local progress_percentage = math.floor((seconds_since_year_start / (days_in_year * 24 * 60 * 60)) * 100)
|
||||||
|
local label = "Year: " .. progress_percentage .. "%"
|
||||||
|
table.insert(gui_elems, {"progress", label, {progress = progress_percentage}})
|
||||||
|
end
|
||||||
|
|
||||||
|
gui(gui_elems).render()
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_settings()
|
||||||
|
prefs:show_dialog()
|
||||||
|
end
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
-- description = "Скрипт показывает историю вызов через прямое чтение базы звонков"
|
-- description = "Скрипт показывает историю вызов через прямое чтение базы звонков"
|
||||||
-- type = "widget"
|
-- type = "widget"
|
||||||
-- author = "Andrey Gavrilov"
|
-- author = "Andrey Gavrilov"
|
||||||
-- version = "1.0"
|
-- version = "1.1"
|
||||||
-- lang = "ru"
|
-- lang = "ru"
|
||||||
-- root = "true"
|
-- root = "true"
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ end
|
|||||||
|
|
||||||
function on_click(idx)
|
function on_click(idx)
|
||||||
if math.ceil(idx/3) > #tab then
|
if math.ceil(idx/3) > #tab then
|
||||||
ui:show_radio_dialog("Выберите тип вызовов",types,typ)
|
dialogs:show_radio_dialog("Выберите тип вызовов",types,typ)
|
||||||
else
|
else
|
||||||
local cmd = "am start -a android.intent.action.DIAL -d tel:"..tab[math.ceil(idx/3)].number
|
local cmd = "am start -a android.intent.action.DIAL -d tel:"..tab[math.ceil(idx/3)].number
|
||||||
system:exec(cmd)
|
system:exec(cmd)
|
||||||
|
|||||||
41
community/chrome-shortctus-app-widget.lua
Normal file
41
community/chrome-shortctus-app-widget.lua
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
-- name = "Chrome shortcuts"
|
||||||
|
-- description = "AIO wrapper for the Chrome shortcuts app widget"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
||||||
|
-- version = "1.0"
|
||||||
|
-- foldable = "false"
|
||||||
|
-- uses_app: "com.android.chrome"
|
||||||
|
|
||||||
|
local prefs = require "prefs"
|
||||||
|
prefs._name = "chrome"
|
||||||
|
|
||||||
|
local buttons_labels = {"fa:magnifying-glass", "fa:microphone", "fa:hat_cowboy_side", "fa:camera", "fa:gamepad"}
|
||||||
|
local buttons_targets = {"h_layout_2", "image_2", "image_3", "image_4", "image_5"}
|
||||||
|
local w_bridge = nil
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
if not widgets:bound(prefs.wid) then
|
||||||
|
setup_app_widget()
|
||||||
|
end
|
||||||
|
|
||||||
|
widgets:request_updates(prefs.wid)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_app_widget_updated(bridge)
|
||||||
|
w_bridge = bridge
|
||||||
|
ui:show_buttons(buttons_labels)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
w_bridge:click(buttons_targets[idx])
|
||||||
|
end
|
||||||
|
|
||||||
|
function setup_app_widget()
|
||||||
|
local id = widgets:setup("com.android.chrome/org.chromium.chrome.browser.quickactionsearchwidget.QuickActionSearchWidgetProvider$QuickActionSearchWidgetProviderSearch")
|
||||||
|
if (id ~= nil) then
|
||||||
|
prefs.wid = id
|
||||||
|
else
|
||||||
|
ui:show_text("Can't add widget")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -2,12 +2,14 @@
|
|||||||
-- description = "Time counting widget to fight bad habits"
|
-- description = "Time counting widget to fight bad habits"
|
||||||
-- type = "widget"
|
-- type = "widget"
|
||||||
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
||||||
-- arguments_help = "Enter the date of the start of counting in the format DD.MM.YYYY"
|
-- version = "2.0"
|
||||||
-- version = "1.0"
|
-- on_resume_when_folding = "true"
|
||||||
|
|
||||||
|
local prefs = require "prefs"
|
||||||
local date = require "date"
|
local date = require "date"
|
||||||
|
|
||||||
-- constants
|
-- constants
|
||||||
|
local max_counters = 5
|
||||||
local year = 365.25
|
local year = 365.25
|
||||||
local month = 30.43
|
local month = 30.43
|
||||||
|
|
||||||
@@ -18,44 +20,129 @@ local milestones = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
local milestones_formatted = {
|
local milestones_formatted = {
|
||||||
"1 day", "3 days", "1 week", "2 weeks",
|
"1+ days", "3+ days", "1+ weeks", "2+ weeks",
|
||||||
"1 months", "3 months", "6 months",
|
"1+ months", "3+ months", "6+ months",
|
||||||
"1 year", "3 years", "5 years", "10 years", "20 years", "100 years"
|
"1+ years", "3+ years", "5+ years", "10+ years", "20+ years", "100+ years"
|
||||||
}
|
}
|
||||||
|
|
||||||
function on_resume()
|
function on_load()
|
||||||
local args = settings:get()
|
init_default_settings()
|
||||||
|
end
|
||||||
|
|
||||||
if next(args) == nil then
|
function on_resume()
|
||||||
ui:show_text("Tap to enter date")
|
local gui_inst = {}
|
||||||
return
|
local lines_num = max_counters
|
||||||
|
|
||||||
|
if ui:folding_flag() then
|
||||||
|
lines_num = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, lines_num do
|
||||||
|
local title, start_date = parse_settings_string(i)
|
||||||
|
if title == nil then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local curr_date = date()
|
||||||
|
local passed = date.diff(curr_date, start_date)
|
||||||
|
local passed_days = math.floor(passed:spandays())
|
||||||
|
|
||||||
|
if passed_days < 0 then
|
||||||
|
table.insert(gui_inst, {"text", "Error: the date can't be in the future"})
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
local idx = get_milestone_idx(passed)
|
||||||
|
local passed_str = passed_days.." days"
|
||||||
|
if prefs.show_milestones and passed_days > 0 then
|
||||||
|
passed_str = passed_str.." / "..milestones_formatted[idx]
|
||||||
|
end
|
||||||
|
local next_milestone_percent = passed_days / milestones[idx+1] * 100
|
||||||
|
|
||||||
|
table.insert(gui_inst, {"progress", title..": "..passed_str, {progress = next_milestone_percent}})
|
||||||
|
end
|
||||||
|
|
||||||
|
gui_inst = insert_between_elements(gui_inst, {"new_line", 1})
|
||||||
|
gui(gui_inst):render()
|
||||||
|
end
|
||||||
|
|
||||||
|
function init_default_settings()
|
||||||
|
if prefs.counter_1 == nil then
|
||||||
|
prefs["counter_1"] = "Sample / 31.01.2024"
|
||||||
|
for i = 2,max_counters do
|
||||||
|
prefs["counter_"..i] = ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if prefs.show_milestones == nil then
|
||||||
|
prefs.show_milestones = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function parse_settings_string(i)
|
||||||
|
local title_and_date = prefs["counter_"..i]
|
||||||
|
if title_and_date == nil or #title_and_date == 0 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local splitted = title_and_date:split("/")
|
||||||
|
|
||||||
|
local title = trim(splitted[1])
|
||||||
|
if title == nil or #title == 0 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local date_str = trim(splitted[2])
|
||||||
|
if date_str == nil or #date_str == 0 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local arr = date_str:split("%.")
|
||||||
|
if arr == nil or #arr < 3 then
|
||||||
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
local arr = args[1]:split("%.")
|
|
||||||
local start_date = date(arr[3], arr[2], arr[1])
|
local start_date = date(arr[3], arr[2], arr[1])
|
||||||
|
|
||||||
local curr_date = date()
|
return title, start_date
|
||||||
local passed = date.diff(curr_date, start_date)
|
|
||||||
local passed_days = math.floor(passed:spandays())
|
|
||||||
local idx = get_milestone_idx(passed)
|
|
||||||
|
|
||||||
ui:show_progress_bar(passed_days.." days / "..milestones_formatted[idx],
|
|
||||||
passed_days, milestones[idx])
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_click()
|
function on_click()
|
||||||
settings:show_dialog()
|
prefs:show_dialog()
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_settings()
|
||||||
|
prefs:show_dialog()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- utils
|
-- utils
|
||||||
|
|
||||||
|
function trim(s)
|
||||||
|
if s == nil or #s == 0 then return s end
|
||||||
|
return s:match("^%s*(.-)%s*$")
|
||||||
|
end
|
||||||
|
|
||||||
|
function insert_between_elements(t, element)
|
||||||
|
local new_table = {}
|
||||||
|
for i = 1, #t do
|
||||||
|
table.insert(new_table, t[i])
|
||||||
|
if i < #t then
|
||||||
|
table.insert(new_table, element)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return new_table
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function get_milestone_idx(passed)
|
function get_milestone_idx(passed)
|
||||||
local days_passed = passed:spandays()
|
local days_passed = passed:spandays()
|
||||||
local idx = 1
|
local idx = 0
|
||||||
|
|
||||||
for k,v in ipairs(milestones) do
|
for k,v in ipairs(milestones) do
|
||||||
if days_passed > v then
|
if days_passed > v then
|
||||||
idx = idx + 1
|
idx = idx + 1
|
||||||
|
else
|
||||||
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
2754
community/country-search.lua
Normal file
2754
community/country-search.lua
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,105 +0,0 @@
|
|||||||
-- name = "Currencies"
|
|
||||||
-- description = "Currency rates widget. Click on the date to change it."
|
|
||||||
-- data_source = "github.com/fawazahmed0/currency-api#readme"
|
|
||||||
-- type = "widget"
|
|
||||||
-- author = "Andrey Gavrilov"
|
|
||||||
-- version = "1.0"
|
|
||||||
-- arguments_help = "Enter the list of currency pairs in the format usd:rub btc:usd"
|
|
||||||
-- arguments_default = "usd:rub eur:rub"
|
|
||||||
|
|
||||||
json = require "json"
|
|
||||||
|
|
||||||
-- constants
|
|
||||||
local red_color = "#f44336"
|
|
||||||
local green_color = "#48ad47"
|
|
||||||
local text_color = ui:get_colors().secondary_text
|
|
||||||
local equals = "<font color=\""..text_color.."\"> = </font>"
|
|
||||||
|
|
||||||
-- global vars
|
|
||||||
local result_curr = ""
|
|
||||||
local tabl = {}
|
|
||||||
|
|
||||||
function on_resume()
|
|
||||||
ui:set_folding_flag(true)
|
|
||||||
ui:show_lines(tabl)
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_alarm()
|
|
||||||
get_rates("latest", "curr")
|
|
||||||
end
|
|
||||||
|
|
||||||
function get_rates(loc_date,id)
|
|
||||||
http:get("https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/"..loc_date.."/currencies/usd.json",id)
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_network_result_curr(result)
|
|
||||||
result_curr = result
|
|
||||||
|
|
||||||
local t = json.decode(result)
|
|
||||||
local dat = t.date
|
|
||||||
local prev_date = prev_date(dat)
|
|
||||||
|
|
||||||
get_rates(prev_date, "prev")
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_network_result_prev(result)
|
|
||||||
tabl = create_tab(result)
|
|
||||||
ui:show_lines(tabl)
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_click(idx)
|
|
||||||
ui:show_edit_dialog("Enter the date", "Enter the date in the format 2020.12.31. A blank value is the current date.")
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_dialog_action(dat)
|
|
||||||
if dat == "" then dat = "latest" end
|
|
||||||
get_rates(dat:gsub(".", "-"), "curr")
|
|
||||||
end
|
|
||||||
|
|
||||||
function prev_date(dat)
|
|
||||||
local prev_date = dat:split("-")
|
|
||||||
local prev_time = os.time{year=prev_date[1], month=prev_date[2], day=prev_date[3]} - (60*60*24)
|
|
||||||
return os.date("%Y-%m-%d", prev_time)
|
|
||||||
end
|
|
||||||
|
|
||||||
function create_tab(result)
|
|
||||||
local curs = settings:get()
|
|
||||||
local tab = {}
|
|
||||||
local t_c = json.decode(result_curr)
|
|
||||||
local t_p = json.decode(result)
|
|
||||||
|
|
||||||
-- set title
|
|
||||||
local dat = t_c.date
|
|
||||||
ui:set_title(ui:get_default_title().." "..dat:gsub("-", "."))
|
|
||||||
|
|
||||||
for idx = 1, #curs, 1 do
|
|
||||||
local cur = curs[idx]:split(":")
|
|
||||||
|
|
||||||
local rate_curr1 = t_c.usd[cur[1]]
|
|
||||||
local rate_curr2 = t_c.usd[cur[2]]
|
|
||||||
local rate_prev1 = t_p.usd[cur[1]]
|
|
||||||
local rate_prev2 = t_p.usd[cur[2]]
|
|
||||||
|
|
||||||
local rate_curr = round(rate_curr2/rate_curr1, 4)
|
|
||||||
local rate_prev = round(rate_prev2/rate_prev1, 4)
|
|
||||||
local change = round((rate_curr-rate_prev)/rate_prev*100,2)
|
|
||||||
|
|
||||||
local line = "1 "..string.upper(cur[1])..equals..rate_curr.." "..string.upper(cur[2])
|
|
||||||
line = line..get_formatted_change_text(change)
|
|
||||||
|
|
||||||
table.insert(tab, line)
|
|
||||||
end
|
|
||||||
return tab
|
|
||||||
end
|
|
||||||
|
|
||||||
-- utils --
|
|
||||||
|
|
||||||
function get_formatted_change_text(change)
|
|
||||||
if change > 0 then
|
|
||||||
return "<font color=\""..green_color.."\"><small> +"..change.."%</small></font>"
|
|
||||||
elseif change < 0 then
|
|
||||||
return "<font color=\""..red_color.."\"><small> "..change.."%</small></font>"
|
|
||||||
else
|
|
||||||
return "<font color=\""..text_color.."\"><small> "..change.."%</small></font>"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -47,7 +47,7 @@ function on_click()
|
|||||||
http:get("https://www.cbr.ru/scripts/XML_daily.asp?date_req="..dat:replace("%.","/"))
|
http:get("https://www.cbr.ru/scripts/XML_daily.asp?date_req="..dat:replace("%.","/"))
|
||||||
return false
|
return false
|
||||||
else
|
else
|
||||||
system:copy_to_clipboard(val)
|
system:to_clipboard(val)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -61,10 +61,10 @@ function on_network_result(res)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
search:show({"Нет данных по валюте "..cur},{red})
|
search:show_buttons({"Нет данных по валюте "..cur},{red})
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_long_click()
|
function on_long_click()
|
||||||
system:copy_to_clipboard(val)
|
system:to_clipboard(val)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
-- name = "Exchange rates"
|
|
||||||
-- description = "Shows currency rate by code"
|
|
||||||
-- data_source = "https://api.exchangerate.host"
|
|
||||||
-- type = "search"
|
|
||||||
-- author = "Evgeny Zobbin & Andrey Gavrilov"
|
|
||||||
-- version = "1.0"
|
|
||||||
|
|
||||||
-- modules
|
|
||||||
local json = require "json"
|
|
||||||
local md_color = require "md_colors"
|
|
||||||
|
|
||||||
-- constants
|
|
||||||
local host = "https://api.exchangerate.host"
|
|
||||||
local red = md_colors.red_500
|
|
||||||
local base_currency = system:get_currency()
|
|
||||||
|
|
||||||
-- variables
|
|
||||||
local req_currency = ""
|
|
||||||
local req_amount = 1
|
|
||||||
local result = 0
|
|
||||||
|
|
||||||
function on_search(inp)
|
|
||||||
req_currency = ""
|
|
||||||
req_amount = 1
|
|
||||||
result = 0
|
|
||||||
|
|
||||||
local a,c = inp:match("^(%d*)%s?(%a%a%a)$")
|
|
||||||
if c == nil then return end
|
|
||||||
|
|
||||||
req_currency = c:upper()
|
|
||||||
|
|
||||||
if get_index(supported, req_currency) == 0 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if a ~= "" then
|
|
||||||
req_amount = a
|
|
||||||
end
|
|
||||||
|
|
||||||
search:show({"Exchange rate for "..req_amount.." "..req_currency},{red})
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_click()
|
|
||||||
if result == 0 then
|
|
||||||
http:get(host.."/convert?from="..req_currency.."&to="..base_currency.."&amount="..req_amount)
|
|
||||||
return false
|
|
||||||
else
|
|
||||||
system:copy_to_clipboard(result)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_long_click()
|
|
||||||
system:copy_to_clipboard(result)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_network_result(res)
|
|
||||||
local tab = json.decode(res)
|
|
||||||
|
|
||||||
if tab.success then
|
|
||||||
search:show({tab.query.amount.." "..tab.query.from.." = "..tab.result.." "..tab.query.to}, {red})
|
|
||||||
else
|
|
||||||
search:show({"No data"},{red})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- curl -s -XGET 'https://api.exchangerate.host/symbols?format=csv' | cut -d ',' -f 2 | grep -v code | sed 's/$/,/' | tr '\n' ' '
|
|
||||||
supported = {
|
|
||||||
"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", "BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BRL", "BSD", "BTC", "BTN", "BWP", "BYN", "BZD", "CAD", "CDF", "CHF", "CLF", "CLP", "CNH", "CNY", "COP", "CRC", "CUC", "CUP", "CVE", "CZK", "DJF", "DKK", "DOP", "DZD", "EGP", "ERN", "ETB", "EUR", "FJD", "FKP", "GBP", "GEL", "GGP", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD", "HKD", "HNL", "HRK", "HTG", "HUF", "IDR", "ILS", "IMP", "INR", "IQD", "IRR", "ISK", "JEP", "JMD", "JOD", "JPY", "KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LYD", "MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MRU", "MUR", "MVR", "MWK", "MXN", "MYR", "MZN", "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", "OMR", "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG", "QAR", "RON", "RSD", "RUB", "RWF", "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SRD", "SSP", "STD", "STN", "SVC", "SYP", "SZL", "THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS", "UAH", "UGX", "USD", "UYU", "UZS", "VEF", "VES", "VND", "VUV", "WST", "XAF", "XAG", "XAU", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "YER", "ZAR", "ZMW", "ZWL",
|
|
||||||
}
|
|
||||||
@@ -9,12 +9,12 @@ function on_resume()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function on_network_result(result)
|
function on_network_result(result)
|
||||||
joke = ajson:get_value(result, "object array:attachments object:0 string:text")
|
joke = ajson:read(result, "object array:attachments object:0 string:text")
|
||||||
ui:show_text(joke)
|
ui:show_text(joke)
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_click()
|
function on_click()
|
||||||
if joke ~= nil then
|
if joke ~= nil then
|
||||||
system:copy_to_clipboard(joke)
|
system:to_clipboard(joke)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
189
community/electricity-price-widget.lua
Normal file
189
community/electricity-price-widget.lua
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
-- name = "Electricity spot price"
|
||||||
|
-- description = "Day-ahead spot price of electricity"
|
||||||
|
-- data_source = "https://api.energy-charts.info/"
|
||||||
|
-- type = "widget"
|
||||||
|
-- foldable = "true"
|
||||||
|
-- author = "Hannu Hartikainen <hannu.hartikainen@gmail.com>"
|
||||||
|
-- version = "1.1"
|
||||||
|
|
||||||
|
json = require "json"
|
||||||
|
prefs = require "prefs"
|
||||||
|
|
||||||
|
url_base = "https://api.energy-charts.info/price?bzn=%s&end=%d"
|
||||||
|
|
||||||
|
price_data = nil
|
||||||
|
price_interval = nil
|
||||||
|
price_unit = nil
|
||||||
|
unit_multiplier = 1
|
||||||
|
|
||||||
|
next_fetch_ts = 0
|
||||||
|
next_redraw_ts = nil
|
||||||
|
|
||||||
|
-- parameters and default values
|
||||||
|
function on_load()
|
||||||
|
-- bidding zone: see "Available bidding zones" in https://api.energy-charts.info/
|
||||||
|
if not prefs.bidding_zone then
|
||||||
|
prefs.bidding_zone = "FI"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- VAT percentage
|
||||||
|
if not prefs.vat_percentage then
|
||||||
|
prefs.vat_percentage = 25.5
|
||||||
|
end
|
||||||
|
|
||||||
|
-- change threshold percentages for showing changes in folded format
|
||||||
|
if not prefs.oneline_threshold_high then
|
||||||
|
prefs.oneline_threshold_high = 1.3
|
||||||
|
end
|
||||||
|
if not prefs.oneline_threshold_low then
|
||||||
|
prefs.oneline_threshold_low = 0.7
|
||||||
|
end
|
||||||
|
|
||||||
|
-- url to open when clicked
|
||||||
|
if not prefs.click_url then
|
||||||
|
prefs.click_url = "https://www.sahkonhintatanaan.fi/"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_url()
|
||||||
|
-- at most about 35 hours are known in advance; fetch all known prices
|
||||||
|
local end_offset = 2*24*60*60
|
||||||
|
local end_ts = os.time() + end_offset
|
||||||
|
return string.format(url_base, prefs.bidding_zone, end_ts)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_alarm()
|
||||||
|
if os.time() > next_fetch_ts then
|
||||||
|
http:get(get_url())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_network_result(result, code)
|
||||||
|
if code >= 200 and code < 299 then
|
||||||
|
parse_result(result)
|
||||||
|
draw_widget(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_tick(t)
|
||||||
|
local ts = os.time()
|
||||||
|
if next_redraw_ts and ts > next_redraw_ts then
|
||||||
|
draw_widget(true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click()
|
||||||
|
if not ui:folding_flag() then
|
||||||
|
system:open_browser(prefs.click_url)
|
||||||
|
else
|
||||||
|
draw_widget(false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function parse_result(result)
|
||||||
|
price_data = json.decode(result)
|
||||||
|
local price_count = #price_data.unix_seconds
|
||||||
|
if price_count < 2 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
price_interval = price_data.unix_seconds[2] - price_data.unix_seconds[1]
|
||||||
|
if price_data.unit == "EUR/MWh" or price_data.unit == "EUR / megawatt_hour" then
|
||||||
|
price_unit = "c/kWh"
|
||||||
|
unit_multiplier = 0.1
|
||||||
|
else
|
||||||
|
price_unit = price_data.unit
|
||||||
|
unit_multiplier = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- assume next day is known 8 hours before it starts
|
||||||
|
-- (eg. Nord Pool Spot typically publishes dayahead prices at 14 local time)
|
||||||
|
local next_fetch_offset = 8*60*60
|
||||||
|
local end_of_data_ts = price_data.unix_seconds[price_count] + price_interval
|
||||||
|
next_fetch_ts = end_of_data_ts - next_fetch_offset
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_current_idx()
|
||||||
|
local t = os.time()
|
||||||
|
for i = 1, #price_data.unix_seconds do
|
||||||
|
local ts = price_data.unix_seconds[i]
|
||||||
|
if ts < t and t < (ts+price_interval) then
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function price(i)
|
||||||
|
return price_data.price[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_display_price(idx)
|
||||||
|
local mul = (1.0 + (prefs.vat_percentage / 100.0)) * unit_multiplier
|
||||||
|
-- NOTE: float rounding in string.format doesn't work so do it here
|
||||||
|
return math.floor(100.0 * price(idx) * mul + 0.5) / 100.0
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_display_time(idx)
|
||||||
|
return os.date("%H", price_data.unix_seconds[idx])
|
||||||
|
end
|
||||||
|
|
||||||
|
function format_price(idx)
|
||||||
|
return string.format("%0.2f", get_display_price(idx))
|
||||||
|
end
|
||||||
|
|
||||||
|
function format_price_and_unit(idx)
|
||||||
|
return string.format("%s %s", format_price(idx), price_unit)
|
||||||
|
end
|
||||||
|
|
||||||
|
function format_oneline(idx)
|
||||||
|
local more_prices = ""
|
||||||
|
local more_count = 0
|
||||||
|
local cur_price = price(idx)
|
||||||
|
for i = idx+1, #price_data.price do
|
||||||
|
if price(i) > prefs.oneline_threshold_high * cur_price
|
||||||
|
or price(i) < prefs.oneline_threshold_low * cur_price then
|
||||||
|
more_count = more_count + 1
|
||||||
|
more_prices = more_prices .. string.format("⋄ <i>%s′</i> <b>%s</b> ", get_display_time(i), get_display_price(i))
|
||||||
|
cur_price = price(i)
|
||||||
|
if more_count > 3 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return string.format("<b>%s</b> %s", format_price_and_unit(idx), more_prices)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- NOTE: using timestamps would be better than indices, but the chart element
|
||||||
|
-- doesn't support times spanning multiple days properly
|
||||||
|
function make_chart_data(idx)
|
||||||
|
local chart = {}
|
||||||
|
for i = idx, #price_data.price do
|
||||||
|
table.insert(chart, {
|
||||||
|
i-1,
|
||||||
|
get_display_price(i)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
return chart
|
||||||
|
end
|
||||||
|
|
||||||
|
function draw_widget(fold)
|
||||||
|
ui:set_folding_flag(fold)
|
||||||
|
local idx = get_current_idx()
|
||||||
|
if not idx then
|
||||||
|
ui:show_text("Error: no current price data")
|
||||||
|
-- request fetch on next on_alarm and don't redraw before that
|
||||||
|
next_fetch_ts = 0
|
||||||
|
next_redraw_ts = nil
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
next_redraw_ts = price_data.unix_seconds[idx+1]
|
||||||
|
ui:set_title(string.format("Electricity spot price: %s", format_price_and_unit(idx)))
|
||||||
|
ui:show_chart(make_chart_data(idx), "x: int, y: float", "", true, format_oneline(idx))
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_settings()
|
||||||
|
if (prefs.show_dialog) then
|
||||||
|
prefs:show_dialog()
|
||||||
|
end
|
||||||
|
end
|
||||||
6783
community/fennel-search.lua
Normal file
6783
community/fennel-search.lua
Normal file
File diff suppressed because one or more lines are too long
@@ -2,7 +2,7 @@
|
|||||||
-- description = "Game"
|
-- description = "Game"
|
||||||
-- type = "widget"
|
-- type = "widget"
|
||||||
-- author = "Andrey Gavrilov"
|
-- author = "Andrey Gavrilov"
|
||||||
-- version = "1.0"
|
-- version = "1.1"
|
||||||
|
|
||||||
local json = require "json"
|
local json = require "json"
|
||||||
local folded = "15 puzzle"
|
local folded = "15 puzzle"
|
||||||
@@ -96,7 +96,7 @@ end
|
|||||||
|
|
||||||
function on_click(idx)
|
function on_click(idx)
|
||||||
if idx == 0 then
|
if idx == 0 then
|
||||||
ui:show_dialog("Select Action","","Cancel","Reload")
|
dialogs:show_dialog("Select Action","","Cancel","Reload")
|
||||||
return
|
return
|
||||||
else
|
else
|
||||||
local tab = tabs_to_tab(tabs_to_desk(tab_to_tabs(json.decode(files:read("fifteen")))))
|
local tab = tabs_to_tab(tabs_to_desk(tab_to_tabs(json.decode(files:read("fifteen")))))
|
||||||
|
|||||||
69
community/google-tasks-app-widget.lua
Normal file
69
community/google-tasks-app-widget.lua
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
-- name = "Google Tasks"
|
||||||
|
-- description = "AIO wrapper for the official Google Tasks app widget"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
||||||
|
-- aio_version = "4.1.99"
|
||||||
|
-- uses_app = "com.google.android.apps.tasks"
|
||||||
|
|
||||||
|
local prefs = require "prefs"
|
||||||
|
local fmt = require "fmt"
|
||||||
|
|
||||||
|
local max_mails = 1
|
||||||
|
local curr_tab = {}
|
||||||
|
local w_bridge = nil
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
if not widgets:bound(prefs.wid) then
|
||||||
|
setup_app_widget()
|
||||||
|
end
|
||||||
|
|
||||||
|
widgets:request_updates(prefs.wid)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_app_widget_updated(bridge)
|
||||||
|
local tab = bridge:dump_strings().values
|
||||||
|
|
||||||
|
-- Bold list name
|
||||||
|
tab[1] = fmt.bold(tab[1])
|
||||||
|
|
||||||
|
-- Remove "A fresh start"
|
||||||
|
table.remove(tab, #tab-1)
|
||||||
|
|
||||||
|
curr_tab = deep_copy(tab)
|
||||||
|
w_bridge = bridge
|
||||||
|
|
||||||
|
-- Change "Anything to add?" string to "Add task"
|
||||||
|
tab[#tab] = fmt.secondary("Add task")
|
||||||
|
|
||||||
|
ui:show_lines(tab)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
w_bridge:click(curr_tab[idx])
|
||||||
|
end
|
||||||
|
|
||||||
|
function setup_app_widget()
|
||||||
|
local id = widgets:setup("com.google.android.apps.tasks/com.google.android.apps.tasks.features.widgetlarge.ListWidgetProvider")
|
||||||
|
if (id ~= nil) then
|
||||||
|
prefs.wid = id
|
||||||
|
else
|
||||||
|
ui:show_text("Can't add widget")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function deep_copy(orig)
|
||||||
|
local orig_type = type(orig)
|
||||||
|
local copy
|
||||||
|
if orig_type == 'table' then
|
||||||
|
copy = {}
|
||||||
|
for orig_key, orig_value in next, orig, nil do
|
||||||
|
copy[deep_copy(orig_key)] = deep_copy(orig_value)
|
||||||
|
end
|
||||||
|
setmetatable(copy, deep_copy(getmetatable(orig)))
|
||||||
|
else
|
||||||
|
copy = orig
|
||||||
|
end
|
||||||
|
return copy
|
||||||
|
end
|
||||||
|
|
||||||
@@ -21,29 +21,29 @@ function on_search(input)
|
|||||||
text_from = input
|
text_from = input
|
||||||
text_to = ""
|
text_to = ""
|
||||||
|
|
||||||
search:show_top({"Translate \""..input.."\""}, {blue})
|
search:show_buttons({"Translate \""..input.."\""}, {blue}, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_click()
|
function on_click()
|
||||||
if text_to == "" then
|
if text_to == "" then
|
||||||
search:show_top({"Translating..."}, {blue})
|
search:show_buttons({"Translating..."}, {blue}, true)
|
||||||
request_trans(text_from)
|
request_trans(text_from)
|
||||||
return false
|
return false
|
||||||
else
|
else
|
||||||
system:copy_to_clipboard(text_to)
|
system:to_clipboard(text_to)
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function request_trans(str)
|
function request_trans(str)
|
||||||
http:get(uri.."&tl="..system:get_lang().."&dt=t&q="..str)
|
http:get(uri.."&tl="..system:lang().."&dt=t&q="..str)
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_network_result(result, code)
|
function on_network_result(result, code)
|
||||||
if code >= 200 and code < 300 then
|
if code >= 200 and code < 300 then
|
||||||
decode_and_show(result)
|
decode_and_show(result)
|
||||||
else
|
else
|
||||||
search:show_top({"Server error"}, {red})
|
search:show_buttons({"Server error"}, {red}, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ function decode_and_show(result)
|
|||||||
--local lang_from = t[3]
|
--local lang_from = t[3]
|
||||||
|
|
||||||
if text_to ~= "" then
|
if text_to ~= "" then
|
||||||
search:show_top({text_to}, {blue})
|
search:show_buttons({text_to}, {blue}, true)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
-- data_source = "https://translate.google.com"
|
-- data_source = "https://translate.google.com"
|
||||||
-- type = "widget"
|
-- type = "widget"
|
||||||
-- author = "Andrey Gavrilov"
|
-- author = "Andrey Gavrilov"
|
||||||
-- version = "1.1"
|
-- version = "1.3"
|
||||||
|
|
||||||
local json = require "json"
|
local json = require "json"
|
||||||
local uri = "http://translate.googleapis.com/translate_a/single?client=gtx&sl=auto"
|
local uri = "http://translate.googleapis.com/translate_a/single?client=gtx&sl=auto"
|
||||||
@@ -13,12 +13,12 @@ function on_resume()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function on_click()
|
function on_click()
|
||||||
ui:show_edit_dialog("Enter text")
|
dialogs:show_edit_dialog("Enter text")
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_dialog_action(text)
|
function on_dialog_action(text)
|
||||||
if text == "" or text == -1 then
|
if text == "" or text == -1 then
|
||||||
on_alarm()
|
on_resume()
|
||||||
else
|
else
|
||||||
text_from = text
|
text_from = text
|
||||||
translate(text)
|
translate(text)
|
||||||
@@ -26,7 +26,7 @@ function on_dialog_action(text)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function translate(str)
|
function translate(str)
|
||||||
http:get(uri.."&tl="..system:get_lang().."&dt=t&q="..str)
|
http:get(uri.."&tl="..system:lang().."&dt=t&q="..str)
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_network_result(result)
|
function on_network_result(result)
|
||||||
|
|||||||
217
community/habits-widget.lua
Normal file
217
community/habits-widget.lua
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
-- name = "Habit tracker"
|
||||||
|
-- description = "Daily habit tracker with JSON storage"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Sergey Mironov"
|
||||||
|
-- version = "1.0"
|
||||||
|
|
||||||
|
local json = require("json")
|
||||||
|
local md = require("md_colors")
|
||||||
|
|
||||||
|
local buttons = {}
|
||||||
|
local filename = "button_data.json"
|
||||||
|
local dialog_state = nil -- Track current dialog state
|
||||||
|
|
||||||
|
-- Get current date in YYYY-MM-DD format
|
||||||
|
local function get_current_date()
|
||||||
|
return os.date("%Y-%m-%d")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Load button data from JSON file
|
||||||
|
local function load_data()
|
||||||
|
local content = files:read(filename)
|
||||||
|
if content then
|
||||||
|
local success, data = pcall(json.decode, content)
|
||||||
|
if success and data then
|
||||||
|
buttons = data
|
||||||
|
else
|
||||||
|
buttons = {}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
buttons = {}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Save button data to JSON file
|
||||||
|
local function save_data()
|
||||||
|
local content = json.encode(buttons)
|
||||||
|
files:write(filename, content)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Display all buttons
|
||||||
|
local function display_buttons()
|
||||||
|
local names = {}
|
||||||
|
local colors = {}
|
||||||
|
local current_date = get_current_date()
|
||||||
|
|
||||||
|
-- Add the "+" button at the beginning
|
||||||
|
table.insert(names, "fa:plus")
|
||||||
|
table.insert(colors, aio:colors().button) -- Gray color for add button
|
||||||
|
|
||||||
|
-- Add existing buttons
|
||||||
|
for i, button in ipairs(buttons) do
|
||||||
|
table.insert(names, button.title)
|
||||||
|
|
||||||
|
-- Check if button was clicked today
|
||||||
|
if button.last_clicked == current_date then
|
||||||
|
table.insert(colors, md.green_600) -- Green if clicked today
|
||||||
|
else
|
||||||
|
table.insert(colors, button.color) -- Original color
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
ui:show_buttons(names, colors)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle button clicks
|
||||||
|
function on_click(index)
|
||||||
|
local current_date = get_current_date()
|
||||||
|
|
||||||
|
-- Check if it's the "+" button (first button)
|
||||||
|
if index == 1 then
|
||||||
|
-- Show add dialog - ask for title first
|
||||||
|
dialog_state = {action = "add_title"}
|
||||||
|
dialogs:show_edit_dialog("Add New Button", "Enter button title:", "")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle existing button click (adjust index for "+" button)
|
||||||
|
local button_index = index - 1
|
||||||
|
local button = buttons[button_index]
|
||||||
|
if button.last_clicked == current_date then
|
||||||
|
-- Already clicked today, reset
|
||||||
|
button.last_clicked = ""
|
||||||
|
ui:show_toast("Unmarked: " .. button.title)
|
||||||
|
else
|
||||||
|
-- Not clicked today, mark as clicked
|
||||||
|
button.last_clicked = current_date
|
||||||
|
ui:show_toast("Marked: " .. button.title)
|
||||||
|
end
|
||||||
|
|
||||||
|
save_data()
|
||||||
|
display_buttons()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle long clicks for editing
|
||||||
|
function on_long_click(index)
|
||||||
|
-- Don't allow long-click on "+" button (first button)
|
||||||
|
if index == 1 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Show edit options (adjust index for "+" button)
|
||||||
|
local button_index = index - 1
|
||||||
|
dialog_state = {action = "edit_options", index = button_index}
|
||||||
|
local button = buttons[button_index]
|
||||||
|
dialogs:show_dialog("Edit Button: " .. button.title, "Choose action:", "Edit", "Delete")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Handle dialog results
|
||||||
|
function on_dialog_action(value)
|
||||||
|
if value == -1 then
|
||||||
|
-- Cancel pressed
|
||||||
|
dialog_state = nil
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not dialog_state then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if dialog_state.action == "add_title" then
|
||||||
|
if type(value) == "string" and value:trim() ~= "" then
|
||||||
|
-- Got title, now ask for color
|
||||||
|
dialog_state = {action = "add_color", title = value}
|
||||||
|
dialogs:show_edit_dialog("Add New Button\nTitle: " .. value, "Enter color (e.g., #FF0000):", "#FF0000")
|
||||||
|
else
|
||||||
|
ui:show_toast("Invalid title")
|
||||||
|
dialog_state = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif dialog_state.action == "add_color" then
|
||||||
|
if type(value) == "string" and value:match("^#%x%x%x%x%x%x$") then
|
||||||
|
-- Valid color, create button
|
||||||
|
local new_button = {
|
||||||
|
title = dialog_state.title,
|
||||||
|
color = value,
|
||||||
|
last_clicked = ""
|
||||||
|
}
|
||||||
|
table.insert(buttons, new_button)
|
||||||
|
save_data()
|
||||||
|
display_buttons()
|
||||||
|
ui:show_toast("Added: " .. dialog_state.title)
|
||||||
|
else
|
||||||
|
ui:show_toast("Invalid color format. Use #RRGGBB")
|
||||||
|
end
|
||||||
|
dialog_state = nil
|
||||||
|
|
||||||
|
elseif dialog_state.action == "edit_options" then
|
||||||
|
if value == 1 then
|
||||||
|
-- Edit button - ask for new title
|
||||||
|
local button = buttons[dialog_state.index]
|
||||||
|
dialog_state = {action = "edit_title", index = dialog_state.index}
|
||||||
|
dialogs:show_edit_dialog("Edit Button", "Enter new title:", button.title)
|
||||||
|
elseif value == 2 then
|
||||||
|
-- Delete button
|
||||||
|
local button = buttons[dialog_state.index]
|
||||||
|
table.remove(buttons, dialog_state.index)
|
||||||
|
save_data()
|
||||||
|
display_buttons()
|
||||||
|
ui:show_toast("Deleted: " .. button.title)
|
||||||
|
dialog_state = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif dialog_state.action == "edit_title" then
|
||||||
|
if type(value) == "string" and value:trim() ~= "" then
|
||||||
|
-- Got new title, now ask for color
|
||||||
|
local button = buttons[dialog_state.index]
|
||||||
|
dialog_state = {action = "edit_color", index = dialog_state.index, title = value}
|
||||||
|
dialogs:show_edit_dialog("Edit Button\nTitle: " .. value, "Enter new color:", button.color)
|
||||||
|
else
|
||||||
|
ui:show_toast("Invalid title")
|
||||||
|
dialog_state = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif dialog_state.action == "edit_color" then
|
||||||
|
if type(value) == "string" and value:match("^#%x%x%x%x%x%x$") then
|
||||||
|
-- Valid color, update button
|
||||||
|
local button = buttons[dialog_state.index]
|
||||||
|
button.title = dialog_state.title
|
||||||
|
button.color = value
|
||||||
|
save_data()
|
||||||
|
display_buttons()
|
||||||
|
ui:show_toast("Updated: " .. button.title)
|
||||||
|
else
|
||||||
|
ui:show_toast("Invalid color format. Use #RRGGBB")
|
||||||
|
end
|
||||||
|
dialog_state = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Initialize widget
|
||||||
|
function on_resume()
|
||||||
|
load_data()
|
||||||
|
|
||||||
|
-- Add some sample data if empty
|
||||||
|
if #buttons == 0 then
|
||||||
|
buttons = {
|
||||||
|
{
|
||||||
|
title = "Exercise",
|
||||||
|
color = md.pink_600,
|
||||||
|
last_clicked = ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title = "Read",
|
||||||
|
color = md.cyan_600,
|
||||||
|
last_clicked = ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title = "Water",
|
||||||
|
color = md.light_blue_600,
|
||||||
|
last_clicked = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
save_data()
|
||||||
|
end
|
||||||
|
|
||||||
|
display_buttons()
|
||||||
|
end
|
||||||
@@ -28,6 +28,15 @@ local buttons_cmds = { prev_cmd, backward_cmd, play_cmd, stop_cmd, forward_cmd,
|
|||||||
local url = nil
|
local url = nil
|
||||||
local curr_idx = nil
|
local curr_idx = nil
|
||||||
|
|
||||||
|
function on_preview()
|
||||||
|
if next(settings:get()) == nil then
|
||||||
|
ui:show_text("Remote control for Kodi multimedia player")
|
||||||
|
return
|
||||||
|
else
|
||||||
|
on_resume()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function on_resume()
|
function on_resume()
|
||||||
if next(settings:get()) == nil then
|
if next(settings:get()) == nil then
|
||||||
ui:show_text("Tap to enter Kodi address")
|
ui:show_text("Tap to enter Kodi address")
|
||||||
@@ -84,6 +93,10 @@ function on_network_result_cmd(result)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function on_settings()
|
||||||
|
settings:show_dialog()
|
||||||
|
end
|
||||||
|
|
||||||
-- utils
|
-- utils
|
||||||
|
|
||||||
function init_url_from_settings()
|
function init_url_from_settings()
|
||||||
40
community/monitor-lite-widget.lua
Normal file
40
community/monitor-lite-widget.lua
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
-- name = "Monitor"
|
||||||
|
-- description = "One line monitor widget"
|
||||||
|
-- type = "widget"
|
||||||
|
-- foldable = "false"
|
||||||
|
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
||||||
|
-- version = "1.0"
|
||||||
|
|
||||||
|
local fmt = require "fmt"
|
||||||
|
local good_color = aio:colors().progress_good
|
||||||
|
local bad_color = aio:colors().progress_bad
|
||||||
|
|
||||||
|
function on_tick(n)
|
||||||
|
-- Update every ten seconds
|
||||||
|
if n % 10 == 0 then
|
||||||
|
update()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function update()
|
||||||
|
local batt_percent = system:battery_info().percent
|
||||||
|
local is_charging = system:battery_info().charging
|
||||||
|
local mem_total = system:system_info().mem_total
|
||||||
|
local mem_available = system:system_info().mem_available
|
||||||
|
local storage_total = system:system_info().storage_total
|
||||||
|
local storage_available = system:system_info().storage_available
|
||||||
|
|
||||||
|
if (is_charging) then
|
||||||
|
batt_percent = fmt.colored(batt_percent.."%", good_color)
|
||||||
|
elseif (batt_percent <= 15) then
|
||||||
|
batt_percent = fmt.colored(batt_percent.."%", bad_color)
|
||||||
|
else
|
||||||
|
batt_percent = batt_percent.."%"
|
||||||
|
end
|
||||||
|
|
||||||
|
ui:show_text(
|
||||||
|
"BATT: "..batt_percent..fmt.space(4)..
|
||||||
|
"RAM: "..mem_available..fmt.space(4)..
|
||||||
|
"NAND: "..storage_available
|
||||||
|
)
|
||||||
|
end
|
||||||
@@ -18,7 +18,7 @@ local blue = md_colors.light_blue_800
|
|||||||
function on_search(input)
|
function on_search(input)
|
||||||
text_from = input
|
text_from = input
|
||||||
text_to = ""
|
text_to = ""
|
||||||
search:show({input},{blue})
|
search:show_buttons({input},{blue})
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_click(idx)
|
function on_click(idx)
|
||||||
|
|||||||
@@ -14,9 +14,14 @@ function on_resume()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function on_click()
|
function on_click()
|
||||||
aio:show_args_dialog()
|
settings:show_dialog()
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_network_result(result)
|
function on_network_result(result)
|
||||||
ui:show_text(result)
|
ui:show_text(result)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function on_settings()
|
||||||
|
settings:show_dialog()
|
||||||
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ local pass = ""
|
|||||||
function on_search(str)
|
function on_search(str)
|
||||||
if str:lower():find(string.lower("password")) then
|
if str:lower():find(string.lower("password")) then
|
||||||
pass = gen_pass()
|
pass = gen_pass()
|
||||||
search:show{pass}
|
search:show_buttons{pass}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -26,5 +26,5 @@ function gen_pass()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function on_click()
|
function on_click()
|
||||||
system:copy_to_clipboard(pass)
|
system:to_clipboard(pass)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -31,3 +31,8 @@ function init_progressbar()
|
|||||||
percent = math.floor((current_time - start_period) / ((end_period - start_period) / 100))
|
percent = math.floor((current_time - start_period) / ((end_period - start_period) / 100))
|
||||||
ui:show_progress_bar(name_period..": "..percent.."%", current_time - start_period, end_period - start_period, "#7069f0ae")
|
ui:show_progress_bar(name_period..": "..percent.."%", current_time - start_period, end_period - start_period, "#7069f0ae")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function on_settings()
|
||||||
|
settings:show_dialog()
|
||||||
|
end
|
||||||
|
|
||||||
|
|||||||
16
community/profiles-dumper-widget.lua
Normal file
16
community/profiles-dumper-widget.lua
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
-- name = "Profiles auto dumper"
|
||||||
|
-- description = "Hidden widget that auto dump profile on every return to the home screen"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
||||||
|
-- version = "1.0"
|
||||||
|
-- foldable = "false"
|
||||||
|
|
||||||
|
local prof_name = "Auto dumped"
|
||||||
|
|
||||||
|
function on_load()
|
||||||
|
ui:hide_widget()
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
profiles:dump(prof_name)
|
||||||
|
end
|
||||||
35
community/profiles-restore-save-widget.lua
Normal file
35
community/profiles-restore-save-widget.lua
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
-- name = "Profile Switcher"
|
||||||
|
-- version = "1.1"
|
||||||
|
-- description = "Tap: restore profile / Long-press: save to profile"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Marcus Johansson"
|
||||||
|
|
||||||
|
local profs
|
||||||
|
local profile
|
||||||
|
|
||||||
|
function on_load()
|
||||||
|
profs = profiles:list()
|
||||||
|
ui:show_buttons(profs)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
profile = profs[idx]
|
||||||
|
profiles:restore(profile)
|
||||||
|
ui:show_toast(profile..": Restored")
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_long_click(idx)
|
||||||
|
profile = profs[idx]
|
||||||
|
title = 'Save to "'..profile..'" ?'
|
||||||
|
text = 'Do you want to save the current screen state to the "'..profile..'" profile ?'
|
||||||
|
dialogs:show_dialog(title, text, "Yes", "Cancel")
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_dialog_action(value)
|
||||||
|
if value == 1 then
|
||||||
|
profiles:dump(profile)
|
||||||
|
ui:show_toast(profile..": Saved")
|
||||||
|
elseif value == 2 then
|
||||||
|
ui:show_toast("Canceled")
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -17,7 +17,7 @@ function on_search(input)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function on_click()
|
function on_click()
|
||||||
system:copy_to_clipboard(ip)
|
system:to_clipboard(ip)
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_ip()
|
function get_ip()
|
||||||
@@ -27,9 +27,9 @@ end
|
|||||||
function on_network_result(result,code)
|
function on_network_result(result,code)
|
||||||
if code >= 200 and code < 300 then
|
if code >= 200 and code < 300 then
|
||||||
ip = result
|
ip = result
|
||||||
search:show({result},{blue})
|
search:show_buttons({result},{blue})
|
||||||
else
|
else
|
||||||
search:show({"Server Error"},{red})
|
search:show_buttons({"Server Error"},{red})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
143
community/quickactions-widget.lua
Normal file
143
community/quickactions-widget.lua
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
-- name = "Quick Actions"
|
||||||
|
-- type = "widget"
|
||||||
|
-- description = "Launcher selected actions widget"
|
||||||
|
-- arguments_help = "Long click button for options, open widget settings for list of buttons"
|
||||||
|
--foldable = "true"
|
||||||
|
-- author = "Theodor Galanis"
|
||||||
|
-- version = "2.6"
|
||||||
|
|
||||||
|
md_colors = require "md_colors"
|
||||||
|
|
||||||
|
local icons = { "fa:pen", "fa:edit", "fa:indent", "fa:bars", "fa:sliders-h", "fa:redo", "fa:power-off", "fa:bring-forward", "fa:eraser", "fa:tools", "fa:layer-minus", "fa:layer-group", "fa:user-shield", "fa:lock", "fa:chevron-down", "fa:chevron-up", "fa:notes-medical", "fa:circle-dot", "fa:envelope", "fa:square-full", "fa:microphone", "fa:hand", "fa:search"}
|
||||||
|
|
||||||
|
local names = {"Quick menu", "Quick apps menu", "Applications menu", "Toggle headers", "Settings", "Screen refresh", "Restart AIO launcher", "Notifications panel", "Clear notifications", "Quick settings", "Fold all widgets", "Unfold all widgets", "Private mode", "Screen off", "Scroll down", "Scroll up", "Add note", "Start audio recording", "Send mail", "Recent apps", "Voice command", "One-handed mode", "Search"}
|
||||||
|
|
||||||
|
local colors = { md_colors.purple_800, md_colors.purple_600, md_colors.amber_900, md_colors.orange_900, md_colors.blue_900, md_colors.deep_purple_800, md_colors.grey_600, md_colors.green_900, md_colors.green_900, md_colors.blue_800, md_colors.pink_300, md_colors.pink_A200, md_colors.green_600, md_colors.grey_800, md_colors.teal_700, md_colors.teal_800, md_colors.orange_700, md_colors.red_800, md_colors.red_900, md_colors.deep_purple_700, md_colors.blue_700, md_colors.amber_800, md_colors.blue_grey_700}
|
||||||
|
|
||||||
|
local actions = { "quick_menu", "quick_apps_menu", "apps_menu", "headers", "settings", "refresh", "restart", "notify", "clear_notifications", "quick_settings", "fold", "unfold", "private_mode", "screen_off", "scroll_down", "scroll_up", "add_note", "start_record", "send_mail", "show_recents", "voice", "one_handed", "search"}
|
||||||
|
|
||||||
|
local pos = 0
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
if next(settings:get()) == nil then
|
||||||
|
set_default_args()
|
||||||
|
end
|
||||||
|
local buttons,colors = get_buttons()
|
||||||
|
ui:show_buttons(buttons, colors)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
pos = idx
|
||||||
|
redraw()
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_long_click(idx)
|
||||||
|
pos = idx
|
||||||
|
local tab = settings:get()
|
||||||
|
label = get_label(actions[get_checkbox_idx()[idx]])
|
||||||
|
if label == nil then
|
||||||
|
label = names[get_checkbox_idx()[idx]]
|
||||||
|
end
|
||||||
|
ui:show_context_menu({{"angle-left",""},{"ban",""},{"angle-right",""},{icons[get_checkbox_idx()[idx]]:gsub("fa:",""),label}})
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_context_menu_click(menu_idx)
|
||||||
|
if menu_idx == 1 then
|
||||||
|
move(-1)
|
||||||
|
elseif menu_idx == 2 then
|
||||||
|
remove()
|
||||||
|
elseif menu_idx == 3 then
|
||||||
|
move(1)
|
||||||
|
elseif menu_idx == 4 then
|
||||||
|
redraw()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_dialog_action(data)
|
||||||
|
if data == -1 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
settings:set(data)
|
||||||
|
on_resume()
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_settings()
|
||||||
|
axions = aio:actions()
|
||||||
|
lab = {}
|
||||||
|
for i = 1, #axions do
|
||||||
|
lav = get_label(actions[i])
|
||||||
|
if lav == nil then
|
||||||
|
table.insert(lab,names[i])
|
||||||
|
else
|
||||||
|
table.insert(lab, lav)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
dialogs:show_checkbox_dialog("Select actions", lab, get_checkbox_idx())
|
||||||
|
end
|
||||||
|
|
||||||
|
--utilities--
|
||||||
|
|
||||||
|
function redraw()
|
||||||
|
local buttons,colors = get_buttons()
|
||||||
|
local checkbox_idx = get_checkbox_idx()
|
||||||
|
local action = actions[checkbox_idx[pos]]
|
||||||
|
aio:do_action(action)
|
||||||
|
ui:show_buttons(buttons, colors)
|
||||||
|
end
|
||||||
|
|
||||||
|
function set_default_args()
|
||||||
|
local args = {}
|
||||||
|
for i = 1, #actions do
|
||||||
|
table.insert(args, i)
|
||||||
|
end
|
||||||
|
settings:set(args)
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_checkbox_idx()
|
||||||
|
local tab = settings:get()
|
||||||
|
for i = 1, #tab do
|
||||||
|
tab[i] = tonumber(tab[i])
|
||||||
|
end
|
||||||
|
return tab
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_buttons()
|
||||||
|
local buttons,bcolors = {},{}
|
||||||
|
local checkbox_idx = get_checkbox_idx()
|
||||||
|
for i = 1, #checkbox_idx do
|
||||||
|
table.insert(buttons, icons[checkbox_idx[i]])
|
||||||
|
table.insert(bcolors, colors[checkbox_idx[i]])
|
||||||
|
end
|
||||||
|
return buttons,bcolors
|
||||||
|
end
|
||||||
|
|
||||||
|
function move(x)
|
||||||
|
local tab = settings:get()
|
||||||
|
if (pos == 1 and x < 0) or (pos == #tab and x > 0) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local cur = tab[pos]
|
||||||
|
local prev = tab[pos+x]
|
||||||
|
tab[pos+x] = cur
|
||||||
|
tab[pos] = prev
|
||||||
|
settings:set(tab)
|
||||||
|
local buttons,colors = get_buttons()
|
||||||
|
ui:show_buttons(buttons, colors)
|
||||||
|
end
|
||||||
|
|
||||||
|
function remove()
|
||||||
|
local tab = settings:get()
|
||||||
|
table.remove(tab,pos)
|
||||||
|
settings:set(tab)
|
||||||
|
local buttons,colors = get_buttons()
|
||||||
|
ui:show_buttons(buttons, colors)
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_label(name)
|
||||||
|
axions = aio:actions()
|
||||||
|
for _, action in ipairs(axions) do
|
||||||
|
if action["name"] == name then
|
||||||
|
return action["label"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -10,14 +10,14 @@ function on_alarm()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function on_network_result(result)
|
function on_network_result(result)
|
||||||
setup = ajson:get_value(result, "object string:setup")
|
setup = ajson:read(result, "object string:setup")
|
||||||
punchline = ajson:get_value(result, "object string:punchline")
|
punchline = ajson:read(result, "object string:punchline")
|
||||||
|
|
||||||
ui:show_lines({setup, punchline})
|
ui:show_lines({setup, punchline})
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_click()
|
function on_click()
|
||||||
if setup ~= nil then
|
if setup ~= nil then
|
||||||
system:copy_to_clipboard(setup.."\n"..punchline)
|
system:to_clipboard(setup.."\n"..punchline)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
120
community/recent-apps-widget.lua
Normal file
120
community/recent-apps-widget.lua
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
-- name = "Recent Apps"
|
||||||
|
-- description = "Gets the apps in the recent apps (app overview) screen. Tap to open the app or long press to remove it.\nREQUIRES ROOT and Android 11 or higher."
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Abi"
|
||||||
|
-- version = "3.3"
|
||||||
|
-- foldable = "false"
|
||||||
|
|
||||||
|
local entries = {}
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
system:su('dumpsys activity recents')
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_shell_result(raw)
|
||||||
|
local start = raw:find("Visible recent tasks")
|
||||||
|
if not start then
|
||||||
|
ui:show_text("No recent apps")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local trimmed = raw:sub(start)
|
||||||
|
|
||||||
|
local blocks = {}
|
||||||
|
local pat = "%* RecentTaskInfo #%d+:"
|
||||||
|
local pos = 1
|
||||||
|
while true do
|
||||||
|
local s,e = trimmed:find(pat, pos)
|
||||||
|
if not s then break end
|
||||||
|
local ns,_ = trimmed:find(pat, e+1)
|
||||||
|
if ns then
|
||||||
|
blocks[#blocks+1] = trimmed:sub(s, ns-1)
|
||||||
|
pos = ns
|
||||||
|
else
|
||||||
|
blocks[#blocks+1] = trimmed:sub(s)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
entries = {}
|
||||||
|
for _, block in ipairs(blocks) do
|
||||||
|
local id = block:match("id=(%d+)")
|
||||||
|
local has = block:match("hasTask=(%a+)")
|
||||||
|
local last = block:match("lastActiveTime=(%d+)")
|
||||||
|
local full = block:match("cmp=([%w%.]+/[%w%._]+)")
|
||||||
|
local pkg = full and full:match("^([^/]+)")
|
||||||
|
|
||||||
|
if id and has and last and pkg then
|
||||||
|
entries[#entries+1] = {
|
||||||
|
id = tonumber(id),
|
||||||
|
hasTask = (has == "true"),
|
||||||
|
lastActiveTime = tonumber(last),
|
||||||
|
packageName = pkg,
|
||||||
|
appName = get_name_or_color(pkg, "name"),
|
||||||
|
appColor = get_name_or_color(pkg, "color"),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #entries == 0 then
|
||||||
|
ui:show_toast("No recent apps parsed")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
do
|
||||||
|
local names, colors = {}, {}
|
||||||
|
for i, e in ipairs(entries) do
|
||||||
|
names[i] = e.appName
|
||||||
|
colors[i] = e.appColor
|
||||||
|
end
|
||||||
|
ui:show_buttons(names, colors)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
local pkg = entries[idx] and entries[idx].packageName
|
||||||
|
if pkg then
|
||||||
|
apps:launch(pkg)
|
||||||
|
else
|
||||||
|
ui:show_toast("No package at index "..tostring(idx))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_long_click(idx)
|
||||||
|
local e = entries[idx]
|
||||||
|
if not e then
|
||||||
|
ui:show_toast("No app at index "..tostring(idx))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
system:su("am stack remove " .. entries[idx].id)
|
||||||
|
system:su('dumpsys activity recents')
|
||||||
|
end
|
||||||
|
|
||||||
|
function to_hex_rgb(argb)
|
||||||
|
if argb < 0 then argb = 4294967296 + argb end
|
||||||
|
local r = math.floor((argb / 65536) % 256)
|
||||||
|
local g = math.floor((argb / 256) % 256)
|
||||||
|
local b = math.floor(argb % 256)
|
||||||
|
return string.format("#%02X%02X%02X", r, g, b)
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_name_or_color(pkg, nameorcolor)
|
||||||
|
local info = apps:app(pkg)
|
||||||
|
if not info then
|
||||||
|
if nameorcolor == "name" then
|
||||||
|
return "???"
|
||||||
|
elseif nameorcolor == "color" then
|
||||||
|
return "#FFFFFF"
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if nameorcolor == "name" then
|
||||||
|
return info.name or "???"
|
||||||
|
elseif nameorcolor == "color" then
|
||||||
|
return to_hex_rgb(info.color or 0xFFFFFF)
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -10,7 +10,7 @@ function on_search(str)
|
|||||||
local region = codes[code]
|
local region = codes[code]
|
||||||
|
|
||||||
if region ~= nil then
|
if region ~= nil then
|
||||||
search:show{code.." - "..region}
|
search:show_buttons{code.." - "..region}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
17
community/settings-menu.lua
Normal file
17
community/settings-menu.lua
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
-- name = "Settings menu"
|
||||||
|
-- name_id = "settings"
|
||||||
|
-- description = "Side menu with AIO settings"
|
||||||
|
-- aio_version = "4.7.99"
|
||||||
|
-- type = "drawer"
|
||||||
|
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
||||||
|
-- version = "1.0"
|
||||||
|
|
||||||
|
function on_drawer_open()
|
||||||
|
settings = aio:settings()
|
||||||
|
labels = map(function(it) return it.label end, settings)
|
||||||
|
drawer:show_list(labels)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
aio:open_settings(settings[idx].name)
|
||||||
|
end
|
||||||
@@ -18,7 +18,7 @@ text_to=""
|
|||||||
function on_search(input)
|
function on_search(input)
|
||||||
text_from = input
|
text_from = input
|
||||||
text_to = ""
|
text_to = ""
|
||||||
search:show({"Share \""..input.."\""}, {blue})
|
search:show_buttons({"Share \""..input.."\""}, {blue}, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_click()
|
function on_click()
|
||||||
|
|||||||
@@ -7,16 +7,20 @@
|
|||||||
|
|
||||||
current_output = "Click to enter command"
|
current_output = "Click to enter command"
|
||||||
|
|
||||||
|
function on_preview()
|
||||||
|
ui:show_text("Shows the result of executing console commands")
|
||||||
|
end
|
||||||
|
|
||||||
function on_resume()
|
function on_resume()
|
||||||
redraw()
|
redraw()
|
||||||
end
|
end
|
||||||
|
|
||||||
function redraw()
|
function redraw()
|
||||||
ui:show_text(current_output)
|
ui:show_text("%%txt%%"..current_output)
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_click(idx)
|
function on_click(idx)
|
||||||
ui:show_edit_dialog("Enter command")
|
dialogs:show_edit_dialog("Enter command")
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_dialog_action(text)
|
function on_dialog_action(text)
|
||||||
79
community/solar-cycle-widget.lua
Normal file
79
community/solar-cycle-widget.lua
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
-- name = "Solar Cycle"
|
||||||
|
-- description = "Shows Sunrise Sunset at your location"
|
||||||
|
-- data_source = "https://api.sunrise-sunset.org/"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Sriram S V, Will Hall"
|
||||||
|
-- version = "1.0"
|
||||||
|
-- foldable = "false"
|
||||||
|
|
||||||
|
local json = require "json"
|
||||||
|
local md_colors = require "md_colors"
|
||||||
|
|
||||||
|
function on_alarm()
|
||||||
|
local location=system:location()
|
||||||
|
url="https://api.sunrise-sunset.org/json?lat="..location[1].."&lng="..location[2].."&formatted=0"
|
||||||
|
http:get(url)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function on_network_result(result)
|
||||||
|
local t = json.decode(result)
|
||||||
|
|
||||||
|
local times_table = {
|
||||||
|
{
|
||||||
|
gen_icon("red_900","↦"),
|
||||||
|
gen_icon("orange_900", "↗"),
|
||||||
|
gen_icon("yellow_900", "☀"),
|
||||||
|
gen_icon("orange_900", "↘"),
|
||||||
|
gen_icon("red_900", "⇥"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
time_from_utc(t.results.civil_twilight_begin),
|
||||||
|
time_from_utc(t.results.sunrise),
|
||||||
|
time_from_utc(t.results.solar_noon),
|
||||||
|
time_from_utc(t.results.sunset),
|
||||||
|
time_from_utc(t.results.civil_twilight_end),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui:show_table(times_table, 0, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
function time_from_utc(utc)
|
||||||
|
return utc_to_local(parse_iso8601_datetime(utc))
|
||||||
|
end
|
||||||
|
|
||||||
|
function gen_icon(md_color, icon)
|
||||||
|
return "<font color="..md_colors[md_color].."><b>"..icon.."</b></font>"
|
||||||
|
end
|
||||||
|
|
||||||
|
function parse_iso8601_datetime(json_date)
|
||||||
|
local pattern = "(%d+)%-(%d+)%-(%d+)%a(%d+)%:(%d+)%:([%d%.]+)([Z%+%-]?)(%d?%d?)%:?(%d?%d?)"
|
||||||
|
local year, month, day, hour, minute,
|
||||||
|
seconds, offsetsign, offsethour, offsetmin = json_date:match(pattern)
|
||||||
|
local timestamp = os.time{year = year, month = month,
|
||||||
|
day = day, hour = hour, min = minute, sec = seconds}
|
||||||
|
local offset = 0
|
||||||
|
if offsetsign ~= '' and offsetsign ~= 'Z' then
|
||||||
|
offset = tonumber(offsethour) * 60 + tonumber(offsetmin)
|
||||||
|
if xoffset == "-" then offset = offset * -1 end
|
||||||
|
end
|
||||||
|
|
||||||
|
return timestamp + offset * 60
|
||||||
|
end
|
||||||
|
|
||||||
|
function utc_to_local(utctime)
|
||||||
|
local local_time_str = os.date("%H:%M", utctime)
|
||||||
|
local utc_time_str = os.date("!%H:%M", utctime)
|
||||||
|
|
||||||
|
local function time_to_seconds(timestr)
|
||||||
|
local hour, minute = timestr:match("(%d+):(%d+)")
|
||||||
|
return tonumber(hour) * 3600 + tonumber(minute) * 60
|
||||||
|
end
|
||||||
|
|
||||||
|
local local_seconds = time_to_seconds(local_time_str)
|
||||||
|
local utc_seconds = time_to_seconds(utc_time_str)
|
||||||
|
local delta = local_seconds - utc_seconds
|
||||||
|
|
||||||
|
return os.date("%H:%M", utctime + delta)
|
||||||
|
end
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
-- name = "Sudoku"
|
-- name = "Sudoku"
|
||||||
-- description = "Sudoku games"
|
-- description = "Sudoku games"
|
||||||
-- type = "Game"
|
-- type = "widget"
|
||||||
-- author = "Andrey Gavrilov"
|
-- author = "Andrey Gavrilov"
|
||||||
-- version = "1.0"
|
-- version = "1.0"
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
-- name = "Sunrise/Sunset"
|
|
||||||
-- description = "Shows Sunrise Sunset at your location"
|
|
||||||
-- data_source = "https://api.sunrise-sunset.org/"
|
|
||||||
-- type = "widget"
|
|
||||||
-- author = "Sriram S V"
|
|
||||||
-- version = "1.0"
|
|
||||||
-- foldable = "false"
|
|
||||||
|
|
||||||
local json = require "json"
|
|
||||||
local date = require "date"
|
|
||||||
function on_alarm()
|
|
||||||
local location=system:location()
|
|
||||||
url="https://api.sunrise-sunset.org/json?lat="..location[1].."&lng="..location[2].."&date=today&formatted=1"
|
|
||||||
http:get(url)
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function on_network_result(result)
|
|
||||||
local t = json.decode(result)
|
|
||||||
local table = {
|
|
||||||
{ "sunrise:", date(t.results.sunrise):tolocal():fmt("%r") },
|
|
||||||
{ "sunset:", date(t.results.sunset):tolocal():fmt("%r") },
|
|
||||||
}
|
|
||||||
ui:show_table(table, 2)
|
|
||||||
end
|
|
||||||
80
community/thirukkural-widget.lua
Normal file
80
community/thirukkural-widget.lua
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
-- name = "Thirukkural"
|
||||||
|
-- description = "Thirukkural Widget that refreshes a verse form the Tamil Book Thirukkural. Single click will open the diaglog with multiple translations in English and Tamili. Long press will retrive a new verse. This uses the API from getthirukural"
|
||||||
|
-- data_source = "https://getthirukural.appspot.com/"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Abdul MJ (mjabdulm@gmail.com)"
|
||||||
|
-- version = "1.0"
|
||||||
|
-- foldable = "false"
|
||||||
|
|
||||||
|
local json = require "json"
|
||||||
|
local kural_data = nil -- Store the kural translation data
|
||||||
|
|
||||||
|
function on_alarm()
|
||||||
|
-- Fetch random kural with English translation
|
||||||
|
-- http:get("https://api.alquran.cloud/v1/ayah/random/en.sahih")
|
||||||
|
http:get("https://getthirukural.appspot.com/api/3.0/kural/rnd?appid=bzh3rnqagllov")
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_network_result(result)
|
||||||
|
local response = json.decode(result)
|
||||||
|
|
||||||
|
if response then
|
||||||
|
-- Store kural data including athigaram name, kural number
|
||||||
|
kural_data = {
|
||||||
|
number = response.number,
|
||||||
|
line1 = response.line1,
|
||||||
|
line2 = response.line2,
|
||||||
|
paal = response.paal,
|
||||||
|
athigaram = response.athigaram,
|
||||||
|
iyal = response.iyal,
|
||||||
|
urai1Author = response.urai1Author,
|
||||||
|
urai2Author = response.urai2Author,
|
||||||
|
urai3Author = response.urai3Author,
|
||||||
|
urai1 = response.urai1,
|
||||||
|
urai2 = response.urai2,
|
||||||
|
urai3 = response.urai3,
|
||||||
|
en = response.en
|
||||||
|
}
|
||||||
|
|
||||||
|
display_kural()
|
||||||
|
else
|
||||||
|
ui:show_message("Error loading kural data.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function display_kural()
|
||||||
|
if kural_data then
|
||||||
|
local display_lines = {
|
||||||
|
-- kural_data.number .. " : " .. kural_data.line1 .. "<br/> " .. kural_data.line2
|
||||||
|
-- kural_data.line1 .. "<br/>" .. kural_data.line2 .. " - <font color=red style=font-size:0.5em;>" .. kural_data.number .. ":".. kural_data.athigaram .. ":" .. kural_data.paal .. "</font>"
|
||||||
|
kural_data.line1 .. "<br/>" .. kural_data.line2 .. " -<i><font color=red>" .. kural_data.number .. "</i></font>"
|
||||||
|
-- kural_data.line1 .. "<br/>" .. kural_data.line2 .. [[- <font color=grey style="font-size: 0.5em;">]] .. kural_data.number .. ":" .. kural_data.athigaram .. "</font>"
|
||||||
|
}
|
||||||
|
|
||||||
|
-- ui:show_lines(display_lines display_titles)
|
||||||
|
ui:show_lines(display_lines)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click()
|
||||||
|
if kural_data then
|
||||||
|
-- Prepare text to copy to clipboard with English translation only
|
||||||
|
local title = "Thirukkural:" .. kural_data.number
|
||||||
|
|
||||||
|
local text = kural_data.number .. ":" .. kural_data.paal .. ":" .. kural_data.iyal .. ":" .. kural_data.athigaram .. "<br/><br/><b>Kural<br/></b>" .. kural_data.line1 .. "<br/>" .. kural_data.line2 .. "<br/><br/><b>English:</b><br/>" .. kural_data.en .. "<br\><br\><b>" .. kural_data.urai1Author .. "</b><br/>" .. kural_data.urai1 .. "<br\><br\><b>" .. kural_data.urai2Author .. "</b><br/>" .. kural_data.urai2 .. "<br\><br\><b>" .. kural_data.urai3Author .. "</b><br/>" .. kural_data.urai3
|
||||||
|
|
||||||
|
-- system:to_clipboard(clipboard_text)
|
||||||
|
dialogs:show_dialog(title,text)
|
||||||
|
display_kural()
|
||||||
|
-- ui:show_lines(clipboard_text)
|
||||||
|
else
|
||||||
|
ui:show_message("No kural available to copy.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_long_click()
|
||||||
|
-- Fetch random kural on long-click
|
||||||
|
http:get("https://getthirukural.appspot.com/api/3.0/kural/rnd?appid=bzh3rnqagllov")
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
110
community/ticktick-app-widget.lua
Normal file
110
community/ticktick-app-widget.lua
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
-- name = "TickTick"
|
||||||
|
-- description = "AIO Launhcer wrapper for official TickTick app widget"
|
||||||
|
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
||||||
|
-- version = "1.0"
|
||||||
|
-- uses_app = "com.ticktick.task"
|
||||||
|
|
||||||
|
local prefs = require "prefs"
|
||||||
|
local fmt = require "fmt"
|
||||||
|
|
||||||
|
local w_bridge = nil
|
||||||
|
local lines = {}
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
if not widgets:bound(prefs.wid) then
|
||||||
|
setup_app_widget()
|
||||||
|
end
|
||||||
|
|
||||||
|
widgets:request_updates(prefs.wid)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_app_widget_updated(bridge)
|
||||||
|
lines = {}
|
||||||
|
|
||||||
|
-- We use dump_tree instead of dump_table because Lua tables
|
||||||
|
-- do not preserve the order of elements (which is important in this case).
|
||||||
|
-- Additionally, the text tree is simply easier to parse.
|
||||||
|
local tree = bridge:dump_tree()
|
||||||
|
local all_lines = extract_list_item_lines(tree)
|
||||||
|
lines = combine_lines(all_lines)
|
||||||
|
|
||||||
|
table.insert(lines, fmt.secondary("Add task"))
|
||||||
|
|
||||||
|
w_bridge = bridge
|
||||||
|
ui:show_lines(lines)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
if idx == #lines then
|
||||||
|
-- "Plus" button
|
||||||
|
w_bridge:click("image_3")
|
||||||
|
else
|
||||||
|
-- First task name
|
||||||
|
w_bridge:click("text_2")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_settings()
|
||||||
|
w_bridge:click("text_1")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Extract all elements with a nesting level of 6 (task texts and dates)
|
||||||
|
function extract_list_item_lines(str)
|
||||||
|
for line in str:gmatch("[^\n]+") do
|
||||||
|
if line:match("^%s%s%s%s%s%s%s%s%s%s%s%s") then
|
||||||
|
table.insert(lines, extract_text_after_colon(line))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return lines
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Tasks list elements in the the dump are separated by a 1x1 image,
|
||||||
|
-- so we can use it to understand where one task ends and another begins.
|
||||||
|
function combine_lines(lines)
|
||||||
|
local result = {}
|
||||||
|
local temp_lines = {}
|
||||||
|
|
||||||
|
for i, line in ipairs(lines) do
|
||||||
|
if line == "1x1" then
|
||||||
|
if #temp_lines > 0 then
|
||||||
|
table.insert(result, concat_lines(temp_lines))
|
||||||
|
temp_lines = {}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
table.insert(temp_lines, line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #temp_lines > 0 then
|
||||||
|
table.insert(result, concat_lines(temp_lines))
|
||||||
|
end
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- The text and date of a task in the dump appear consecutively,
|
||||||
|
-- and we need to combine them, keeping in mind that the date might be absent.
|
||||||
|
function concat_lines(lines)
|
||||||
|
if lines[1] then
|
||||||
|
lines[1] = fmt.primary(lines[1])
|
||||||
|
end
|
||||||
|
|
||||||
|
if lines[2] then
|
||||||
|
lines[2] = fmt.secondary(lines[2])
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.concat(lines, fmt.secondary(" - "))
|
||||||
|
end
|
||||||
|
|
||||||
|
function extract_text_after_colon(text)
|
||||||
|
return text:match(":%s*(.*)")
|
||||||
|
end
|
||||||
|
|
||||||
|
function setup_app_widget()
|
||||||
|
local id = widgets:setup("com.ticktick.task/com.ticktick.task.activity.widget.GoogleTaskAppWidgetProviderLarge")
|
||||||
|
if (id ~= nil) then
|
||||||
|
prefs.wid = id
|
||||||
|
else
|
||||||
|
ui:show_text("Can't add widget")
|
||||||
|
end
|
||||||
|
end
|
||||||
114
community/tide-widget.lua
Normal file
114
community/tide-widget.lua
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
-- name = "Tide Time"
|
||||||
|
-- description = "Widget shows high and low tide times for today."
|
||||||
|
-- data_source = "https://open-meteo.com/en/docs/marine-weather-api"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "AI Assistant"
|
||||||
|
-- version = "1.0"
|
||||||
|
|
||||||
|
local json = require "json"
|
||||||
|
local color = require "md_colors"
|
||||||
|
local text_color = aio:colors().secondary_text
|
||||||
|
|
||||||
|
-- variables
|
||||||
|
local tide_data = nil
|
||||||
|
local tide_times = {}
|
||||||
|
|
||||||
|
function on_alarm()
|
||||||
|
get_tide_data()
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_tide_data()
|
||||||
|
local today = os.date("%Y-%m-%d")
|
||||||
|
local location = system:location()
|
||||||
|
local url = string.format(
|
||||||
|
"https://marine-api.open-meteo.com/v1/marine?latitude=%f&longitude=%f&hourly=sea_level_height_msl&timezone=auto&start_date=%s&end_date=%s",
|
||||||
|
location[1], location[2], today, today
|
||||||
|
)
|
||||||
|
http:get(url)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_network_result(result)
|
||||||
|
local data = json.decode(result)
|
||||||
|
if not data or not data.hourly then
|
||||||
|
ui:show_toast("Error getting data: " .. (result or "no response"))
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
tide_data = data
|
||||||
|
process_tide_data()
|
||||||
|
display_tide_times()
|
||||||
|
end
|
||||||
|
|
||||||
|
function process_tide_data()
|
||||||
|
tide_times = {}
|
||||||
|
local times = tide_data.hourly.time
|
||||||
|
local heights = tide_data.hourly.sea_level_height_msl
|
||||||
|
|
||||||
|
if not times or not heights then
|
||||||
|
ui:show_text("Invalid data format")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Find local maxima and minima
|
||||||
|
for i = 2, #heights - 1 do
|
||||||
|
if heights[i] > heights[i-1] and heights[i] > heights[i+1] then
|
||||||
|
table.insert(tide_times, {
|
||||||
|
time = times[i],
|
||||||
|
height = heights[i],
|
||||||
|
type = "high tide"
|
||||||
|
})
|
||||||
|
elseif heights[i] < heights[i-1] and heights[i] < heights[i+1] then
|
||||||
|
table.insert(tide_times, {
|
||||||
|
time = times[i],
|
||||||
|
height = heights[i],
|
||||||
|
type = "low tide"
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function display_tide_times()
|
||||||
|
if #tide_times == 0 then
|
||||||
|
ui:show_text("No tide data available")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local tab = {}
|
||||||
|
for _, tide in ipairs(tide_times) do
|
||||||
|
local time = tide.time:match("T(%d+:%d+)")
|
||||||
|
local height = string.format("%.2f", tide.height)
|
||||||
|
local type = tide.type
|
||||||
|
local color = type == "high tide" and color.blue_500 or color.blue_900
|
||||||
|
|
||||||
|
table.insert(tab, {
|
||||||
|
string.format("<font color=\"%s\">%s</font>", color, type),
|
||||||
|
time,
|
||||||
|
height .. " m"
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
ui:show_table(tab, 3)
|
||||||
|
ui:set_title(ui:default_title() .. " (" .. os.date("%d.%m.%Y") .. ")")
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_long_click(idx)
|
||||||
|
ui:show_context_menu({
|
||||||
|
{"redo", "Refresh"},
|
||||||
|
{"copy", "Copy data"}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_context_menu_click(menu_idx)
|
||||||
|
if menu_idx == 1 then
|
||||||
|
get_tide_data()
|
||||||
|
ui:show_toast("Updating data...")
|
||||||
|
elseif menu_idx == 2 then
|
||||||
|
local text = "Tide times for " .. os.date("%d.%m.%Y") .. ":\n"
|
||||||
|
for _, tide in ipairs(tide_times) do
|
||||||
|
local time = tide.time:match("T(%d+:%d+)")
|
||||||
|
text = text .. string.format("%s: %s (%.2f m)\n", tide.type, time, tide.height)
|
||||||
|
end
|
||||||
|
system:copy_to_clipboard(text)
|
||||||
|
ui:show_toast("Data copied")
|
||||||
|
end
|
||||||
|
end
|
||||||
41
community/time-date-widget.lua
Normal file
41
community/time-date-widget.lua
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
-- name = "Time & Date"
|
||||||
|
-- description = "Simple widget showing current time and date"
|
||||||
|
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
||||||
|
-- type = "widget"
|
||||||
|
-- aio_version = "5.2.1"
|
||||||
|
|
||||||
|
local function draw()
|
||||||
|
local now = os.date("*t")
|
||||||
|
local time = string.format("%02d:%02d", now.hour, now.min)
|
||||||
|
local date = os.date("%a, %d %b")
|
||||||
|
|
||||||
|
gui{
|
||||||
|
{"spacer", 2},
|
||||||
|
{"text", time, { size = 40 }},
|
||||||
|
{"text", date, { size = 20, gravity = "right|center_v" }},
|
||||||
|
{"spacer", 2},
|
||||||
|
}.render()
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_tick()
|
||||||
|
draw()
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
-- time
|
||||||
|
if idx == 2 then
|
||||||
|
intent:start_activity{
|
||||||
|
action = "android.intent.action.SHOW_ALARMS"
|
||||||
|
}
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- date
|
||||||
|
if idx == 3 then
|
||||||
|
intent:start_activity{
|
||||||
|
action = "android.intent.action.MAIN",
|
||||||
|
category = "android.intent.category.APP_CALENDAR"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
58
community/timed-message-widget.lua
Normal file
58
community/timed-message-widget.lua
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
-- name = "Timed message"
|
||||||
|
-- description = "A message that is displayed at a specific time"
|
||||||
|
-- type = "widget"
|
||||||
|
-- foldable = "false"
|
||||||
|
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
||||||
|
-- version = "1.0"
|
||||||
|
|
||||||
|
local prefs = require "prefs"
|
||||||
|
|
||||||
|
function on_load()
|
||||||
|
prefs._dialog_order = "message,start_time,end_time"
|
||||||
|
|
||||||
|
if not prefs.message then
|
||||||
|
prefs.message = "Sample message"
|
||||||
|
end
|
||||||
|
|
||||||
|
if not prefs.start_time then
|
||||||
|
prefs.start_time = "00:00"
|
||||||
|
end
|
||||||
|
|
||||||
|
if not prefs.end_time then
|
||||||
|
prefs.end_time = "23:59"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
ui:show_text(prefs.message)
|
||||||
|
hide_or_show()
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_settings()
|
||||||
|
prefs:show_dialog()
|
||||||
|
end
|
||||||
|
|
||||||
|
function hide_or_show()
|
||||||
|
local start_time = convert_to_unix_time(prefs.start_time)
|
||||||
|
local end_time = convert_to_unix_time(prefs.end_time)
|
||||||
|
local current_time = os.time()
|
||||||
|
|
||||||
|
if current_time > start_time and current_time < end_time then
|
||||||
|
ui:show_widget()
|
||||||
|
else
|
||||||
|
ui:hide_widget()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function convert_to_unix_time(time_str)
|
||||||
|
local hour, minute = time_str:match("^(%d%d):(%d%d)$")
|
||||||
|
hour, minute = tonumber(hour), tonumber(minute)
|
||||||
|
|
||||||
|
local current_date = os.date("*t")
|
||||||
|
|
||||||
|
current_date.hour = hour
|
||||||
|
current_date.min = minute
|
||||||
|
current_date.sec = 0
|
||||||
|
|
||||||
|
return os.time(current_date)
|
||||||
|
end
|
||||||
125
community/todoist-app-widget.lua
Normal file
125
community/todoist-app-widget.lua
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
-- name = "Todoist"
|
||||||
|
-- description = "AIO wrapper for the official Todoist app widget"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Andey Gavrilov"
|
||||||
|
-- aio_version = "4.1.99"
|
||||||
|
-- uses_app = "com.todoist"
|
||||||
|
|
||||||
|
local prefs = require "prefs"
|
||||||
|
local fmt = require "fmt"
|
||||||
|
|
||||||
|
local widget = "com.todoist/com.todoist.appwidget.provider.ItemListAppWidgetProvider"
|
||||||
|
|
||||||
|
local curr_tab = {}
|
||||||
|
local w_bridge = nil
|
||||||
|
local colors = {}
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
if not widgets:bound(prefs.wid) then
|
||||||
|
setup_app_widget()
|
||||||
|
end
|
||||||
|
widgets:request_updates(prefs.wid)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_app_widget_updated(bridge)
|
||||||
|
w_bridge = bridge
|
||||||
|
colors = bridge:dump_colors()
|
||||||
|
curr_tab = bridge:dump_table()
|
||||||
|
curr_tab = parse(curr_tab)
|
||||||
|
ui:show_lines(curr_tab.lines)
|
||||||
|
end
|
||||||
|
|
||||||
|
function parse(t)
|
||||||
|
local tab = {}
|
||||||
|
local images = {}
|
||||||
|
local lines = {}
|
||||||
|
local clicks = {}
|
||||||
|
local tasks = t.v_layout_1.frame_layout_2.list_layout_1
|
||||||
|
if not tasks then tasks = {} end
|
||||||
|
local tkeys = {}
|
||||||
|
for k,v in pairs(tasks) do
|
||||||
|
table.insert(tkeys,k)
|
||||||
|
end
|
||||||
|
table.sort(tkeys,function (a,b) return tonumber(a:match("%d+")) < tonumber(b:match("%d+")) end)
|
||||||
|
for i1,v1 in ipairs(tkeys) do
|
||||||
|
for k2,v2 in pairs(tasks[v1]) do
|
||||||
|
for k3,v3 in pairs(v2) do
|
||||||
|
if k3:sub(1,5) == "image" then
|
||||||
|
table.insert(images,k3)
|
||||||
|
elseif k3:sub(1,8) == "v_layout" then
|
||||||
|
local text = ""
|
||||||
|
local subtexts = {}
|
||||||
|
for k4,v4 in pairs(v3) do
|
||||||
|
if k4:sub(1,4) == "text" then
|
||||||
|
table.insert(clicks,k4)
|
||||||
|
text = v4
|
||||||
|
local color = colors[images[#images]]
|
||||||
|
if color ~= "#909090" and color then
|
||||||
|
text = fmt.colored(fmt.bold(text),color)
|
||||||
|
end
|
||||||
|
elseif k4:sub(1,8) == "h_layout" then
|
||||||
|
subtexts = recursion(v4,subtexts,1,false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert(lines,text..table.concat(subtexts))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
local footer = t.v_layout_1.frame_layout_2
|
||||||
|
for k,v in pairs(footer) do
|
||||||
|
if k:sub(1,8) == "relative" then
|
||||||
|
footer = v
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
for k1,v1 in pairs(footer) do
|
||||||
|
for k2,v2 in pairs(v1) do
|
||||||
|
if k2:sub(1,4) == "text" then
|
||||||
|
table.insert(images,k2)
|
||||||
|
table.insert(clicks,k2)
|
||||||
|
local add_task = "Add task"
|
||||||
|
if #lines ~= 0 then
|
||||||
|
add_task = fmt.secondary(add_task)
|
||||||
|
end
|
||||||
|
table.insert(lines,add_task)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
tab["images"] = images
|
||||||
|
tab["lines"] = lines
|
||||||
|
tab["clicks"] = clicks
|
||||||
|
return tab
|
||||||
|
end
|
||||||
|
|
||||||
|
function recursion(t,tab,idx,flag)
|
||||||
|
for k,v in pairs(t) do
|
||||||
|
if k:sub(1,4) == "text" then
|
||||||
|
local a,b = v:match("(%d+)/(%d+)")
|
||||||
|
if a or flag then
|
||||||
|
table.insert(tab,idx,fmt.secondary(" - ") .. fmt.colored(v,colors[k]))
|
||||||
|
end
|
||||||
|
elseif k:sub(1,8) == "h_layout" then
|
||||||
|
tab = recursion(v,tab,#tab+1,true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return tab
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
w_bridge:click(curr_tab.clicks[idx])
|
||||||
|
end
|
||||||
|
|
||||||
|
function setup_app_widget()
|
||||||
|
local id = widgets:setup(widget)
|
||||||
|
if (id ~= nil) then
|
||||||
|
prefs.wid = id
|
||||||
|
else
|
||||||
|
ui:show_toast("Can't add widget")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_settings()
|
||||||
|
w_bridge:click("text_1")
|
||||||
|
end
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
-- name = "ТВ-Программа"
|
-- name = "ТВ-Программа"
|
||||||
-- description = "Программа передач россиийского ТВ"
|
-- description = "Программа передач россиийского ТВ"
|
||||||
-- type = "widget"
|
-- type = "widget"
|
||||||
-- version = "1.0"
|
-- version = "1.1"
|
||||||
-- lang = "ru"
|
-- lang = "ru"
|
||||||
-- author = "Andrey Gavrilov"
|
-- author = "Andrey Gavrilov"
|
||||||
|
|
||||||
@@ -23,9 +23,9 @@ end
|
|||||||
|
|
||||||
function on_click(idx)
|
function on_click(idx)
|
||||||
if math.ceil(idx/2) > #tab_desc then
|
if math.ceil(idx/2) > #tab_desc then
|
||||||
ui:show_edit_dialog("Введите название канала","",title)
|
dialogs:show_edit_dialog("Введите название канала","",title)
|
||||||
else
|
else
|
||||||
ui:show_dialog(tab_name[math.ceil(idx/2)].."\n"..tab_time[math.ceil(idx/2)],tab_desc[math.ceil(idx/2)],"Перейти к каналу")
|
dialogs:show_dialog(tab_name[math.ceil(idx/2)].."\n"..tab_time[math.ceil(idx/2)],tab_desc[math.ceil(idx/2)],"Перейти к каналу")
|
||||||
link = tab_link[math.ceil(idx/2)]
|
link = tab_link[math.ceil(idx/2)]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
73
community/uptimerobot-2-widget.lua
Normal file
73
community/uptimerobot-2-widget.lua
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
-- name = "Uptimerobot V2"
|
||||||
|
-- description = "Shows uptime information from uptimerobot.com. Needs API key. Button-based Version."
|
||||||
|
-- data_source = "uptimerobot.com"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Evgeny Zobnin (zobnin@gmail.com), Will Hall (hello@willhall.uk)"
|
||||||
|
-- version = "1.0"
|
||||||
|
-- arguments_help = "Enter your API key"
|
||||||
|
|
||||||
|
local json = require "json"
|
||||||
|
local md_colors = require "md_colors"
|
||||||
|
|
||||||
|
-- constants
|
||||||
|
local api_url = "https://api.uptimerobot.com/v2/"
|
||||||
|
local base_click_url = "https://uptimerobot.com/dashboard#"
|
||||||
|
local media_type = "application/x-www-form-urlencoded"
|
||||||
|
local status_colors = { "grey_500", "green_500", "red_500", "red_500", "red_500", "red_500", "red_500", "orange_500", "red_500" }
|
||||||
|
|
||||||
|
-- monitors
|
||||||
|
local monitor_ids = {}
|
||||||
|
|
||||||
|
function on_alarm()
|
||||||
|
if (next(settings:get()) == nil) then
|
||||||
|
ui:show_text("Tap to enter API key")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local key = settings:get()[1]
|
||||||
|
local body = "api_key="..key.."&format=json"
|
||||||
|
|
||||||
|
http:post(api_url.."getMonitors", body, media_type)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(i)
|
||||||
|
if (next(settings:get()) == nil) then
|
||||||
|
settings:show_dialog()
|
||||||
|
else
|
||||||
|
if(monitor_ids[i] ~= nil) then
|
||||||
|
system:open_browser(base_click_url..monitor_ids[i])
|
||||||
|
else
|
||||||
|
system:open_browser(base_click_url.."mainDashboard")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_network_result(result, code)
|
||||||
|
if (code >= 400) then
|
||||||
|
ui:show_text("Error: "..code)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local parsed = json.decode(result)
|
||||||
|
|
||||||
|
if (parsed.stat ~= "ok") then
|
||||||
|
ui:show_text("Error: "..parsed.error.message)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local names = {}
|
||||||
|
local colours = {}
|
||||||
|
|
||||||
|
for k,v in pairs(parsed.monitors) do
|
||||||
|
monitor_ids[k] = v.id
|
||||||
|
names[k] = v.friendly_name
|
||||||
|
colours[k] = md_colors[status_colors[v.status]] or md_colors["grey_500"]
|
||||||
|
end
|
||||||
|
|
||||||
|
ui:show_buttons(names, colours)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_settings()
|
||||||
|
settings:show_dialog()
|
||||||
|
end
|
||||||
|
|
||||||
@@ -14,6 +14,15 @@ local api_url = "https://api.uptimerobot.com/v2/"
|
|||||||
local click_url = "https://uptimerobot.com/dashboard#mainDashboard"
|
local click_url = "https://uptimerobot.com/dashboard#mainDashboard"
|
||||||
local media_type = "application/x-www-form-urlencoded"
|
local media_type = "application/x-www-form-urlencoded"
|
||||||
|
|
||||||
|
function on_preview()
|
||||||
|
if (next(settings:get()) == nil) then
|
||||||
|
ui:show_text("Shows uptime information from uptimerobot.com")
|
||||||
|
return
|
||||||
|
else
|
||||||
|
on_alarm()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function on_alarm()
|
function on_alarm()
|
||||||
if (next(settings:get()) == nil) then
|
if (next(settings:get()) == nil) then
|
||||||
ui:show_text("Tap to enter API key")
|
ui:show_text("Tap to enter API key")
|
||||||
@@ -82,3 +91,8 @@ function table_to_tables(tab, num)
|
|||||||
|
|
||||||
return out_tab
|
return out_tab
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function on_settings()
|
||||||
|
settings:show_dialog()
|
||||||
|
end
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
-- name = "What to do?"
|
-- name = "What to do?"
|
||||||
-- description = "Let's find you something to do"
|
-- description = "Let's find you something to do"
|
||||||
-- data_source = "https://www.boredapi.com/"
|
-- data_source = "https://bored.api.lewagon.com/"
|
||||||
-- type = "widget"
|
-- type = "widget"
|
||||||
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
||||||
-- version = "1.0"
|
-- version = "1.0"
|
||||||
@@ -12,7 +12,7 @@ end
|
|||||||
|
|
||||||
function on_click()
|
function on_click()
|
||||||
system:vibrate(100)
|
system:vibrate(100)
|
||||||
http:get("http://www.boredapi.com/api/activity/")
|
http:get("https://bored.api.lewagon.com/api/activity/")
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_network_result(result)
|
function on_network_result(result)
|
||||||
|
|||||||
145
community/widgets-on-off.lua
Normal file
145
community/widgets-on-off.lua
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
-- name = "Widgets switcher"
|
||||||
|
-- description = "Turns screen widgets on and off when buttons are pressed"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Andrey Gavrilov"
|
||||||
|
-- version = "3.1"
|
||||||
|
|
||||||
|
prefs = require "prefs"
|
||||||
|
|
||||||
|
local pos = 0
|
||||||
|
local buttons,colors = {},{}
|
||||||
|
|
||||||
|
function on_alarm()
|
||||||
|
widgets = get_widgets()
|
||||||
|
if not prefs.widgets then
|
||||||
|
prefs.widgets = widgets.name
|
||||||
|
end
|
||||||
|
indexes = get_indexes(prefs.widgets, widgets.name)
|
||||||
|
ui:show_buttons(get_buttons())
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_long_click(idx)
|
||||||
|
system:vibrate(10)
|
||||||
|
pos = idx
|
||||||
|
if idx > #prefs.widgets then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
ui:show_context_menu({{"angle-left",""},{"ban",""},{"angle-right",""},{widgets.icon[indexes[idx]],widgets.label[indexes[idx]]}})
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
system:vibrate(10)
|
||||||
|
if idx > #prefs.widgets then
|
||||||
|
on_settings()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local widget = prefs.widgets[idx]
|
||||||
|
if not aio:is_widget_added(widget) then
|
||||||
|
aio:add_widget(widget, get_pos())
|
||||||
|
aio:fold_widget(widget, false)
|
||||||
|
else
|
||||||
|
aio:remove_widget(widget)
|
||||||
|
end
|
||||||
|
on_alarm()
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_dialog_action(data)
|
||||||
|
if data == -1 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local tab = {}
|
||||||
|
for i,v in ipairs(data) do
|
||||||
|
tab[i] = widgets.name[v]
|
||||||
|
end
|
||||||
|
prefs.widgets = tab
|
||||||
|
on_alarm()
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_settings()
|
||||||
|
dialogs:show_checkbox_dialog("Select widgets", widgets.label, indexes)
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_indexes(tab1,tab2)
|
||||||
|
local tab = {}
|
||||||
|
for i1,v1 in ipairs(tab1) do
|
||||||
|
for i2,v2 in ipairs(tab2) do
|
||||||
|
if v1 == v2 then
|
||||||
|
tab[i1] = i2
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return tab
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_buttons()
|
||||||
|
local enabled_color = "#1976d2"
|
||||||
|
local disabled_color = aio:colors().button
|
||||||
|
buttons,colors = {},{}
|
||||||
|
for i,v in ipairs(indexes) do
|
||||||
|
table.insert(buttons, "fa:" .. widgets.icon[v])
|
||||||
|
table.insert(colors, widgets.enabled[v] and enabled_color or disabled_color)
|
||||||
|
end
|
||||||
|
return buttons,colors
|
||||||
|
end
|
||||||
|
|
||||||
|
function move(x)
|
||||||
|
local tab = prefs.widgets
|
||||||
|
if (pos*x == -1) or (pos*x == #tab) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local cur = tab[pos]
|
||||||
|
tab[pos] = tab[pos+x]
|
||||||
|
tab[pos+x] = cur
|
||||||
|
prefs.widgets = tab
|
||||||
|
on_alarm()
|
||||||
|
end
|
||||||
|
|
||||||
|
function remove()
|
||||||
|
local tab = prefs.widgets
|
||||||
|
table.remove(tab,pos)
|
||||||
|
prefs.widgets = tab
|
||||||
|
on_alarm()
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_context_menu_click(menu_idx)
|
||||||
|
if menu_idx == 1 then
|
||||||
|
move(-1)
|
||||||
|
elseif menu_idx == 2 then
|
||||||
|
remove()
|
||||||
|
elseif menu_idx == 3 then
|
||||||
|
move(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_widget_action(action, name)
|
||||||
|
on_alarm()
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_pos()
|
||||||
|
local name = aio:self_name()
|
||||||
|
local tab = aio:active_widgets()
|
||||||
|
for _,v in ipairs(tab) do
|
||||||
|
if v.name == name then
|
||||||
|
return v.position+1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return 4
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_widgets()
|
||||||
|
local tab = {}
|
||||||
|
tab.icon = {}
|
||||||
|
tab.name = {}
|
||||||
|
tab.label = {}
|
||||||
|
tab.enabled = {}
|
||||||
|
for i,v in ipairs(aio:available_widgets()) do
|
||||||
|
if v.type == "builtin" then
|
||||||
|
table.insert(tab.icon, v.icon)
|
||||||
|
table.insert(tab.name, v.name)
|
||||||
|
table.insert(tab.label, v.label)
|
||||||
|
table.insert(tab.enabled, v.enabled)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return tab
|
||||||
|
end
|
||||||
70
defunct/chatgpt-search.lua
Normal file
70
defunct/chatgpt-search.lua
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
-- name = "ChatGPT"
|
||||||
|
-- description = "A search script that allows you to task with ChatGPT"
|
||||||
|
-- data_source = "openai.com"
|
||||||
|
-- type = "search"
|
||||||
|
-- author = "Evgeny Zobnin"
|
||||||
|
-- version = "1.0"
|
||||||
|
|
||||||
|
local json = require "json"
|
||||||
|
|
||||||
|
-- constants
|
||||||
|
local ok_color = aio:colors().primary_text
|
||||||
|
local error_color = aio:colors().progress_bad
|
||||||
|
local uri = "https://ai.fakeopen.com/v1/chat/completions"
|
||||||
|
local key = "pk-this-is-a-real-free-pool-token-for-everyone"
|
||||||
|
|
||||||
|
-- vars
|
||||||
|
local question = ""
|
||||||
|
local answer = ""
|
||||||
|
|
||||||
|
local payload_template = [[
|
||||||
|
{
|
||||||
|
"model": "gpt-3.5-turbo",
|
||||||
|
"temperature": 0.8,
|
||||||
|
"max_tokens": 100,
|
||||||
|
"messages": [
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "%%Q%%. Answer in one sentence."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]]
|
||||||
|
|
||||||
|
function on_search(input)
|
||||||
|
search:show_lines({"Ask ChatGPT: \""..input.."\""}, {ok_color}, true)
|
||||||
|
question = input
|
||||||
|
answer = ""
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click()
|
||||||
|
if answer == "" then
|
||||||
|
search:show_lines({"Waiting for answer..."}, {ok_color}, true)
|
||||||
|
make_request(question)
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
system:copy_to_clipboard(answer)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function make_request(str)
|
||||||
|
local payload = payload_template:replace("%%Q%%", str)
|
||||||
|
|
||||||
|
http:set_headers{ "Authorization: Bearer "..key }
|
||||||
|
http:post(uri, payload, "application/json")
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_network_result(result, code)
|
||||||
|
if code >= 200 and code < 300 then
|
||||||
|
answer = get_answer(result)
|
||||||
|
search:show_lines({answer}, {ok_color}, true)
|
||||||
|
else
|
||||||
|
search:show_lines({"Server error: "..code}, {error_color}, true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_answer(result)
|
||||||
|
local t = json.decode(result)
|
||||||
|
return t.choices[1].message.content
|
||||||
|
end
|
||||||
33
defunct/covid-widget.lua
Normal file
33
defunct/covid-widget.lua
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
-- name = "Covid info"
|
||||||
|
-- description = "Cases of illness and death from covid"
|
||||||
|
-- data_source = "covid19api.com"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
||||||
|
-- version = "1.0"
|
||||||
|
|
||||||
|
equals = "<font color=\""..ui:colors().secondary_text.."\"> = </font>"
|
||||||
|
|
||||||
|
function on_alarm()
|
||||||
|
http:get("https://api.covid19api.com/summary")
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_network_result(result, code)
|
||||||
|
if code >= 200 and code < 299 then
|
||||||
|
local new = ajson:get_value(result, "object object:Global int:NewConfirmed")
|
||||||
|
local total = ajson:get_value(result, "object object:Global int:TotalConfirmed")
|
||||||
|
local newDeaths = ajson:get_value(result, "object object:Global int:NewDeaths")
|
||||||
|
local totalDeaths = ajson:get_value(result, "object object:Global int:TotalDeaths")
|
||||||
|
|
||||||
|
ui:show_lines({
|
||||||
|
"<b>Disease</b> | total"..equals..comma_value(total).." | new"..equals..comma_value(new),
|
||||||
|
"<b>Deaths</b> | total"..equals..comma_value(totalDeaths).." | new"..equals..comma_value(newDeaths)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- credit http://richard.warburton.it
|
||||||
|
function comma_value(n)
|
||||||
|
local left,num,right = string.match(n,'^([^%d]*%d)(%d*)(.-)$')
|
||||||
|
return left..(num:reverse():gsub('(%d%d%d)','%1,'):reverse())..right
|
||||||
|
end
|
||||||
|
|
||||||
128
defunct/google-search-app-widget.lua
Normal file
128
defunct/google-search-app-widget.lua
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
-- name = "Google search"
|
||||||
|
-- description = "AIO wrapper for the Google search app widget - open widget settings for options"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Theodor Galanis (t.me/TheodorGalanis)"
|
||||||
|
-- version = "2.8"
|
||||||
|
-- foldable = "false"
|
||||||
|
-- uses_app = "com.google.android.googlequicksearchbox"
|
||||||
|
|
||||||
|
local prefs = require "prefs"
|
||||||
|
|
||||||
|
local w_bridge = nil
|
||||||
|
local indices = {}
|
||||||
|
|
||||||
|
function on_alarm()
|
||||||
|
if not widgets:bound(prefs.wid) then
|
||||||
|
setup_app_widget()
|
||||||
|
end
|
||||||
|
if not prefs.mode then
|
||||||
|
prefs.mode = 1
|
||||||
|
end
|
||||||
|
mode = prefs.mode
|
||||||
|
indices = set_indices()
|
||||||
|
widgets:request_updates(prefs.wid, "4x1")
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_app_widget_updated(bridge)
|
||||||
|
w_bridge = bridge
|
||||||
|
local tab = {
|
||||||
|
{"button", "fa:magnifying-glass", {expand = true}},
|
||||||
|
{"spacer", 2},
|
||||||
|
{"button", "fa:sun-cloud"},
|
||||||
|
{"spacer", 2},
|
||||||
|
{"button", "fa:asterisk"},
|
||||||
|
{"spacer", 2},
|
||||||
|
{"button", "fa:microphone"},
|
||||||
|
{"spacer", 2},
|
||||||
|
{"button", "fa:camera"}
|
||||||
|
}
|
||||||
|
|
||||||
|
tab = set_gui(tab)
|
||||||
|
my_gui = gui(tab)
|
||||||
|
my_gui.render()
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
if idx == indices[1] then
|
||||||
|
w_bridge:click("image_2")
|
||||||
|
elseif idx == indices[2] then
|
||||||
|
intent:start_activity(open_weather())
|
||||||
|
elseif idx == indices[3] then
|
||||||
|
w_bridge:click("image_3")
|
||||||
|
elseif idx == indices[4] then
|
||||||
|
w_bridge:click("image_5")
|
||||||
|
elseif idx == indices[5] then
|
||||||
|
w_bridge:click("image_6")
|
||||||
|
else return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_settings()
|
||||||
|
local tab = {"Left-handed mode with weather", "Left-handed mode, no weather", "Right-handed mode with weather", "Right-handed mode, no weather" }
|
||||||
|
dialogs:show_radio_dialog("Select mode", tab, mode)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_long_click(idx)
|
||||||
|
if idx == indices[1] then
|
||||||
|
ui:show_toast("Google search")
|
||||||
|
elseif idx == indices[2] then
|
||||||
|
ui:show_toast("Google weather")
|
||||||
|
elseif idx == indices[3] then
|
||||||
|
ui:show_toast("Google discover")
|
||||||
|
elseif idx == indices[4] then
|
||||||
|
ui:show_toast("Google voice search")
|
||||||
|
elseif idx == indices[5] then
|
||||||
|
ui:show_toast("Google Lens")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_dialog_action(data)
|
||||||
|
if data == -1 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
prefs.mode = data
|
||||||
|
on_alarm()
|
||||||
|
end
|
||||||
|
|
||||||
|
function setup_app_widget()
|
||||||
|
local id = widgets:setup("com.google.android.googlequicksearchbox/com.google.android.googlequicksearchbox.SearchWidgetProvider")
|
||||||
|
|
||||||
|
if (id ~= nil) then
|
||||||
|
prefs.wid = id
|
||||||
|
else
|
||||||
|
ui:show_text("Can't add widget")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function set_indices()
|
||||||
|
local temp = {1, 3, 5, 7, 9}
|
||||||
|
if mode == 2 then
|
||||||
|
temp = {1, 9, 3, 5, 7}
|
||||||
|
elseif mode == 3 then
|
||||||
|
temp = reverse (temp)
|
||||||
|
elseif mode == 4 then
|
||||||
|
temp = {7, 9, 5, 3, 1}
|
||||||
|
end
|
||||||
|
return temp
|
||||||
|
end
|
||||||
|
|
||||||
|
function set_gui(tab)
|
||||||
|
local temp = tab
|
||||||
|
if mode == 2 or mode == 4 then
|
||||||
|
table.remove(tab, 3)
|
||||||
|
table.remove(tab, 3)
|
||||||
|
end
|
||||||
|
if mode > 2 then
|
||||||
|
temp = reverse(temp)
|
||||||
|
end
|
||||||
|
return temp
|
||||||
|
end
|
||||||
|
|
||||||
|
function open_weather()
|
||||||
|
local tab ={}
|
||||||
|
tab.category = "MAIN"
|
||||||
|
tab.package = "com.google.android.googlequicksearchbox"
|
||||||
|
tab.component = "com.google.android.googlequicksearchbox/com.google.android.apps.search.weather.WeatherExportedActivity"
|
||||||
|
return tab
|
||||||
|
end
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
-- name = "Chuck Norris translated"
|
-- name = "Chuck Norris jokes"
|
||||||
-- description = "Jokes with translation"
|
-- description = "Jokes with translation"
|
||||||
-- data_source = "icndb.com"
|
-- data_source = "icndb.com"
|
||||||
-- type = "widget"
|
-- type = "widget"
|
||||||
@@ -10,11 +10,13 @@ function on_alarm()
|
|||||||
http:get("https://api.quotable.io/random")
|
http:get("https://api.quotable.io/random")
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_network_result(result)
|
function on_network_result(result, code)
|
||||||
quote = ajson:get_value(result, "object string:content")
|
if code >= 200 and code < 299 then
|
||||||
author = ajson:get_value(result, "object string:author")
|
quote = ajson:get_value(result, "object string:content")
|
||||||
|
author = ajson:get_value(result, "object string:author")
|
||||||
|
|
||||||
ui:show_lines({ quote }, { author })
|
ui:show_lines({ quote }, { author })
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_click()
|
function on_click()
|
||||||
23
dev/aioactionslist-widget.lua
Normal file
23
dev/aioactionslist-widget.lua
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
-- name = "AΙΟ actions list"
|
||||||
|
-- type = "widget"
|
||||||
|
-- description = "Shows actions returned by aio:actions() function"
|
||||||
|
--foldable = "true"
|
||||||
|
-- author = "Theodor Galanis"
|
||||||
|
-- version = "1"
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
actions = aio:actions()
|
||||||
|
local labels = ""
|
||||||
|
|
||||||
|
labels = map(actions, function(it) return it.label end)
|
||||||
|
names = map(actions, function(it) return it.name end)
|
||||||
|
ui:show_lines(names, labels)
|
||||||
|
end
|
||||||
|
|
||||||
|
function map(tbl, f)
|
||||||
|
local ret = {}
|
||||||
|
for k,v in pairs(tbl) do
|
||||||
|
ret[k] = f(v)
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
23
dev/aiocolors-widget.lua
Normal file
23
dev/aiocolors-widget.lua
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
-- name = "AΙΟ colors"
|
||||||
|
-- type = "widget"
|
||||||
|
-- description = "Shows colors returned by aio:colors() function"
|
||||||
|
--foldable = "true"
|
||||||
|
-- author = "Theodor Galanis"
|
||||||
|
-- version = "1"
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
local colors = aio:colors()
|
||||||
|
local colors_strings = stringify_table(colors)
|
||||||
|
|
||||||
|
ui:show_lines(colors_strings)
|
||||||
|
end
|
||||||
|
|
||||||
|
function stringify_table(tab)
|
||||||
|
local new_tab = {}
|
||||||
|
|
||||||
|
for k,v in pairs(tab) do
|
||||||
|
table.insert(new_tab, k..": "..tostring(v))
|
||||||
|
end
|
||||||
|
|
||||||
|
return new_tab
|
||||||
|
end
|
||||||
23
dev/aiowidgetslist-widget.lua
Normal file
23
dev/aiowidgetslist-widget.lua
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
-- name = "AΙΟ widgets list"
|
||||||
|
-- type = "widget"
|
||||||
|
-- description = "Shows widgets returned by aio:available_widgets() function"
|
||||||
|
--foldable = "true"
|
||||||
|
-- author = "Theodor Galanis"
|
||||||
|
-- version = "1"
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
actions = aio:available_widgets()
|
||||||
|
local labels = ""
|
||||||
|
|
||||||
|
labels = map(actions, function(it) return it.label end)
|
||||||
|
names = map(actions, function(it) return it.name end)
|
||||||
|
ui:show_lines(names, labels)
|
||||||
|
end
|
||||||
|
|
||||||
|
function map(tbl, f)
|
||||||
|
local ret = {}
|
||||||
|
for k,v in pairs(tbl) do
|
||||||
|
ret[k] = f(v)
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
59
dev/android-widget-dumper.lua
Normal file
59
dev/android-widget-dumper.lua
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
-- name = "Android widgets dumper"
|
||||||
|
-- foldable = false
|
||||||
|
|
||||||
|
-- Place app package name with widget here
|
||||||
|
app_pkg = "com.google.android.googlequicksearchbox"
|
||||||
|
--app_pkg = "com.weather.Weather"
|
||||||
|
--app_pkg = "com.google.android.apps.tasks"
|
||||||
|
--app_pkg = "com.android.chrome"
|
||||||
|
--app_pkg = "com.whatsapp"
|
||||||
|
|
||||||
|
-- Widget size (string from "1x1" to "4x4")
|
||||||
|
-- In most cases you can use nil
|
||||||
|
widget_size = "4x1"
|
||||||
|
|
||||||
|
-- Globals
|
||||||
|
labels = {}
|
||||||
|
providers = {}
|
||||||
|
dump = ""
|
||||||
|
wid = -1
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
local list = widgets:list(app_pkg)
|
||||||
|
|
||||||
|
if list == nil then
|
||||||
|
ui:show_text("Error: No widgets")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
labels = map(function(it) return it.label end, list)
|
||||||
|
providers = map(function(it) return it.provider end, list)
|
||||||
|
|
||||||
|
w_content = ""
|
||||||
|
|
||||||
|
if wid < 0 then
|
||||||
|
ui:show_lines(labels)
|
||||||
|
else
|
||||||
|
widgets:request_updates(wid, widget_size)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
if w_content == "" then
|
||||||
|
wid = widgets:setup(providers[idx])
|
||||||
|
widgets:request_updates(wid, widget_size)
|
||||||
|
else
|
||||||
|
system:copy_to_clipboard(w_content)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_app_widget_updated(bridge)
|
||||||
|
local provider = bridge:provider()
|
||||||
|
local dump = bridge:dump_tree()
|
||||||
|
local colors = bridge:dump_colors()
|
||||||
|
w_content = provider.."\n\n"..dump.."\n\n"..serialize(colors)
|
||||||
|
|
||||||
|
ui:show_text("%%txt%%"..w_content)
|
||||||
|
debug:log("dump:\n\n"..w_content)
|
||||||
|
end
|
||||||
|
|
||||||
3
env
Normal file
3
env
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
REPOS="main ru samples community dev"
|
||||||
|
SCRIPTS_DIR="/sdcard/Android/data/ru.execbit.aiolauncher/files/"
|
||||||
|
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
REPOS="main ru samples community"
|
source ./env
|
||||||
SCRIPTS_DIR="/sdcard/Android/data/ru.execbit.aiolauncher/files/"
|
|
||||||
|
|
||||||
adb shell rm -rf $SCRIPTS_DIR/*.lua
|
./rm-scripts.sh
|
||||||
|
|
||||||
for repo in $REPOS; do
|
for repo in $REPOS; do
|
||||||
adb push $repo/*.lua $SCRIPTS_DIR
|
adb push $repo/*.lua $SCRIPTS_DIR
|
||||||
|
|||||||
283
lib/utils.lua
283
lib/utils.lua
@@ -1,5 +1,25 @@
|
|||||||
-- Standard AIO Launcher library
|
-- Standard AIO Launcher library
|
||||||
|
|
||||||
|
function string:trim()
|
||||||
|
if #self == 0 then return self end
|
||||||
|
|
||||||
|
return self:match("^%s*(.-)%s*$")
|
||||||
|
end
|
||||||
|
|
||||||
|
function string:starts_with(start_str)
|
||||||
|
if #self == 0 or #self < #start_str then return false end
|
||||||
|
if start_str == nil or start_str == "" then return true end
|
||||||
|
|
||||||
|
return self:sub(1, #start_str) == start_str
|
||||||
|
end
|
||||||
|
|
||||||
|
function string:ends_with(end_str)
|
||||||
|
if #self == 0 or #self < #end_str then return false end
|
||||||
|
if end_str == nil or end_str == "" then return true end
|
||||||
|
|
||||||
|
return self:sub(-#end_str) == end_str
|
||||||
|
end
|
||||||
|
|
||||||
function string:split(sep)
|
function string:split(sep)
|
||||||
if sep == nil then
|
if sep == nil then
|
||||||
sep = "%s"
|
sep = "%s"
|
||||||
@@ -29,7 +49,7 @@ function slice(tbl, s, e)
|
|||||||
return new
|
return new
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_index(tab, val)
|
function index(tab, val)
|
||||||
for index, value in ipairs(tab) do
|
for index, value in ipairs(tab) do
|
||||||
if value == val then
|
if value == val then
|
||||||
return index
|
return index
|
||||||
@@ -39,7 +59,7 @@ function get_index(tab, val)
|
|||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_key(tab, val)
|
function key(tab, val)
|
||||||
for index, value in pairs(tab) do
|
for index, value in pairs(tab) do
|
||||||
if value == val then
|
if value == val then
|
||||||
return index
|
return index
|
||||||
@@ -49,6 +69,65 @@ function get_key(tab, val)
|
|||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function concat(t1, t2)
|
||||||
|
for _,v in ipairs(t2) do
|
||||||
|
table.insert(t1, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function contains(table, val)
|
||||||
|
for i=1, #table do
|
||||||
|
if table[i] == val then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
function reverse(tab)
|
||||||
|
for i = 1, math.floor(#tab/2) do
|
||||||
|
tab[i], tab[#tab-i+1] = tab[#tab-i+1], tab[i]
|
||||||
|
end
|
||||||
|
return tab
|
||||||
|
end
|
||||||
|
|
||||||
|
function serialize(tab, ind)
|
||||||
|
ind = ind and (ind .. " ") or " "
|
||||||
|
local nl = "\n"
|
||||||
|
local str = "{" .. nl
|
||||||
|
for k, v in pairs(tab) do
|
||||||
|
local pr = (type(k)=="string") and ("[\"" .. k .. "\"] = ") or ""
|
||||||
|
str = str .. ind .. pr
|
||||||
|
if type(v) == "table" then
|
||||||
|
str = str .. serialize(v, ind) .. ",\n"
|
||||||
|
elseif type(v) == "string" then
|
||||||
|
str = str .. "\"" .. tostring(v) .. "\",\n"
|
||||||
|
elseif type(v) == "number" or type(v) == "boolean" then
|
||||||
|
str = str .. tostring(v) .. ",\n"
|
||||||
|
else
|
||||||
|
str = str .. "[[" .. tostring(v) .. "]],\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
str = str:gsub(".$","")
|
||||||
|
str = str .. nl .. ind .. "}"
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
|
function deep_copy(orig)
|
||||||
|
local orig_type = type(orig)
|
||||||
|
local copy
|
||||||
|
if orig_type == 'table' then
|
||||||
|
copy = {}
|
||||||
|
for orig_key, orig_value in next, orig, nil do
|
||||||
|
copy[deep_copy(orig_key)] = deep_copy(orig_value)
|
||||||
|
end
|
||||||
|
setmetatable(copy, deep_copy(getmetatable(orig)))
|
||||||
|
else
|
||||||
|
copy = orig
|
||||||
|
end
|
||||||
|
return copy
|
||||||
|
end
|
||||||
|
|
||||||
function round(x, n)
|
function round(x, n)
|
||||||
local n = math.pow(10, n or 0)
|
local n = math.pow(10, n or 0)
|
||||||
local x = x * n
|
local x = x * n
|
||||||
@@ -59,10 +138,208 @@ end
|
|||||||
function use(module, ...)
|
function use(module, ...)
|
||||||
for k,v in pairs(module) do
|
for k,v in pairs(module) do
|
||||||
if _G[k] then
|
if _G[k] then
|
||||||
io.stderr:write("use: skipping duplicate symbol ", k, "\n")
|
print("use: skipping duplicate symbol ", k, "\n")
|
||||||
else
|
else
|
||||||
_G[k] = module[k]
|
_G[k] = module[k]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function for_each(tbl, callback)
|
||||||
|
for index, value in ipairs(tbl) do
|
||||||
|
callback(value, index, tbl)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Deprecated
|
||||||
|
function get_index(tab, val)
|
||||||
|
return index(tab, val)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Deprecated
|
||||||
|
function get_key(tab, val)
|
||||||
|
return key(tab, val)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Deprecated
|
||||||
|
function concat_tables(t1, t2)
|
||||||
|
concat(t1, t2)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Functional Library
|
||||||
|
--
|
||||||
|
-- @file functional.lua
|
||||||
|
-- @author Shimomura Ikkei
|
||||||
|
-- @date 2005/05/18
|
||||||
|
--
|
||||||
|
-- @brief porting several convenience functional utilities form Haskell,Python etc..
|
||||||
|
|
||||||
|
-- map(function, table)
|
||||||
|
-- e.g: map(double, {1,2,3}) -> {2,4,6}
|
||||||
|
function map(func, tbl)
|
||||||
|
local newtbl = {}
|
||||||
|
for i,v in pairs(tbl) do
|
||||||
|
newtbl[i] = func(v)
|
||||||
|
end
|
||||||
|
return newtbl
|
||||||
|
end
|
||||||
|
|
||||||
|
-- filter(function, table)
|
||||||
|
-- e.g: filter(is_even, {1,2,3,4}) -> {2,4}
|
||||||
|
function filter(func, tbl)
|
||||||
|
local newtbl= {}
|
||||||
|
for i,v in pairs(tbl) do
|
||||||
|
if func(v) then
|
||||||
|
newtbl[i]=v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return newtbl
|
||||||
|
end
|
||||||
|
|
||||||
|
-- skip(table, N)
|
||||||
|
-- e.g: skip({1,2,3,4}, 2) -> {3,4}
|
||||||
|
function skip(tbl, N)
|
||||||
|
local result = {}
|
||||||
|
for i = N+1, #tbl do
|
||||||
|
table.insert(result, tbl[i])
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- take(table, N)
|
||||||
|
-- e.g: take({1,2,3,4}, 2) -> {1,2}
|
||||||
|
function take(tbl, N)
|
||||||
|
local result = {}
|
||||||
|
for i = 1, N do
|
||||||
|
if tbl[i] == nil then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
table.insert(result, tbl[i])
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
-- head(table)
|
||||||
|
-- e.g: head({1,2,3}) -> 1
|
||||||
|
function head(tbl)
|
||||||
|
return tbl[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- tail(table)
|
||||||
|
-- e.g: tail({1,2,3}) -> {2,3}
|
||||||
|
--
|
||||||
|
-- XXX This is a BAD and ugly implementation.
|
||||||
|
-- should return the address to next porinter, like in C (arr+1)
|
||||||
|
function tail(tbl)
|
||||||
|
if #tbl < 1 then
|
||||||
|
return nil
|
||||||
|
else
|
||||||
|
local newtbl = {}
|
||||||
|
local i = 2
|
||||||
|
while (i <= #tbl) do
|
||||||
|
table.insert(newtbl, i-1, tbl[i])
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
return newtbl
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- foldr(function, default_value, table)
|
||||||
|
-- e.g: foldr(operator.mul, 1, {1,2,3,4,5}) -> 120
|
||||||
|
function foldr(func, val, tbl)
|
||||||
|
for i,v in pairs(tbl) do
|
||||||
|
val = func(val, v)
|
||||||
|
end
|
||||||
|
return val
|
||||||
|
end
|
||||||
|
|
||||||
|
-- reduce(function, table)
|
||||||
|
-- e.g: reduce(operator.add, {1,2,3,4}) -> 10
|
||||||
|
function reduce(func, tbl)
|
||||||
|
return foldr(func, head(tbl), tail(tbl))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- curry(f,g)
|
||||||
|
-- e.g: printf = curry(io.write, string.format)
|
||||||
|
-- -> function(...) return io.write(string.format(unpack(arg))) end
|
||||||
|
function curry(f, g)
|
||||||
|
return function (...)
|
||||||
|
return f(g(table.unpack({...})))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- bind1(func, binding_value_for_1st)
|
||||||
|
-- bind2(func, binding_value_for_2nd)
|
||||||
|
-- @brief
|
||||||
|
-- Binding argument(s) and generate new function.
|
||||||
|
-- @see also STL's functional, Boost's Lambda, Combine, Bind.
|
||||||
|
-- @examples
|
||||||
|
-- local mul5 = bind1(operator.mul, 5) -- mul5(10) is 5 * 10
|
||||||
|
-- local sub2 = bind2(operator.sub, 2) -- sub2(5) is 5 -2
|
||||||
|
function bind1(func, val1)
|
||||||
|
return function (val2)
|
||||||
|
return func(val1, val2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function bind2(func, val2) -- bind second argument.
|
||||||
|
return function (val1)
|
||||||
|
return func(val1, val2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- is(checker_function, expected_value)
|
||||||
|
-- @brief
|
||||||
|
-- check function generator. return the function to return boolean,
|
||||||
|
-- if the condition was expected then true, else false.
|
||||||
|
-- @example
|
||||||
|
-- local is_table = is(type, "table")
|
||||||
|
-- local is_even = is(bind2(math.mod, 2), 1)
|
||||||
|
-- local is_odd = is(bind2(math.mod, 2), 0)
|
||||||
|
is = function(check, expected)
|
||||||
|
return function (...)
|
||||||
|
if (check(table.unpack({...})) == expected) then
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- operator table.
|
||||||
|
-- @see also python's operator module.
|
||||||
|
operator = {
|
||||||
|
mod = math.mod;
|
||||||
|
pow = math.pow;
|
||||||
|
add = function(n,m) return n + m end;
|
||||||
|
sub = function(n,m) return n - m end;
|
||||||
|
mul = function(n,m) return n * m end;
|
||||||
|
div = function(n,m) return n / m end;
|
||||||
|
gt = function(n,m) return n > m end;
|
||||||
|
lt = function(n,m) return n < m end;
|
||||||
|
eq = function(n,m) return n == m end;
|
||||||
|
le = function(n,m) return n <= m end;
|
||||||
|
ge = function(n,m) return n >= m end;
|
||||||
|
ne = function(n,m) return n ~= m end;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
-- enumFromTo(from, to)
|
||||||
|
-- e.g: enumFromTo(1, 10) -> {1,2,3,4,5,6,7,8,9}
|
||||||
|
-- TODO How to lazy evaluate in Lua? (thinking with coroutine)
|
||||||
|
enumFromTo = function (from,to)
|
||||||
|
local newtbl = {}
|
||||||
|
local step = bind2(operator[(from < to) and "add" or "sub"], 1)
|
||||||
|
local val = from
|
||||||
|
while val <= to do
|
||||||
|
table.insert(newtbl, table.getn(newtbl)+1, val)
|
||||||
|
val = step(val)
|
||||||
|
end
|
||||||
|
return newtbl
|
||||||
|
end
|
||||||
|
|
||||||
|
-- make function to take variant arguments, replace of a table.
|
||||||
|
-- this does not mean expand the arguments of function took,
|
||||||
|
-- it expand the function's spec: function(tbl) -> function(...)
|
||||||
|
function expand_args(func)
|
||||||
|
return function(...) return func(arg) end
|
||||||
|
end
|
||||||
|
|||||||
@@ -2,17 +2,12 @@
|
|||||||
-- description = "Simple battery info widget"
|
-- description = "Simple battery info widget"
|
||||||
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
||||||
|
|
||||||
ticks = -1
|
function on_tick(ticks)
|
||||||
|
|
||||||
function on_tick()
|
|
||||||
-- Update one time per 10 seconds
|
-- Update one time per 10 seconds
|
||||||
ticks = ticks + 1
|
|
||||||
if ticks % 10 ~= 0 then
|
if ticks % 10 ~= 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
ticks = 0
|
|
||||||
|
|
||||||
local batt_info = system:battery_info()
|
local batt_info = system:battery_info()
|
||||||
local batt_strings = stringify_table(batt_info)
|
local batt_strings = stringify_table(batt_info)
|
||||||
local folded_str = "Battery: "..batt_info.percent.."% | "..batt_info.temp.."° | "..batt_info.voltage.." mV"
|
local folded_str = "Battery: "..batt_info.percent.."% | "..batt_info.temp.."° | "..batt_info.voltage.." mV"
|
||||||
|
|||||||
70
main/calendar-menu.lua
Normal file
70
main/calendar-menu.lua
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
-- name = "Calendar menu"
|
||||||
|
-- name_id = "calendar"
|
||||||
|
-- description = "Shows events from system calendar"
|
||||||
|
-- type = "drawer"
|
||||||
|
-- aio_version = "4.7.99"
|
||||||
|
-- author = "Evgeny Zobnin"
|
||||||
|
-- version = "1.1"
|
||||||
|
|
||||||
|
local fmt = require "fmt"
|
||||||
|
|
||||||
|
local have_permission = false
|
||||||
|
local events = {}
|
||||||
|
local calendars = {}
|
||||||
|
|
||||||
|
function on_drawer_open()
|
||||||
|
events = calendar:events()
|
||||||
|
calendars = calendar:calendars()
|
||||||
|
|
||||||
|
if events == "permission_error" then
|
||||||
|
calendar:request_permission()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
have_permission = true
|
||||||
|
|
||||||
|
add_cal_colors(events, calendars)
|
||||||
|
|
||||||
|
if #events == #drawer:items() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
lines = map(function(it)
|
||||||
|
local date = fmt.colored(format_date(it.begin), it.calendar_color)
|
||||||
|
return date..fmt.space(4)..it.title
|
||||||
|
end, events)
|
||||||
|
|
||||||
|
drawer:show_ext_list(lines)
|
||||||
|
end
|
||||||
|
|
||||||
|
function add_cal_colors(events, cals)
|
||||||
|
for i, event in ipairs(events) do
|
||||||
|
for _, cal in ipairs(cals) do
|
||||||
|
if event.calendar_id == cal.id then
|
||||||
|
event.calendar_color = cal.color
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function format_date(date)
|
||||||
|
if system.format_date_localized then
|
||||||
|
return system:format_date_localized("dd.MM", date)
|
||||||
|
else
|
||||||
|
return os.date("%d.%m", date)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
if not have_permission then return end
|
||||||
|
|
||||||
|
calendar:show_event_dialog(events[idx].id)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_long_click(idx)
|
||||||
|
if not have_permission then return end
|
||||||
|
|
||||||
|
calendar:open_event(events[idx].id)
|
||||||
|
end
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
-- description = "Monthly calendar with system calendar events"
|
-- description = "Monthly calendar with system calendar events"
|
||||||
-- type = "widget"
|
-- type = "widget"
|
||||||
-- author = "Andrey Gavrilov"
|
-- author = "Andrey Gavrilov"
|
||||||
-- version = "3.1"
|
-- version = "3.2"
|
||||||
|
|
||||||
local tab = {}
|
local tab = {}
|
||||||
local line = " "
|
local line = " "
|
||||||
@@ -15,12 +15,11 @@ local month = os.date("%m"):gsub("^0","")
|
|||||||
local day = os.date("%d"):gsub("^0","")
|
local day = os.date("%d"):gsub("^0","")
|
||||||
|
|
||||||
function on_resume()
|
function on_resume()
|
||||||
--ui:set_folding_flag(true)
|
if calendar:events(0,0) == "permission_error" then
|
||||||
ui:show_table(table_to_tables(tab,8),0, true, line)
|
widget_type = "text"
|
||||||
widget_type = "table"
|
ui:show_text("Click to grant permission")
|
||||||
end
|
return
|
||||||
|
end
|
||||||
function on_alarm()
|
|
||||||
if next(settings:get()) == nil then
|
if next(settings:get()) == nil then
|
||||||
settings:set(get_all_cals()[2])
|
settings:set(get_all_cals()[2])
|
||||||
end
|
end
|
||||||
@@ -33,7 +32,7 @@ end
|
|||||||
|
|
||||||
function on_settings()
|
function on_settings()
|
||||||
dialog_id = "settings"
|
dialog_id = "settings"
|
||||||
ui:show_checkbox_dialog("Check calendars", get_all_cals()[3],cal_id_to_id(settings:get()))
|
dialogs:show_checkbox_dialog("Check calendars", get_all_cals()[3],cal_id_to_id(settings:get()))
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_click(i)
|
function on_click(i)
|
||||||
@@ -43,16 +42,16 @@ function on_click(i)
|
|||||||
local time = os.time{year=year,month=month,day=1}-24*60*60
|
local time = os.time{year=year,month=month,day=1}-24*60*60
|
||||||
year,month = os.date("%Y-%m",time):match("(%d+)-(%d+)")
|
year,month = os.date("%Y-%m",time):match("(%d+)-(%d+)")
|
||||||
year,month = year:gsub("^0",""),month:gsub("^0","")
|
year,month = year:gsub("^0",""),month:gsub("^0","")
|
||||||
on_alarm()
|
on_resume()
|
||||||
elseif i == 8 then
|
elseif i == 8 then
|
||||||
system:vibrate(10)
|
system:vibrate(10)
|
||||||
local time = os.time{year=year,month=month,day=1}+31*24*60*60
|
local time = os.time{year=year,month=month,day=1}+31*24*60*60
|
||||||
year,month = os.date("%Y-%m",time):match("(%d+)-(%d+)")
|
year,month = os.date("%Y-%m",time):match("(%d+)-(%d+)")
|
||||||
year,month = year:gsub("^0",""),month:gsub("^0","")
|
year,month = year:gsub("^0",""),month:gsub("^0","")
|
||||||
on_alarm()
|
on_resume()
|
||||||
elseif i > 1 and i < 8 then
|
elseif i > 1 and i < 8 then
|
||||||
dialog_id = "date"
|
dialog_id = "date"
|
||||||
ui:show_edit_dialog("Enter month and year", "Format - 12.2020. Empty value - current month", string.format("%02d.%04d", month, year))
|
dialogs:show_edit_dialog("Enter month and year", "Format - 12.2020. Empty value - current month", string.format("%02d.%04d", month, year))
|
||||||
return
|
return
|
||||||
elseif (i-1)%8 ~= 0 and tab[i] ~= " " then
|
elseif (i-1)%8 ~= 0 and tab[i] ~= " " then
|
||||||
day = tab[i]:match(">(%d+)<"):gsub("^0","")
|
day = tab[i]:match(">(%d+)<"):gsub("^0","")
|
||||||
@@ -72,9 +71,15 @@ function on_click(i)
|
|||||||
widget_type = "table"
|
widget_type = "table"
|
||||||
ui:show_table(table_to_tables(tab,8),0, true, line)
|
ui:show_table(table_to_tables(tab,8),0, true, line)
|
||||||
end
|
end
|
||||||
|
elseif widget_type == "text" then
|
||||||
|
calendar:request_permission()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function on_permission_granted()
|
||||||
|
on_resume()
|
||||||
|
end
|
||||||
|
|
||||||
function on_dialog_action(data)
|
function on_dialog_action(data)
|
||||||
if data == -1 then
|
if data == -1 then
|
||||||
events = {}
|
events = {}
|
||||||
@@ -90,7 +95,7 @@ function on_dialog_action(data)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
year,month = y,m
|
year,month = y,m
|
||||||
on_alarm()
|
on_resume()
|
||||||
return
|
return
|
||||||
elseif not check_date(data) then
|
elseif not check_date(data) then
|
||||||
return
|
return
|
||||||
@@ -103,15 +108,15 @@ function on_dialog_action(data)
|
|||||||
end
|
end
|
||||||
month,year = data:match("(%d+)%.(%d+)")
|
month,year = data:match("(%d+)%.(%d+)")
|
||||||
month,year = month:gsub("^0",""),year:gsub("^0","")
|
month,year = month:gsub("^0",""),year:gsub("^0","")
|
||||||
on_alarm()
|
on_resume()
|
||||||
elseif dialog_id == "settings" then
|
elseif dialog_id == "settings" then
|
||||||
settings:set(id_to_cal_id(data))
|
settings:set(id_to_cal_id(data))
|
||||||
on_alarm()
|
on_resume()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_cal(y,m)
|
function get_cal(y,m)
|
||||||
local color = ui:colors()
|
local color = aio:colors()
|
||||||
local events = get_my_events(y,m,0)
|
local events = get_my_events(y,m,0)
|
||||||
local from = os.time{year=y,month=m,day=1}
|
local from = os.time{year=y,month=m,day=1}
|
||||||
local tab = {
|
local tab = {
|
||||||
@@ -146,7 +151,7 @@ function get_cal(y,m)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function format_day(y,m,d,events)
|
function format_day(y,m,d,events)
|
||||||
local color = ui:colors()
|
local color = aio:colors()
|
||||||
local from = os.time{year=y,month=m,day=d,hour=0,min=0,sec=0}
|
local from = os.time{year=y,month=m,day=d,hour=0,min=0,sec=0}
|
||||||
local to = os.time{year=y,month=m,day=d,hour=23,min=59,sec=59}
|
local to = os.time{year=y,month=m,day=d,hour=23,min=59,sec=59}
|
||||||
local yes = false
|
local yes = false
|
||||||
@@ -163,7 +168,7 @@ function format_day(y,m,d,events)
|
|||||||
end
|
end
|
||||||
if year == os.date("%Y"):gsub("^0","") and month == os.date("%m"):gsub("^0","") and d == os.date("%d"):gsub("^0","") then
|
if year == os.date("%Y"):gsub("^0","") and month == os.date("%m"):gsub("^0","") and d == os.date("%d"):gsub("^0","") then
|
||||||
dd = "<font color=\""..color.progress_good.."\">"..dd.."</font>"
|
dd = "<font color=\""..color.progress_good.."\">"..dd.."</font>"
|
||||||
elseif os.date("%w",from):gsub("0","7")-5 > 0 then
|
elseif calendar.is_holiday and calendar:is_holiday(os.time{year=y,month=m,day=d}) then
|
||||||
dd = "<font color=\""..color.progress_bad.."\">"..dd.."</font>"
|
dd = "<font color=\""..color.progress_bad.."\">"..dd.."</font>"
|
||||||
else
|
else
|
||||||
dd = "<font color=\""..color.primary_text.."\">"..dd.."</font>"
|
dd = "<font color=\""..color.primary_text.."\">"..dd.."</font>"
|
||||||
@@ -256,7 +261,7 @@ function get_day_tab(events)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function get_lines(events)
|
function get_lines(events)
|
||||||
local color = ui:colors()
|
local color = aio:colors()
|
||||||
local lines = {}
|
local lines = {}
|
||||||
for i,v in ipairs(events) do
|
for i,v in ipairs(events) do
|
||||||
table.insert(lines,"<font color = \""..v[9].."\">•</font>")
|
table.insert(lines,"<font color = \""..v[9].."\">•</font>")
|
||||||
|
|||||||
72
main/contacts-menu.lua
Normal file
72
main/contacts-menu.lua
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
-- name = "Contacts menu"
|
||||||
|
-- name_id = "contacts"
|
||||||
|
-- description = "Shows phone contacts in the side menu"
|
||||||
|
-- type = "drawer"
|
||||||
|
-- aio_version = "4.7.99"
|
||||||
|
-- author = "Evgeny Zobnin"
|
||||||
|
-- version = "1.0"
|
||||||
|
|
||||||
|
local have_permission = false
|
||||||
|
|
||||||
|
function on_drawer_open()
|
||||||
|
local raw_contacts = phone:contacts()
|
||||||
|
|
||||||
|
if raw_contacts == "permission_error" then
|
||||||
|
phone:request_permission()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
have_permission = true
|
||||||
|
|
||||||
|
contacts = distinct_by_name(
|
||||||
|
sort_by_name(phone:contacts())
|
||||||
|
)
|
||||||
|
|
||||||
|
if #contacts == #drawer:items() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
names = map(contacts, function(it) return it.name end)
|
||||||
|
keys = map(contacts, function(it) return it.lookup_key end)
|
||||||
|
|
||||||
|
icons = map(contacts, function(it)
|
||||||
|
if it.icon ~= nil then
|
||||||
|
return it.icon
|
||||||
|
else
|
||||||
|
return "fa:phone" -- Backward compatibility
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
drawer:show_list(names, icons, nil, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
if not have_permission then return end
|
||||||
|
|
||||||
|
phone:show_contact_dialog(contacts[idx].lookup_key)
|
||||||
|
end
|
||||||
|
|
||||||
|
function map(tbl, f)
|
||||||
|
local ret = {}
|
||||||
|
for k,v in pairs(tbl) do
|
||||||
|
ret[k] = f(v)
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
|
|
||||||
|
function sort_by_name(tbl)
|
||||||
|
table.sort(tbl, function(a,b) return a.name:lower() < b.name:lower() end)
|
||||||
|
return tbl
|
||||||
|
end
|
||||||
|
|
||||||
|
function distinct_by_name(tbl)
|
||||||
|
local ret = {}
|
||||||
|
local names = {}
|
||||||
|
for _, contact in ipairs(tbl) do
|
||||||
|
if not names[contact.name] then
|
||||||
|
table.insert(ret, contact)
|
||||||
|
end
|
||||||
|
names[contact.name] = true
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
-- name = "Covid info"
|
|
||||||
-- description = "Cases of illness and death from covid"
|
|
||||||
-- data_source = "covid19api.com"
|
|
||||||
-- type = "widget"
|
|
||||||
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
|
||||||
-- version = "1.0"
|
|
||||||
|
|
||||||
equals = "<font color=\""..ui:colors().secondary_text.."\"> = </font>"
|
|
||||||
|
|
||||||
function on_alarm()
|
|
||||||
http:get("https://api.covid19api.com/summary")
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_network_result(result)
|
|
||||||
local new = ajson:get_value(result, "object object:Global int:NewConfirmed")
|
|
||||||
local total = ajson:get_value(result, "object object:Global int:TotalConfirmed")
|
|
||||||
local newDeaths = ajson:get_value(result, "object object:Global int:NewDeaths")
|
|
||||||
local totalDeaths = ajson:get_value(result, "object object:Global int:TotalDeaths")
|
|
||||||
|
|
||||||
ui:show_lines({
|
|
||||||
"<b>Disease</b> | total"..equals..comma_value(total).." | new"..equals..comma_value(new),
|
|
||||||
"<b>Deaths</b> | total"..equals..comma_value(totalDeaths).." | new"..equals..comma_value(newDeaths)
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
function comma_value(n) -- credit http://richard.warburton.it
|
|
||||||
local left,num,right = string.match(n,'^([^%d]*%d)(%d*)(.-)$')
|
|
||||||
return left..(num:reverse():gsub('(%d%d%d)','%1,'):reverse())..right
|
|
||||||
end
|
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
-- name = "Random facts"
|
-- name = "Random facts"
|
||||||
-- description = "Radom useless facts"
|
-- description = "Random useless facts"
|
||||||
-- data_source = "https://uselessfacts.jsph.pl/"
|
-- data_source = "https://uselessfacts.jsph.pl/"
|
||||||
-- type = "widget"
|
-- type = "widget"
|
||||||
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
||||||
@@ -10,14 +10,15 @@ function on_alarm()
|
|||||||
http:get("https://uselessfacts.jsph.pl/random.json?language=en")
|
http:get("https://uselessfacts.jsph.pl/random.json?language=en")
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_network_result(result)
|
function on_network_result(result, code)
|
||||||
text = ajson:get_value(result, "object string:text")
|
if code >= 200 and code < 299 then
|
||||||
|
text = ajson:read(result, "object string:text")
|
||||||
ui:show_lines{ text }
|
ui:show_lines{ text }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_click()
|
function on_click()
|
||||||
if text ~= nil then
|
if text ~= nil then
|
||||||
system:copy_to_clipboard(text)
|
system:to_clipboard(text)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,24 +1,27 @@
|
|||||||
-- name = "Inspiration quotes"
|
-- name = "Inspiration quotes"
|
||||||
-- description = "inspiration.goprogram.ai"
|
-- description = "ZenQuotes.io"
|
||||||
-- data_source = "inspiration.goprogram.ai"
|
-- data_source = "https://zenquotes.io/"
|
||||||
-- type = "widget"
|
-- type = "widget"
|
||||||
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
||||||
-- version = "1.0"
|
-- version = "2.0"
|
||||||
-- foldable = "false"
|
-- foldable = "false"
|
||||||
|
|
||||||
|
local json = require "json"
|
||||||
|
|
||||||
function on_alarm()
|
function on_alarm()
|
||||||
http:get("https://inspiration.goprogram.ai/")
|
http:get("https://zenquotes.io/api/random")
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_network_result(result)
|
function on_network_result(result, code)
|
||||||
quote = ajson:get_value(result, "object string:quote")
|
if code >= 200 and code < 299 then
|
||||||
author = ajson:get_value(result, "object string:author")
|
res = json.decode(result)
|
||||||
|
ui:show_lines({ res[1].q }, { res[1].a })
|
||||||
ui:show_lines({ quote }, { author })
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_click()
|
function on_click()
|
||||||
if quote ~= nil then
|
if res ~= nil then
|
||||||
system:copy_to_clipboard(quote)
|
system:to_clipboard(res[1].q.." - "..res[1].a)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ function on_alarm()
|
|||||||
http:get("https://api.ipify.org")
|
http:get("https://api.ipify.org")
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_network_result(result)
|
function on_network_result(result, code)
|
||||||
ui:show_text(result)
|
if code >= 200 and code < 299 then
|
||||||
|
ui:show_text(result)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
69
main/sunrise-sunset-widget.lua
Normal file
69
main/sunrise-sunset-widget.lua
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
-- name = "Sunrise/Sunset"
|
||||||
|
-- description = "Shows Sunrise Sunset at your location"
|
||||||
|
-- data_source = "https://api.sunrise-sunset.org/"
|
||||||
|
-- type = "widget"
|
||||||
|
-- author = "Sriram S V"
|
||||||
|
-- version = "1.1"
|
||||||
|
-- foldable = "false"
|
||||||
|
|
||||||
|
local json = require "json"
|
||||||
|
local fmt = require "fmt"
|
||||||
|
|
||||||
|
function on_alarm()
|
||||||
|
local location = system:location()
|
||||||
|
|
||||||
|
if location == nil then
|
||||||
|
return
|
||||||
|
elseif location == "permission_error" then
|
||||||
|
ui:show_text("No location permission")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local url = "https://api.sunrise-sunset.org/json?lat=" .. location[1] .. "&lng=" .. location[2] .. "&formatted=0"
|
||||||
|
|
||||||
|
http:get(url)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_network_result(result)
|
||||||
|
local t = json.decode(result)
|
||||||
|
|
||||||
|
local sunrise_time = utc_to_local(parse_iso8601_datetime(t.results.sunrise))
|
||||||
|
local sunset_time = utc_to_local(parse_iso8601_datetime(t.results.sunset))
|
||||||
|
|
||||||
|
ui:show_text(
|
||||||
|
aio:res_string("today", "Today")..":"..
|
||||||
|
fmt.space(4).."⬆"..fmt.space(2)..sunrise_time..
|
||||||
|
fmt.space(4).."⬇"..fmt.space(2)..sunset_time
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
function parse_iso8601_datetime(json_date)
|
||||||
|
local pattern = "(%d+)%-(%d+)%-(%d+)%a(%d+)%:(%d+)%:([%d%.]+)([Z%+%-]?)(%d?%d?)%:?(%d?%d?)"
|
||||||
|
local year, month, day, hour, minute,
|
||||||
|
seconds, offsetsign, offsethour, offsetmin = json_date:match(pattern)
|
||||||
|
local timestamp = os.time{year = year, month = month,
|
||||||
|
day = day, hour = hour, min = minute, sec = seconds}
|
||||||
|
local offset = 0
|
||||||
|
if offsetsign ~= '' and offsetsign ~= 'Z' then
|
||||||
|
offset = tonumber(offsethour) * 60 + tonumber(offsetmin)
|
||||||
|
if xoffset == "-" then offset = offset * -1 end
|
||||||
|
end
|
||||||
|
|
||||||
|
return timestamp + offset * 60
|
||||||
|
end
|
||||||
|
|
||||||
|
function utc_to_local(utctime)
|
||||||
|
local local_time_str = os.date("%H:%M", utctime)
|
||||||
|
local utc_time_str = os.date("!%H:%M", utctime)
|
||||||
|
|
||||||
|
local function time_to_seconds(timestr)
|
||||||
|
local hour, minute = timestr:match("(%d+):(%d+)")
|
||||||
|
return tonumber(hour) * 3600 + tonumber(minute) * 60
|
||||||
|
end
|
||||||
|
|
||||||
|
local local_seconds = time_to_seconds(local_time_str)
|
||||||
|
local utc_seconds = time_to_seconds(utc_time_str)
|
||||||
|
local delta = local_seconds - utc_seconds
|
||||||
|
|
||||||
|
return os.date("%H:%M", utctime + delta)
|
||||||
|
end
|
||||||
@@ -4,17 +4,11 @@
|
|||||||
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
|
||||||
-- version = "1.0"
|
-- version = "1.0"
|
||||||
|
|
||||||
ticks = -1
|
function on_tick(ticks)
|
||||||
|
|
||||||
function on_tick()
|
|
||||||
-- Update one time per 10 seconds
|
|
||||||
ticks = ticks + 1
|
|
||||||
if ticks % 10 ~= 0 then
|
if ticks % 10 ~= 0 then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
ticks = 0
|
|
||||||
|
|
||||||
local info = system:system_info()
|
local info = system:system_info()
|
||||||
local strings = stringify_table(info)
|
local strings = stringify_table(info)
|
||||||
|
|
||||||
|
|||||||
130
main/tasks-menu.lua
Normal file
130
main/tasks-menu.lua
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
-- name = "Notes & tasks menu"
|
||||||
|
-- name_id = "notes"
|
||||||
|
-- description = "Shows tasks in the side menu"
|
||||||
|
-- type = "drawer"
|
||||||
|
-- aio_version = "4.7.99"
|
||||||
|
-- author = "Evgeny Zobnin"
|
||||||
|
-- version = "1.0"
|
||||||
|
|
||||||
|
local fmt = require "fmt"
|
||||||
|
local prefs = require "prefs"
|
||||||
|
|
||||||
|
local primary_color = aio:colors().primary_color
|
||||||
|
local secondary_color = aio:colors().secondary_color
|
||||||
|
|
||||||
|
local bottom_buttons = {
|
||||||
|
"fa:note_sticky", -- notes tab
|
||||||
|
"fa:list-check", -- tasks tab
|
||||||
|
"fa:pipe", -- separator
|
||||||
|
"fa:note_medical", -- new note button
|
||||||
|
"fa:square_plus" -- new task button
|
||||||
|
}
|
||||||
|
|
||||||
|
local notes_list = {}
|
||||||
|
local tasks_list = {}
|
||||||
|
|
||||||
|
if prefs.curr_tab == nil then
|
||||||
|
prefs.curr_tab = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_drawer_open()
|
||||||
|
drawer:add_buttons(bottom_buttons, prefs.curr_tab)
|
||||||
|
|
||||||
|
if prefs.curr_tab == 1 then
|
||||||
|
notes:load()
|
||||||
|
else
|
||||||
|
tasks:load()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_tasks_loaded(new_tasks)
|
||||||
|
tasks_list = new_tasks
|
||||||
|
local texts = map(tasks_list, task_to_text)
|
||||||
|
drawer:show_ext_list(texts)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_notes_loaded(new_notes)
|
||||||
|
notes_list = new_notes
|
||||||
|
local texts = map(notes_list, note_to_text)
|
||||||
|
drawer:show_ext_list(texts)
|
||||||
|
end
|
||||||
|
|
||||||
|
function note_to_text(it)
|
||||||
|
if it.text == "test note" then
|
||||||
|
test_note = it
|
||||||
|
end
|
||||||
|
|
||||||
|
if it.color ~= 6 then
|
||||||
|
return fmt.colored(it.text, notes:colors()[it.color])
|
||||||
|
else
|
||||||
|
return it.text
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function task_to_text(it)
|
||||||
|
local text = ""
|
||||||
|
local date_str = os.date("%b, %d, %H:%M", it.due_date)
|
||||||
|
|
||||||
|
if it.completed_date > 0 then
|
||||||
|
text = fmt.strike(it.text)
|
||||||
|
elseif it.due_date < os.time() then
|
||||||
|
text = fmt.bold(fmt.red(it.text))
|
||||||
|
elseif it.is_today then
|
||||||
|
text = fmt.bold(it.text)
|
||||||
|
else
|
||||||
|
text = it.text
|
||||||
|
end
|
||||||
|
|
||||||
|
return text.."<br/>"..fmt.space(4)..fmt.small(date_str)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
if prefs.curr_tab == 1 then
|
||||||
|
on_note_click(idx)
|
||||||
|
else
|
||||||
|
on_task_click(idx)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_note_click(idx)
|
||||||
|
notes:show_editor(notes_list[idx].id)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_task_click(idx)
|
||||||
|
tasks:show_editor(tasks_list[idx].id)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_long_click(idx)
|
||||||
|
if prefs.curr_tab == 1 then
|
||||||
|
on_note_long_click(idx)
|
||||||
|
else
|
||||||
|
on_task_long_click(idx)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_note_long_click(idx)
|
||||||
|
system:to_clipboard(notes_list[idx].text)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_task_long_click(idx)
|
||||||
|
system:to_clipboard(tasks_list[idx].text)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_button_click(idx)
|
||||||
|
if idx < 3 then
|
||||||
|
prefs.curr_tab = idx
|
||||||
|
on_drawer_open()
|
||||||
|
elseif idx == 4 then
|
||||||
|
notes:show_editor()
|
||||||
|
elseif idx == 5 then
|
||||||
|
tasks:show_editor()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function map(tbl, f)
|
||||||
|
local ret = {}
|
||||||
|
for k,v in pairs(tbl) do
|
||||||
|
ret[k] = f(v)
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
@@ -18,7 +18,7 @@ function on_alarm()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function redraw()
|
function redraw()
|
||||||
local color = ui:colors()
|
local color = aio:colors()
|
||||||
if unit == "temperature" then
|
if unit == "temperature" then
|
||||||
sum = round(f[units[unit][unit_from]..units[unit][unit_to]](amount),3)
|
sum = round(f[units[unit][unit_from]..units[unit][unit_to]](amount),3)
|
||||||
else
|
else
|
||||||
@@ -37,10 +37,10 @@ end
|
|||||||
function on_click(idx)
|
function on_click(idx)
|
||||||
if idx == 1 then
|
if idx == 1 then
|
||||||
dialog_id ="amount"
|
dialog_id ="amount"
|
||||||
ui:show_edit_dialog("Enter amount", "", amount)
|
dialogs:show_edit_dialog("Enter amount", "", amount)
|
||||||
elseif idx == 2 then
|
elseif idx == 2 then
|
||||||
dialog_id = "unit_from"
|
dialog_id = "unit_from"
|
||||||
ui:show_radio_dialog("Select unit",get_radio_tab(unit),get_radio_idx(unit_from))
|
dialogs:show_radio_dialog("Select unit",get_radio_tab(unit),get_radio_idx(unit_from))
|
||||||
elseif idx == 3 then
|
elseif idx == 3 then
|
||||||
local unit_tmp = unit_from
|
local unit_tmp = unit_from
|
||||||
unit_from = unit_to
|
unit_from = unit_to
|
||||||
@@ -50,10 +50,10 @@ function on_click(idx)
|
|||||||
system:copy_to_clipboard(sum)
|
system:copy_to_clipboard(sum)
|
||||||
elseif idx == 5 then
|
elseif idx == 5 then
|
||||||
dialog_id = "unit_to"
|
dialog_id = "unit_to"
|
||||||
ui:show_radio_dialog("Select unit",get_radio_tab(unit),get_radio_idx(unit_to))
|
dialogs:show_radio_dialog("Select unit",get_radio_tab(unit),get_radio_idx(unit_to))
|
||||||
elseif idx == 6 then
|
elseif idx == 6 then
|
||||||
dialog_id = "unit"
|
dialog_id = "unit"
|
||||||
ui:show_radio_dialog("Select converter",get_radio_tab(""),get_radio_idx(unit))
|
dialogs:show_radio_dialog("Select converter",get_radio_tab(""),get_radio_idx(unit))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -1,130 +0,0 @@
|
|||||||
-- name = "Widgets switcher"
|
|
||||||
-- description = "Turns screen widgets on and off when buttons are pressed"
|
|
||||||
-- type = "widget"
|
|
||||||
-- arguments_help = "Don't change the arguments directly, use the long click menu."
|
|
||||||
-- author = "Andrey Gavrilov"
|
|
||||||
-- version = "2.0"
|
|
||||||
|
|
||||||
--constants--
|
|
||||||
|
|
||||||
local widgets = {"weather","weatheronly","clock","alarm","worldclock","monitor","traffic","player","apps","appbox","applist","contacts","notify","dialogs","dialer","timer","stopwatch","mail","notes","tasks","feed","telegram","twitter","calendar","exchange","finance","bitcoin","control","recorder","calculator","empty","bluetooth","map","remote"}
|
|
||||||
|
|
||||||
local icons = {"fa:user-clock","fa:sun-cloud","fa:clock","fa:alarm-clock","fa:business-time","fa:network-wired","fa:exchange","fa:play-circle","fa:robot","fa:th","fa:list","fa:address-card","fa:bell","fa:comment-alt-minus","fa:phone-alt","fa:chess-clock","fa:stopwatch","fa:at","fa:sticky-note","fa:calendar-check","fa:rss-square","fa:paper-plane","fa:dove","fa:calendar-alt","fa:euro-sign","fa:chart-line","fa:coins","fa:wifi","fa:microphone-alt","fa:calculator-alt","fa:eraser","fa:head-side-headphones","fa:map-marked-alt","fa:user-tag"}
|
|
||||||
|
|
||||||
local names = {"Clock & weather","Weather","Clock","Alarm","Worldclock","Monitor","Traffic","Player","Frequent apps","My apps","App list","Contacts","Notify","Dialogs","Dialer","Timer","Stopwatch","Mail","Notes","Tasks","Feed","Telegram","Twitter","Calendar","Exchange","Finance","Bitcoin","Control panel","Recorder","Calculator","Empty widget","Bluetooth","Map","User widget"}
|
|
||||||
|
|
||||||
--variables--
|
|
||||||
|
|
||||||
local pos = 0
|
|
||||||
|
|
||||||
function on_resume()
|
|
||||||
if next(settings:get()) == nil then
|
|
||||||
set_default_args()
|
|
||||||
end
|
|
||||||
ui:set_folding_flag(true)
|
|
||||||
local buttons,colors = get_buttons()
|
|
||||||
ui:show_buttons(buttons, colors)
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_click(idx)
|
|
||||||
pos = idx
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_long_click(idx)
|
|
||||||
pos = idx
|
|
||||||
local tab = settings:get()
|
|
||||||
ui:show_context_menu({{"angle-left",""},{"ban",""},{"angle-right",""},{icons[get_checkbox_idx()[idx]]:gsub("fa:",""),names[get_checkbox_idx()[idx]]}})
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_context_menu_click(menu_idx)
|
|
||||||
if menu_idx == 1 then
|
|
||||||
move(-1)
|
|
||||||
elseif menu_idx == 2 then
|
|
||||||
remove()
|
|
||||||
elseif menu_idx == 3 then
|
|
||||||
move(1)
|
|
||||||
elseif menu_idx == 4 then
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_dialog_action(data)
|
|
||||||
if data == -1 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
settings:set(data)
|
|
||||||
on_resume()
|
|
||||||
end
|
|
||||||
|
|
||||||
function on_settings()
|
|
||||||
ui:show_checkbox_dialog("Select widgets", names, get_checkbox_idx())
|
|
||||||
end
|
|
||||||
|
|
||||||
--utilities--
|
|
||||||
|
|
||||||
function redraw()
|
|
||||||
local buttons,colors = get_buttons()
|
|
||||||
local checkbox_idx = get_checkbox_idx()
|
|
||||||
local widget = widgets[checkbox_idx[pos]]
|
|
||||||
if aio:is_widget_added(widget) then
|
|
||||||
aio:remove_widget(widget)
|
|
||||||
colors[pos] = "#909090"
|
|
||||||
else
|
|
||||||
aio:add_widget(widget)
|
|
||||||
colors[pos] = "#1976d2"
|
|
||||||
end
|
|
||||||
ui:show_buttons(buttons, colors)
|
|
||||||
end
|
|
||||||
|
|
||||||
function set_default_args()
|
|
||||||
local args = {}
|
|
||||||
for i = 1, #widgets do
|
|
||||||
table.insert(args, i)
|
|
||||||
end
|
|
||||||
settings:set(args)
|
|
||||||
end
|
|
||||||
|
|
||||||
function get_checkbox_idx()
|
|
||||||
local tab = settings:get()
|
|
||||||
for i = 1, #tab do
|
|
||||||
tab[i] = tonumber(tab[i])
|
|
||||||
end
|
|
||||||
return tab
|
|
||||||
end
|
|
||||||
|
|
||||||
function get_buttons()
|
|
||||||
local buttons,colors = {},{}
|
|
||||||
local checkbox_idx = get_checkbox_idx()
|
|
||||||
for i = 1, #checkbox_idx do
|
|
||||||
table.insert(buttons, icons[checkbox_idx[i]])
|
|
||||||
if aio:is_widget_added(widgets[checkbox_idx[i]]) then
|
|
||||||
table.insert(colors, "#1976d2")
|
|
||||||
else
|
|
||||||
table.insert(colors, "#909090")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return buttons,colors
|
|
||||||
end
|
|
||||||
|
|
||||||
function move(x)
|
|
||||||
local tab = settings:get()
|
|
||||||
if (pos == 1 and x < 0) or (pos == #tab and x > 0) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local cur = tab[pos]
|
|
||||||
local prev = tab[pos+x]
|
|
||||||
tab[pos+x] = cur
|
|
||||||
tab[pos] = prev
|
|
||||||
settings:set(tab)
|
|
||||||
local buttons,colors = get_buttons()
|
|
||||||
ui:show_buttons(buttons, colors)
|
|
||||||
end
|
|
||||||
|
|
||||||
function remove()
|
|
||||||
local tab = settings:get()
|
|
||||||
table.remove(tab,pos)
|
|
||||||
settings:set(tab)
|
|
||||||
local buttons,colors = get_buttons()
|
|
||||||
ui:show_buttons(buttons, colors)
|
|
||||||
end
|
|
||||||
@@ -18,18 +18,20 @@ function on_alarm()
|
|||||||
http:get(random_url)
|
http:get(random_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_network_result(result)
|
function on_network_result(result, code)
|
||||||
local parsed = json.decode(result)
|
if code >= 200 and code < 299 then
|
||||||
title = parsed.query.random[1].title
|
local parsed = json.decode(result)
|
||||||
|
title = parsed.query.random[1].title
|
||||||
http:get(summary_url.."&titles="..url.quote(title), "summary")
|
http:get(summary_url.."&titles="..url.quote(title), "summary")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_network_result_summary(result)
|
function on_network_result_summary(result, code)
|
||||||
local parsed = json.decode(result)
|
if code >= 200 and code < 299 then
|
||||||
local extract = get_extract(parsed)
|
local parsed = json.decode(result)
|
||||||
|
local extract = get_extract(parsed)
|
||||||
ui:show_lines({ smart_sub(extract, 200) }, { title })
|
ui:show_lines({ smart_sub(extract, 200) }, { title })
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_click()
|
function on_click()
|
||||||
|
|||||||
5
rm-scripts.sh
Executable file
5
rm-scripts.sh
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
source ./env
|
||||||
|
adb shell rm -rf $SCRIPTS_DIR/*.lua
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ function on_long_click()
|
|||||||
if history == "" then
|
if history == "" then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
ui:show_dialog("История курсов ЦБ\n"..nominal.." "..cur.." / "..base_cur,history)
|
dialogs:show_dialog("История курсов ЦБ\n"..nominal.." "..cur.." / "..base_cur,history)
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_settings()
|
function on_settings()
|
||||||
@@ -38,7 +38,7 @@ function on_settings()
|
|||||||
idx = i
|
idx = i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
ui:show_radio_dialog("Выберите валюту",names,idx)
|
dialogs:show_radio_dialog("Выберите валюту",names,idx)
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_dialog_action(data)
|
function on_dialog_action(data)
|
||||||
@@ -64,12 +64,12 @@ function on_network_result_today(result,error)
|
|||||||
end
|
end
|
||||||
today = today..v.Nominal:value().." "..v.CharCode:value().." = "..v.Value:value():replace(",",".").." "..base_cur
|
today = today..v.Nominal:value().." "..v.CharCode:value().." = "..v.Value:value():replace(",",".").." "..base_cur
|
||||||
end
|
end
|
||||||
ui:show_dialog("Курсы валют ЦБ\n"..date_today,today)
|
dialogs:show_dialog("Курсы валют ЦБ\n"..date_today,today)
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_network_result_history(result,error)
|
function on_network_result_history(result,error)
|
||||||
history = ""
|
history = ""
|
||||||
local color = ui:get_colors()
|
local color = aio:colors()
|
||||||
local equals = "<font color=\""..color.secondary_text.."\"> = </font>"
|
local equals = "<font color=\""..color.secondary_text.."\"> = </font>"
|
||||||
local xml = require "xml"
|
local xml = require "xml"
|
||||||
local t = xml:parse(result)
|
local t = xml:parse(result)
|
||||||
@@ -111,7 +111,7 @@ function get_rates()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function get_formatted_change_text(change)
|
function get_formatted_change_text(change)
|
||||||
local color = ui:get_colors()
|
local color = aio:colors()
|
||||||
if change > 0 then
|
if change > 0 then
|
||||||
return "<font color=\""..color.progress_good.."\"> +"..change.."%</font>"
|
return "<font color=\""..color.progress_good.."\"> +"..change.."%</font>"
|
||||||
elseif change < 0 then
|
elseif change < 0 then
|
||||||
|
|||||||
@@ -11,14 +11,14 @@ function on_alarm()
|
|||||||
end
|
end
|
||||||
|
|
||||||
function on_network_result(result)
|
function on_network_result(result)
|
||||||
quote = ajson:get_value(result, "object string:quoteText")
|
quote = ajson:read(result, "object string:quoteText")
|
||||||
author = ajson:get_value(result, "object string:quoteAuthor")
|
author = ajson:read(result, "object string:quoteAuthor")
|
||||||
|
|
||||||
ui:show_lines({ quote }, { author })
|
ui:show_lines({ quote }, { author })
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_click()
|
function on_click()
|
||||||
if quote ~= nil then
|
if quote ~= nil then
|
||||||
system:copy_to_clipboard(quote)
|
system:to_clipboard(quote)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
17
samples/add_todo_sample.lua
Normal file
17
samples/add_todo_sample.lua
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
function on_resume()
|
||||||
|
ui:show_lines{
|
||||||
|
"Add todo #1",
|
||||||
|
"Add todo #2",
|
||||||
|
"Add todo #3",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
if idx == 1 then
|
||||||
|
aio:add_todo("face-smile", "Todo #1")
|
||||||
|
elseif idx == 2 then
|
||||||
|
aio:add_todo("face-smile-tongue", "Todo #2")
|
||||||
|
else
|
||||||
|
aio:add_todo("face-smile-tear", "Todo #3")
|
||||||
|
end
|
||||||
|
end
|
||||||
7
samples/ai-sample.lua
Normal file
7
samples/ai-sample.lua
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
function on_alarm()
|
||||||
|
ai:complete("Who are you?")
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_ai_answer(answer)
|
||||||
|
ui:show_text(answer)
|
||||||
|
end
|
||||||
69
samples/android-widget-sample.lua
Normal file
69
samples/android-widget-sample.lua
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
-- name = "Android widgets sample (Gmail)"
|
||||||
|
|
||||||
|
local prefs = require "prefs"
|
||||||
|
|
||||||
|
local max_mails = 1
|
||||||
|
local curr_tab = {}
|
||||||
|
local w_bridge = nil
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
if not widgets:bound(prefs.wid) then
|
||||||
|
setup_app_widget()
|
||||||
|
end
|
||||||
|
|
||||||
|
widgets:request_updates(prefs.wid)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_app_widget_updated(bridge)
|
||||||
|
local tab = bridge:dump_strings().values
|
||||||
|
|
||||||
|
-- Removing redunant elements like widget title
|
||||||
|
tab = skip(tab, 4)
|
||||||
|
|
||||||
|
-- Each mail consists of 4 text elements:
|
||||||
|
-- 1. from, 2. time, 3. subject, 4. text
|
||||||
|
tab = take(tab, max_mails * 4)
|
||||||
|
|
||||||
|
-- Concatenate "from" and "time"
|
||||||
|
tab = concat(tab, {1, 2}, ", ")
|
||||||
|
|
||||||
|
curr_tab = tab
|
||||||
|
w_bridge = bridge
|
||||||
|
|
||||||
|
ui:show_lines(tab)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
w_bridge:click(curr_tab[idx])
|
||||||
|
end
|
||||||
|
|
||||||
|
function setup_app_widget()
|
||||||
|
local id = widgets:setup("com.google.android.gm/com.google.android.gm.widget.GmailWidgetProvider")
|
||||||
|
if (id ~= nil) then
|
||||||
|
prefs.wid = id
|
||||||
|
else
|
||||||
|
ui:show_text("Can't add widget")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Utils
|
||||||
|
|
||||||
|
function concat(tbl, indicesToConcatenate, delimiter)
|
||||||
|
local resultTable = {}
|
||||||
|
|
||||||
|
if #indicesToConcatenate > 0 then
|
||||||
|
local concatenatedString = {}
|
||||||
|
for _, index in ipairs(indicesToConcatenate) do
|
||||||
|
table.insert(concatenatedString, tbl[index])
|
||||||
|
end
|
||||||
|
table.insert(resultTable, table.concat(concatenatedString, delimiter))
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, #tbl do
|
||||||
|
if not contains(indicesToConcatenate, i) then
|
||||||
|
table.insert(resultTable, tbl[i])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return resultTable
|
||||||
|
end
|
||||||
56
samples/android-widget-sample2.lua
Normal file
56
samples/android-widget-sample2.lua
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
-- name = "Android widgets sample (The Weather Channel)"
|
||||||
|
-- uses_app = "com.weather.Weather"
|
||||||
|
|
||||||
|
local prefs = require "prefs"
|
||||||
|
|
||||||
|
local curr_temp = ""
|
||||||
|
local w_bridge = nil
|
||||||
|
|
||||||
|
function on_resume()
|
||||||
|
if not widgets:bound(prefs.wid) then
|
||||||
|
setup_app_widget()
|
||||||
|
end
|
||||||
|
|
||||||
|
widgets:request_updates(prefs.wid)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_app_widget_updated(bridge)
|
||||||
|
local tab = bridge:dump_table()
|
||||||
|
|
||||||
|
current_temp = tab.relative_layout_1.relative_layout_2.text_1
|
||||||
|
location = tab.relative_layout_1.relative_layout_2.text_2
|
||||||
|
w_bridge = bridge
|
||||||
|
|
||||||
|
if location ~= nil and current_temp ~= nil then
|
||||||
|
ui:show_text(location..": "..current_temp)
|
||||||
|
else
|
||||||
|
ui:show_text("Empty")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_app_widget_updated_alt(bridge)
|
||||||
|
local strings = bridge:dump_strings().values
|
||||||
|
|
||||||
|
location = strings[2]
|
||||||
|
current_temp = strings[1]
|
||||||
|
w_bridge = bridge
|
||||||
|
|
||||||
|
if location ~= nil and current_temp ~= nil then
|
||||||
|
ui:show_text(location..": "..current_temp)
|
||||||
|
else
|
||||||
|
ui:show_text("Empty")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
w_bridge:click(current_temp)
|
||||||
|
end
|
||||||
|
|
||||||
|
function setup_app_widget()
|
||||||
|
local id = widgets:setup("com.weather.Weather/com.weather.Weather.widgets.WeatherWidgetProvider2x2")
|
||||||
|
if (id ~= nil) then
|
||||||
|
prefs.wid = id
|
||||||
|
else
|
||||||
|
ui:show_text("Can't add widget")
|
||||||
|
end
|
||||||
|
end
|
||||||
23
samples/apps-menu.lua
Normal file
23
samples/apps-menu.lua
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
-- name = "Apps menu"
|
||||||
|
-- type = "drawer"
|
||||||
|
-- testing = "true"
|
||||||
|
|
||||||
|
function on_drawer_open()
|
||||||
|
apps_tab = apps:apps()
|
||||||
|
|
||||||
|
-- Do not update if the list of the apps is not changed
|
||||||
|
if #apps_tab ~= #drawer:items() then
|
||||||
|
update()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function update()
|
||||||
|
names_tab = map(function(it) return it.name end, apps_tab)
|
||||||
|
icons_tab = map(function(it) return it.icon end, apps_tab)
|
||||||
|
|
||||||
|
drawer:show_list(names_tab, icons_tab, nil, true)
|
||||||
|
end
|
||||||
|
|
||||||
|
function on_click(idx)
|
||||||
|
apps:launch(apps_tab[idx])
|
||||||
|
end
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user